15.3.10 异常何时会迷失方向(1)
异常被引发后,在两种情况下,会导致问题。首先,如果它是在带异常规范的函数中引发的,则必须与规范列表中的某种异常匹配(在继承层次结构中,类类型与这个类及其派生类的对象匹配),否则称为意外异常(unexpected exception)。在默认情况下,这将导致程序异常终止(虽然C++(www.cppentry.com)11摒弃了异常规范,但仍支持它,且有些现有的代码使用了它)。如果异常不是在函数中引发的(或者函数没有异常规范),则必须捕获它。如果没被捕获(在没有try块或没有匹配的catch块时,将出现这种情况),则异常被称为未捕获异常(uncaught exception)。在默认情况下,这将导致程序异常终止。然而,可以修改程序对意外异常和未捕获异常的反应。下面来看如何修改,先从未捕获异常开始。
未捕获异常不会导致程序立刻异常终止。相反,程序将首先调用函数terminate( )。在默认情况下,terminate( )调用abort( )函数。可以指定terminate( )应调用的函数(而不是abort( ))来修改terminate( )的这种行为。为此,可调用set_terminate( )函数。set_terminate( )和terminate( )都是在头文件exception中声明的:
其中的typedef使terminate_handler成为这样一种类型的名称:指向没有参数和返回值的函数的指针。set_terminate( )函数将不带任何参数且返回类型为void的函数的名称(地址)作为参数,并返回该函数的地址。如果调用了set_terminate( )函数多次,则terminate( )将调用最后一次set_terminate( )调用设置的函数。
来看一个例子。假设希望未捕获的异常导致程序打印一条消息,然后调用exit( )函数,将退出状态值设置为5。首先,请包含头文件exception。可以使用using编译指令、适当的using声明或std ::限定符,来使其声明可用。
然后,设计一个完成上述两种操作所需的函数,其原型如下:
最后,在程序的开头,将终止操作指定为调用该函数。
现在,如果引发了一个异常且没有被捕获,程序将调用terminate( ),而后者将调用MyQuit( )。
接下来看意外异常。通过给函数指定异常规范,可以让函数的用户知道要捕获哪些异常。假设函数的原型如下:
则可以这样使用该函数:
知道应捕获哪些异常很有帮助,因为默认情况下,未捕获的异常将导致程序异常终止。
原则上,异常规范应包含函数调用的其他函数引发的异常。例如,如果Argh( )调用了Duh( )函数,而后者可能引发retort对象异常,则Argh( )和Duh( )的异常规范中都应包含retort。除非自己编写所有的函数,并且特别仔细,否则无法保证上述工作都已正确完成。例如,可能使用的是老式商业库,而其中的函数没有异常规范。这表明应进一步探讨这样一点,即如果函数引发了其异常规范中没有的异常,情况将如何?这也表明异常规范机制处理起来比较麻烦,这也是C++(www.cppentry.com)11将其摒弃的原因之一。