MFC中高级调试技术
1.TRACE宏的利用
TRACE宏有点像我们以前在C语言中用的printf函数,使程序在运行过程中输出一些调试信息,使我们能了解程序的一些状态。但有一点不同的是,TRACE宏只有在调试状态下才
有输出,而以前用的Printh函数在任何情况下都有输出。同printf函数一样,TRACE宏可以接受多个参数,如:
int x = 1;
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement/n" );
TRACE( "The value of x is %d/n", x );
TRACE( "x = %d and y = %d/n", x, y );
TRACE( "x = %d and y = %d and z = %f ", x, y, z );
要注意的是TRACE宏只对Debug版本的工程产生作用,在Release版本的工程中,TRACE宏将被忽略。
2. ASSERT宏的利用
在开发过程中我们可以假设只要程序运行正确,某一条件肯定成立。若不成立,那么我们可以断言程序肯定出错。在这种情况下我们可要利用ASSERT来设定断言。ASSERT宏的
参数是一个逻辑表达式,在程序运行过程中,若该逻辑表达式为真,则不会发生任何动作;若此表达式为假,则系统弹出一个对话框警告你,并停止程序的执行。同时要求你作出
选择:取消、忽略和重试。若你选择取消,则系统将停止程序的运行;若你选择忽略,则系统将忽略该错误,并继续执行程序;若你选择重试,则系统将重新计算该表达式,并激
活调试器。同TRACE宏一样,ASSERT宏只对Debug版本的工程产生作用,在Release版本的工程中,TRACE宏将被忽略。
下面的示例显示如何使用ASSERT检查函数的返回值:
int x = SomeFunc(y);
ASSERT( x >= 0); // 如果x为负,则断言失败。
可将断言用于:
(1)可以使用断言语句捕捉逻辑错误。可以在程序逻辑必须为真的条件上设置断言。除非发生逻辑错误,否则断言对程序无任何影响。
(2)可以使用断言语句检查操作的结果。断言对于快速直观地检查不明显的操作结果最有价值。
(3)可以使用断言在代码中已处理了错误的点处测试错误类型。
3. ASSERT_VALID宏的使用
ASSERT_VALID宏用来在程序运行时检查一个对象的内部合法性。比如说,现在有一个学生对象,我们知道每一个学生的年龄一个大于零,若年龄小于零,则该学生对象肯定有问
题。事实上,ASSERT_VALID宏就是转化为对象的成员函数AssertValid的调用,只是这中方法更安全。它的参数是一个对象指针,通过这个指针来调用它的AssertValid成员函数。
与此相配套,每当创建从CObject类继承而来的一个新类时,可以重载该成员函数,以执行特定的合法性检查。下面的示例显示如何声明AssertValid函数:
sclass CPerson : public CObject
{
protedted:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
virtual void AssertValid() const; // Override
#endif
// .....
};
当重写AssertValid时,在执行程序的检查之前请调用AssertValid的基类模板。然后使用ASSERT宏来检查派生类特有的成员,如下所示:
#ifdef _DEBUG
void CPerson::AssertValid() const
{
// call inherited AssertValid first
CObject::AssertValid();
// check CPerson menbers...
ASSERT(!m_strName.IsEmpty()); // Must have a name
ASSERT(m_salary > 0); // Must have an income
}
#endif
4. 内存漏洞的检查
在C++和C语言中指针问题也就是内存申请与释放是一个令人头疼的事情,假如申请了内存,但没有释放,并且程序需要长时间的运行,那么,系统资源将逐渐减少。当系统的
资源全部用完时,系统将崩溃。所以在开发过程中一定要保证资源的完全释放。
检查内存漏洞的方法如下:
假如要检查某一程序段是否有内存漏洞,只需在这一程序段的开始要求系统做一次内存使用情况的映射,记录下程序开始时的内存使用情况,然后在程序段的末尾再使系统做
一次内存映射。比较两次映射,以检查是否有未释放的内存,加入有未释放的内存,根据这一块中有关分配情况的信息来告诉用户在那里申请的内存未释放。
具体的讲,检查内存漏洞需要一下几步:
(1)在所检测的程序段开始处建立一个CmemoryState对象,调用其成员函数Checkpoint,以取得当前内存使用情况的映射;
(2)在所检测的程序段的末尾处再建立一个CmemoryState对象,调用其成员函数Checkpoint,以取得当前内存使用情况的映射;
(3)再建立第3个CmemoryState对象,调用其成员函数Difference,把第一个CmemoryState对象和第二个CmemoryState对象作为其参数,如果两次内存映射不相同,则该函数
返回非零,说明此程序段中有内存漏洞。
下面来看一个例子:
#ifdef _DEBUG
CMemoryState oldMemState, newMemState, diffMemState;
oldMemState.Checkpoint();
#endif
CString s = "This is a frame variable";
// the next object is a heap object
CPerson* p = new CPerson("Smith", "Alan", "581_0215");
#ifdef _DEBUG
newMemState.Checkpoint();
if (diffMemState.Difference(oldMemState, newMemState))
{
TRACE("Memory Leaked!/n");
}
在此例中,首先定义了3个CMemoryState对象。然后在需要检测内存漏洞的代码前后分别调用CMemoryState对象成员函数Checkpoint,再后调用第3个CMemoryState对象的成员
函数Difference来判断是否有内存漏洞,如果有,则使用TRACE宏打印提示消息。