VCMFC 调试技术 VC/MFC 调试技术(上) 在 VC 程序中使用调试语句 为了更好地对程序调试,可以使用如下方法:使用断言、使用跟踪语句、使用异常和返回值。 一、断言 1、基本概念 断言是一种让错误在运行时候自我暴露的简单有效实用的技术。它们帮助你较早较轻易地发现错误,使得整个调试过程效 率更高。 断言是布尔调试语句,用来检测在程序正常运行的时候某一个条件的值是否总为真,它能让错误在运行时刻暴露在程序员 面前。使用断言的最大好处在于,能在更解决错误的发源地的地方发现错误。断言具有以下特征: .断言是用来发现运行时刻错误的,发现的错误是关于程序实现方面的。 .断言中的布尔表达式显示的是某个对象或者状态的有效性而不是正确性。 .断言在条件编译后只存在于调试版本中,而不是发布版本里。 .断言不能包含程序代码。 .断言是为了给程序员而不是用户提供信息。 使用断言最根本的好处是自动发现许多运行时产生的错误,但断言不能发现所有错误。断言检查的是程序的有效性而不是 正确性,可通过断言把错误限制在一个有限的范围内。当断言为假,激活调试器显示出错代码时,可用 Call Stack 命令,通过 检查栈里的调用上下文、少量相关参数的值以及输出窗口中 Debug 表的内容,通常能检查出导致断言失败的原因。_ASSERTE 宏(属于 C 运行时间库)还能在断言失败时显示出失效断言。下面我们讨论一下 MFC 库中的断言。 2、MFC 库中的断言 (1) ASSERT(布尔表达式) 用 MFC 时最好选择 ASSERT 宏,它的优点是即使出现了 WM_QUIT 消息也能显示断言失效消息框。 (2) VERIFY(布尔表达式) VERIFY 宏中的布尔表达式在发布版本中被保留下来。 VERIFY 宏简化了对函数返回值的检查, 一般用来检查 Windows API 的返回值。由于 VERIFY 宏里的布尔表达式在发布版本里保留了下来,因此最好尽量不要使用这个宏以实现程序代码和调试代 码的完全分离。 (3 )ASSERT_VALID(指向 CObject 派生类对象的指针) ASSERT_VALID 宏通过调用重载的 AssertValid 函数来确定指向 CObject 派生类对象的指针是否有效。无论你什么时候从 CObject 派生类中得到一个对象,在对这个对象做任何操作之前都应该调用 ASSERT_VALID 宏。 (4) ASSERT_KINDOF(类名, 指向 CObject 派生类对象的指针) 这个宏用来验证指向 CObject 派生类对象的指针是否从某个特殊类中派生,在调用它之前先调用 ASSERT_VALID 宏。只 有在很特殊的场合下才用得到,如检测编译器可能错过的对象类型问题。 此外, 还有两个没有正式文件的ASSERT 宏的变种: ASSERT_POINTER(指针,指针类型), ASSERT_NULL_OR_POINTER(指 针,指针类型)。 3、什么时
候使用断言 把断言看作一种简单的制造栅栏的方法,这种栅栏能使错误在穿过自己时 暴露。 .检查函数的输入 .检查函数的输出 .检查对象的当前状态 .坚持逻辑变量的合理性和一致性 .检查类中的不变量 公有成员函数比私有和保护的成员函数需要更全面的断言。 不正确地使用断言会导致错误。断言应该检测那些在程序正常运行的时候永远都不可能出现的状态。断言是用来揭示错误 的,而不是用来纠正运行时刻错误的。 4、断言与防御性编程(Defensive Programming) 断言在调试的时候向程序员揭示运行时刻错误(调试版本里) ,而防御性编程使用户在运行程序(发布版本里)时,当出现 意外情况时程序仍能继续
工作。 实际上, 防御性的编程要求程序在检测到意外时返回一个“安全”的值 (比如布尔函数返回 false, 指针和句柄返回空值) ,一个错误代码或者抛出一个异常来解决问题。特定的防御性编程技术包括:处理无效函数参数和数据、 出现问题的时候程序失败、检查临界函数返回的错误代码以及处理异常。需要防御性编程的标准问题包括:错误的输入数据、