解决项目的问题,意识到断言的重要性。如果一个程序在某处遇到了非法的值,那么最好的情况便是在此刻停下报错,最坏的情况便是程序不吭不响的执行着~~直到你发现他执行的方式极为诡异,此时,你要花九牛二虎之力才能找到错误所在之处~~~~
学习一下断言吧:
·······什么是断言
在某处判断某一个表达式的值为真或者假,如果假则输出错误消息并停止程序的执行~
assert是宏,而不是函数,只在debug版本中有效,因此无需在release版本删除。
·······哪几种断言
MFC
ASSERT
void foo(char* p,int size)
{
ASSERT(p != 0); // 验证缓冲区指针
ASSERT((size >= 100); // 确认缓冲区大小至少为100字节
// foo 函数的其它计算过程
}
如果没有定义_DEBUG预处理符,则该语句不会真正生成代码。Visual C++会在调试模式编译时自动定义_DEBUG,而在发行模式下,该预处理符是不存在的。如果定义了_DEBUG,则上述两个断言生成的代码类如:
//ASSERT(p != 0);
do
{
if(!(p != 0) && AfxAssertFailedLine(__FILE__, __LINE__))
AfxDebugBreak();
} while(0);
//ASSERT((size >= 100);
do
{
if(!(size >= 100) && AfxAssertFailedLine(__FILE__,__LINE__))
AfxDebugBreak();
}while(0);
ASSERT_KINDOF(classname,pObject); ASSERT_KINDOF(CDocument,pDocument);
检验pObject指向的对象是classname类的一个对象或者其派生类的对象
ASSERT_VALID(pObject); pObject 必须是一个派生于CObject类的类对象,会调用其重写的AssertValid函数 ,例如
如果使用应用向导或类向导生成基于MFC的类,通常会得到AssertValid()的骨架,最好改写这些骨架代码以增加最基本的完整性检查。下面是一个典型的例子,类Sample从CObject继承,假定它含有职员名字及其薪水:
class Sample : public CObject
{
protected:
CString m_Name; // 职员名字
double m_Salary; // 薪水
public:
Sample(LPCTSTR name,double salary) : m_Name(name), m_Salary(salary) {}
#ifdef _DEBUG
virtual void AssertValid() const;
#endif
};
#ifdef _DEBUG
void Sample::AssertValid() const
{
CObject::AssertValid(); // 验证基类
ASSERT(!m_Name.IsEmpty()); // 验证职员名字
ASSERT(m_Salary > 0); // 验证薪水
}
#endif
CRT assertion
_ASSERT 和 _ASSERTE 后一个会在出错时同时打印出条件判断句
ANSI
assert()
注意:assert用于检测非法的输入,但是合法的输入并不一定是正确的,例如:
int pB = (int*)malloc(sizeof(int)*1000);
assert(pB!=NULL) //错误的使用assert 他会在release版本失效~也就是说assert不应该对程序产生副作用
正确的做法:
int pB = (int*) malloc(sizeof(int)*1000);
if(pB == NULL)
{
//错误处理
}
else{
}
另一个例子:
void draw(){
CFigure* pF = getCF();
assert(pf!=NULL);
if(pf == NULL){}
else{
}
}
此处,对于getCF来说返回值为NULL是非法的,如果他的返回值可能为null就没必要加上assert语句。
而下面的if语句则是为了防止release版本出现null指针的情况。
VERIFY()
由于ASSERT仅在程序的调试版起作用,测试表达式总是被动的。也就是说,它们不能包含赋值、增量、减量等真正改变数据的操作。但有时候我们需要验证一个主动表达式,比如赋值语句。这时可以使用VERIFY代替ASSERT。下面是一个例子:
void foo(char* p,int size)
{
char* q; // 指针的副本
VERIFY(q = p); // 拷贝指针并执行验证
ASSERT((size >= 100); // 确保缓冲区大小至少为100字节
// 执行 foo 的其它操作
}
在调试模式下ASSERT和VERIFY是相同的。但在release模式下,VERIFY能够继续对表达式求值(但不再进行断言检验),而ASSERT语句在效果上就如同已经删除了一样。
尽管在MFC源代码中可以找到一些应用VERIFY的例子,但ASSERT用得更为普遍。一些程序员总是完全避免使用VERIFY,因为他们已经习惯于使用被动断言。请记住,如果在ASSERT语句中使用了主动表达式,编译器不会发出任何警告。在发行模式下编译时该表达式会被直接删除,从而导致程序运行的错误。由于发行版程序不含调试信息,这种类型的错误是很难找到原因的。