您的位置:首页 > 其它

在VC程序中使用调试语句----(三)异常

2005-11-17 01:43 302 查看
三、异常

1、基本概念

  错误是一种条件,在这种条件下,如果不执行额外的处理,线程就不能正常地执行下去。异常是用于处理错误的。使用异常的一个很明显的好处就是它们通过发出错误信号,可以让程序代码和错误处理代码分开,而且不会让程序忽略错误,你不用不断地检查函数的返回值,因此它们将程序代码简单化。另一个好处是它们不需要严格的编程作风。

异常的基本特性:

.异常是基于每个进程而提出并处理的。

.异常不能被线程忽略,必须被处理。

.未处理的异常会使进程结束,而不仅仅是结束线程。

.异常出来在释放栈时会释放所有的栈对象,避免了资源的漏洞。

.异常处理需要大量的额外操作,使得它不适于经常运行的代码。

.可以抛出任何类型的异常对象,除了整数。

如果正确执行,异常处理有下面的特性:

.异常是不是正常的运行结果,是特殊情况。

.异常在返回值无效的情况下使用。

.异常是可靠的,不可能被忽略。

.异常简化了错误处理,简化了程序代码,使错误处理更加方便。

  Visual C++的默认情况下,在调试版本中处理异常,而在发布版本中并不进行处理。由于异常也是错误,Windows异常码采用了同Windows错误码一样的位映射模式,为一个32位的值,这些码由Microsoft定义,任何异常码的最高四位总是1100(二进制),即十六进制里的0xC。

2、Windows结构异常和C++异常

  Windows结构异常作为硬件异常(如访问非法或被零除)或操作系统异常的结果被抛出,C++异常只能由throw语句抛出。Windows结构异常处理不能处理对象的解析,因此你应该在C++程序中一直使用C++异常。然而,C++异常不能处理硬件和操作系统异常,你的程序需要将结构异常转化为C++异常。C++异常并不直接从你的程序代码中抛出而是从C++运行库中抛出,因此你需要调用栈窗口来返回你的代码。为了正确处理硬件和操作系统异常,你可以创建自己的异常类并使用_set_se_translator函数安装一个结构异常向C++异常的转化器,但不要捕获那些不能恢复所产生问题的转化后的结构异常。

3、MFC中的异常

  在MFC中,所有的异常对象都是从CException基类(它有使用起来非常方便的GetErrorMessage和ReportError成员函数)中派生来的。大多数的MFC异常对象都是动态分配的,而且当它们被捕获时,必须被删除,而没有被捕获的MFC异常由MFC本身在AfxCallWndProc函数中捕获并删除。

4、异常的开销

  当抛出C++异常时,函数调用链将从此回溯搜索,寻找可以处理抛出这类异常的处理器。若没找到,进程结束。如果找到,调用栈将被释放,所有的自动(局部)变量也将释放,然后栈将被整理为异常处理器的上下文相关设备。因此异常开销由一个异常处理器目录和一个活动的自动变量表(它需要额外的代码、内存,而且不论异常是否抛出,都会运行),还得加上函数调用链的搜索、自动变量的解析和栈的调整(它只在抛出异常的时候需要执行)组成。

5、异常策略

(1)抛出时机

  抛出异常的时机应该是一个函数发现一个错误,如果没有一些特殊的操作,该错误能阻止程序正常的运行,而这种操作它自己不能完成,或是在函数不可能有返回值的时候。

  使用异常处理更简单,更可靠,更有效,可以创建更健壮的代码。然而,应该只在意外的情况下使用异常处理。如果你认为一个指针应该是空值,这种条件下就直接在代码中检查这个值,而不要使用异常。

(2)何时捕获

对于这个问题,有一些可能的标准:

.当函数知道如何处理这个异常时。

.当这个函数可以合理地处理这个异常而高级的函数不知道如何处理时。

.当抛出异常可能使进程崩溃时。

.当函数可以继续执行它的任务时。

.当需要整理分配好的资源时。

  异常处理的一个缺点是它可能导致资源的泄露。因此,防止资源泄露更应该是保持程序异常安全的一部分。栈释放时会自动整理局部变量,但不包括动态分配的变量。可以使用智能(smart)指针来保护你的代码在存在异常的情况下不会产生资源泄漏。

(3)怎样捕获

.非MFC的C++异常应该通过引用来捕获。使用引用捕获异常不需要删除异常对象(因为使用引用捕获的异常会在栈中传送),而且它保留了多态性(因此你捕获的异常对象正是你抛出的异常对象)。

.MFC异常应该通过指针来捕获。使用指针捕获异常需要你删除对象。因为它们通常从堆中分配,当你处理完异常之后,需要调用Delete成员函数来删除。你不可以使用省略捕获处理器捕获MFC异常,这会导致一个内存泄露。必须使用Delete成员函数删除MFC异常,而不用delete,因为一些MFC异常为静态对象创建。

在释放栈的过程中抛出异常会导致进程的终止。释放栈涉及到调用析构函数,异常可以阻止调用delete操作符,这样会有资源泄漏,因此异常最好不要从析构函数中抛出。如果非要在析构函数里抛出异常,必须妥善处理,避免资源泄漏。

6、异常与防御性编程

  在异常发生时继续执行程序,远比执行一个正常的关闭动作要重要。如果可能,应该将精力集中在继续执行程序,并在必须的情况下才正常地关闭程序。可能最根本的正常关闭是一个在崩溃时可以重新启动自己的进程,这是Windows资源管理器使用的一种技术。

  如果一个与错误相关的C++异常是可预料的,如果它发生在非关键性的代码中,如果它不是发生在程序启动或结束过程中或一个不可恢复的结构异常的结果中,这个程序就可以从其中恢复。

  一旦你的程序可以从与错误相关的异常中恢复,应该先检查程序的状态和它的文档。如果程序和文档已经被破坏了,进程也应该终止运行。否则,程序需要通知客户机确定动作的过程。如果客户机同意执行下去,程序应该恢复错误并继续执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: