变量/对象合法性检查
在VC中检查变量合法性一般利用ASSERT(x)宏,ASSERT的作用在于检查表达式是否为假或为NULL,如果为假则会引发异常。在MFC中ASSERT宏被大量使用,例如: BOOL CWnd::Attach(HWND hWndNew)
{
ASSERT(m_hWnd == NULL); // only attach once, detach on
destroy
// …
return TRUE;
}
void CString::AllocBuffer(int nLen)
{
ASSERT(nLen >= 0);
ASSERT(nLen <= INT_MAX-1); //
}
void CDocument::AddView(CView* pView)
{
// other code…
ASSERT(pView->m_pDocument == NULL);
ASSERT(m_viewList.Find(pView, NULL) == NULL);
}
当ASSERT失败并引发异常时会有对话框谈出并报告发生该ASSERT失败位置。报错信息如:assertion failed in file <THIS_FILE> in line <__LINE__>。

并允许你选择继续运行(Ignore)或是终止(Abort)程序。(当然选择继续运行是很危险的)选择Retry将会启动调试软件对程序进行调试。
此外我们时常可以看到下面的用法: ASSERT(pWnd);//检查指针是否已经赋值
if( condition )
{
ASSERT(FALSE);//强制抛出一个ASSERT异常
}
此外还有一点,ASSERT宏只在调试版本中才会有作用,在调试版本中ASSERT(f)宏被展开为 do
{
if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__))
AfxDebugBreak();
} while (0)
// while(0)用来保证 ASSERT宏后面可以不跟随“;”如 ASSERT(f)与ASSERT(f);都合法
// THIS_FILE表示当前当前文件文件名,__LINE__为当前代码所在的行数
而在发行版本中会被展开为:
((void)0)
所以对程序内部状态改变的代码不能够放置在ASSERT宏中否则在发行版中会出现不正常的现象,例如下面的代码: void yourClass::fun1()
{
ASSERT(++m_iTick > 5);
ASSERT(DoSomething() == TRUE);
}
void yourClass:DoSomething()
{
if(m_szOut == "No")
{
return FALSE;
}
else
{
m_szOut="Yes"; //状态改变
reutrn TRUE;
}
}
如果希望合法检查在发行版本中同样起作用则可以利用VERIFY宏,VERIFY宏与ASSERT宏的VERIFY的不同在与VERIFY在发行版本中同样会起作用,但是使用VERIFY会导致非常不友好的用户界面。
对象的合法性检查需要根据对象自身的状态和一些对象自己的逻辑来作出判断,因此在对象外部就无法正确判断,一个省时有效的办法是在对象内部进行检查,有对象自己负责合法性检查,例如下面的代码: void CObList::AssertValid() const
{
CObject::AssertValid();
if (m_nCount == 0)
{
// empty list
ASSERT(m_pNodeHead == NULL);
ASSERT(m_pNodeTail == NULL);
}
else
{
// non-empty list
ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode)));
ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode)));
}
}
MFC利用成员函数 void CObject::AssertValid() const来实现对象的合法性检查,所以新的类必须是CObject的派生类,(在MFC中几乎所有的类都由CObject派生)由于C++的多态性派生类的AssertValid函数会被正确的调用。函数定义中的const表示该函数体中不能改变成员变量的值。
我们所需要做的就是重载AssertValid,并实现对象状态合法性的检查。在AssertValid我们不但可以检查数据的正确性,也可以对数据的逻辑性进行检查。例如一个盒子中的白球不能多于黑球,而且总数不能多于100: class CBox : public CObject
{
...
void AssertValid() const;
int m_iWhiteBall,m_iBlackBall;
}
void CBox::AssertValid() const
{
CObject::AssertValid();//先调用父类的检查函数
ASSERT(m_iWhiteBall<=m_iBlackBall);
ASSERT(m_iWhiteBall+m_iBlackBall <=100);
}
到这里你会问什么时候调用AssertValid函数?在MFC中对象的合法性检查都依赖AssertValid,比如在销毁窗口对象时会首先检查该窗口对象是否合法,而你自己也可以手工调用AssertValid来检查对象的合法性,例如下面的代码: void CDocument::AssertValid() const
{
CObject::AssertValid();
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
}
}
而当你对自己的CView派生类CYourView重载AssertValid后,CYourView的AssertValid就会在文档类检查视类合法性时调用。此外MFC中定义了ASSERT_VALID宏来执行安全的对象检查,ASSERT_VALID宏会展开AfxAssertValidObject,并先检查指针的合法性。这样避免了下面的错误: CView *pV=NULL;
pV->AssertValid();
//安全的方法是利用
ASSERT_VALID(pView);
与ASSERT宏一样,ASSERT_VALID宏只在调试版本中起作用。
利用合法性检查可以帮助我们在由于变量非法而引发异常方便的定位错误,所以在开发程序时多利用合法性检查并在必要的地方使用检查宏会帮助我们更有效的进行调试。
|