13.7 捕获异常
异常处理器放在 catch 块中。每个catch块以关键字catch开始,接着是括号内包含的类型(表示该块处理的异常类型)和可选参敷名.后面是用花括号括起来的描述异常处理器的代码。捕获异常时,执行 catch 块中的代码。
catch 处理器定义自己的范围。catch在括号中指定要捕获的对象类型。cateh处理器中的参数可以命名也可以无名。如果是命名参数,则可以在处理器中引用这个参数。如果是无名参数(只指定匹配抛出对象类型的类型),则信息不从抛出点传递到处理器中,只是把控制从抛出点转到处理器中.许多异常都可以这样。
常见编程错误 13.5
指定逗号分开的 catch 参数表是个语法错误。
抛出异常对象类型与catch处理器参数类型相符时,执行该类型的catch块,即执行该类型的异常处理器。
catch 处理器中在当前活动try块后面第一个符合所抛出对象的处理器捕获异常。稍后将介绍匹配规则。
没有捕获的异常调用terminate(默认调用abort终止程序。也可以指定自定义行为,在set_terminate函数调用中指定函数名参数,从而执行另一个函数。
catch后面是括号和省略号:
carch(...)
表示捕获所有异常。
常见编程错误 11.6
将catch(...)放在其他catch块前面时,其他块根本无法执行。catch(...)总是作为try块后面的处理器列表中最后一个处理器。
软件工程视点 12.7
用catch(...)捕获异常的一个缺点是通常无法确定异常类型。另一个缺点是没有命名参数,就无法在异常处理器中引用异常对象。
也许某个抛出对象没有任何匹配的异常处理器。这时匹配搜索会继续到外面一层try块。这个过程一直继续,也许最终还是没有任何匹配的异常处理器。这时调用terminate(默认调用abort)终止程序。
异常处理器按顺序搜索,寻找匹配项,并执行第一个匹配的处理器。处理器执行完毕时,控制恢复到最后一个catch块后面的第一条语句,即该try块中最后一个异常处理器后面的第一条语句。
也许几个异常都匹配所抛出的对象。这时执行第一个匹配的异常处理器。如果几个异常处理器都匹配所抛出的对象,而每个异常处理器用不同方法处理异常,则处理器的顺序会影响处理异常的方法。
也许几个catch处理器中都包含匹配所抛出对象的类类型。这可能有几个原因:第一,有一个捕获任何异常的catch(…)处理器。第二,由于继承层次,派生类对象可以由派生类类型的异常处理器和基类类型的异常处理器捕获。
常见编程错误 13.7
将捕获基类类型的异常处理器放在捕获派生类类型的异常处理器之前是个逻辑错误。基类类型的异常处理器捕获从该类派生的所有对象.因此根本不会执行派生类类型的异常处理器。
测试与调试提示 13.1
程序员确定异常处理器列出的顺序。这个顺序可能影响try块中所产生异常的处理方法。如果程序处理异常时出现意外行为,可能是前面的catch块截获并处理了这个异常,使其没有被所需的异常处理器处理。
有时程序可能处理许多密切相关的异常类型。这时不是提供不同的异常类和catch处理器,而是用一个异常类和catch处理器处理一组异常。发生每个异常时,可以生成具有不同private数据的异常对象。cateh 处理器通过检查private数据区分异常的类型。
何时发生匹配呢?下列情况下,catch 处理器参数匹配所抛出对象的类型:
- 实际是同一类型。
- cateh 处理器参数类型是所抛出对象类型的Public基类。
- 处理器参数为基类指针或引用类型,而抛出对象为派生类指针或引用类型。
- cateh 处理器为 cateh(...)。
常见编程错误 13.8
将带 void *
参数类型的异常处理器放在具有其他指针类型的异常处理器前面是个逻辑错误。void*处理器捕获所有指针类型的异常,因此其他异常处理器根本不可能执行。
需要有准确的类型匹配。寻找处理器时只允许派生类向基类转换,而不允许其他转换和升级。
可以抛出const对象。这时catch处理器参数类型也应声明为const。
默认情况下,如果异常没有相应的处理器,则程序终止。尽管这应该是正确的做法,但程序员通常不这样做,从而造成错误,使得程序继续执行。
一个try块后面跟几个cateh块类似于switch语句。不必用break退出异常处理器(跳过其余异常处理器)。每个catceh块定义不同的范围,而switch浯句中的所有case都在switch的范围中。
常见编程错误 13.9
将分号放在try块后面或try块后面的任何catch处理器(除最后一个catch处理器)后面是个语法错误。
异常处理器无法访问try块中定义的自动化对象,因为发生异常时try块终止,try中的所有自动对象均在处理器开始执行之前删除。
异常处理器中发生异常时会出现什么情况呢?异常处理器开始执行时,原先捕获的异常正在处理。因此异常处理器中发生异常时应在抛出原异常的try块之外处理。
异常处理器可以用不同的方式编写,可以检查错误和确定调用terminate;可以再抛出异常(见13 8节);可以将一种异常变为另一种异常,抛出不同异常;可以进行必要的恢复,并恢复执行最后一个异常处理器之后的语句;可以检查错误原因、删除错误原因和重新调用原先导致异常的函数(这不会生成无穷递归);可以向程序环境返回一些状态值等等。
软件工程视点 13.8
最好在设计过程中把异常处理策略加进系统,后面要再把异常处理策略加进第系统是很困难的。
try 块不抛出任何异常而正常地执行完毕时,控制传入try块后面最后一个catch处理器之后的第一条浯句。
在catch处理器中用 return 语句无法返回抛出点。这种 return 语句只是返回调用catch块所在函数的函数。
常兄编程错误 13.10
如果认为处理异常后控制会返回throw后面妁第一条语句是个逻辑错误。
软件工程视点 13.9
传统控制流程不用异常的另一个原因是这些“增加的”异常可能搞乱真正的错误型异常。程序员更难跟踪异常种类。例如,程序处理大量异常时,无法确定catch(...)捕获的是什么异常。异常情况只能针对不常见的极少数情况。
捕获异常时,try 块中可能有已经分配而还没有释放的资源。如果可能,cateh处理器应释放这些资源。例如,catch处理器删除new分配的空间和关闭抛出异常的try块中打开的任何文件。
catch处理器处理错误之后可以让程序继续正确执行,也可以终止程序。
catch处理器本身也可以发现错误和抛出异常。这种异常不是由抛出异常的catch处理器所在try块中的catth处理器处理,而是由外层try块中的相关catch处理器处理。
常见编程错误 13.11
假设catch处理器抛出的异常由该处理器处理,或由抛出异常的try决中相关的处理器(导致执行原先的catch处理器)处理是个逻辑错误。