📄 13章 异常处理.txt
字号:
●异常处理器放在catch块中。每个catch块以关键字catch开始,接着是括号内的包含类型(表示该块处理的异常类型)和可选参数名。后面是用花括号括起来的描述异常处理器的代码。
捕获异常时,执行catch块中的代码。
●catch处理器定义自己的范围。
●catch处理器中的参数可以命名也可以无名。如果是命名参数.则可以在处理器中引用这个参数,如果是无名参数(只指定匹配抛出对象类型的类型或用省略号表示所有类型),则处理器忽略所有抛出异常。处理器可以将对象重新抛出到外层try块中。
●也可以指定自定义行为,在set_terminate函数调用中指定函数名参数,指定执行另一个函数,代替函数terminnate。
●catch(...)表示捕获所有异常。
●也许某个抛出对象没有任何匹配的异常处理器。这时匹配搜索会继续到外面一层try块。
●异常处理器按顺序搜索,寻找匹配项.并执行第一个匹配的处理器。处理器执行完毕时.控制恢复到最后一个catch块后面的第一条语句。
●处理器的顺序会影响处理异常的方法。
●派生类对象可以由派生类类型的异常处理器和基类类型的异常处理器捕获。
●有时程序可能处理许多密切相关的异常类型。这时不是提供不同的异常类和catch处理器,而是可以用一个异常类和catch处理器处理一组异常。发生每个异常时,可以生成具有不同 private数据的异常对象。catch处理器通过检查private数据区分异常的类型。
●即使有准确匹配也还在匹配时要求标准转换,因为这个处理器出现在导致准确匹配的处理器之前。
●默认情况下,如果找不到一个异常的处理器,则程序终止。
●异常处理器无法直接访问try块范围中的变量。处理器所需的信息通常在抛出对象中传递。
●异常处理器可以用不同方式编写,可以检查错误和确定调用terminate;可以再抛出;通过抛出不同异常可以将一种异常变为另一种异常;可以进行必要的恢复,并恢复执行最后一个异常处理器之后的语句;可以检查错误原因,删除错误原因和重新调用原先导致异常的函数(这不会生成无穷递归);可以向运行环境返回一些状态值等等。
●应当将捕获基类类型的异常处理器放在捕获派生类类型的异常处理器之,否则基类类型的异常处理器捕获基类对象和从该类派生的所有对象。
●捕获异常时,try块中可能有已经分配而还没有释放的资源。catch处理器应释放这些资源。
●捕获异常的处理器也可以决定不处理异常。这时,处理器只要再抛出该异常。这种不带参数的throw再抛出异常。如果开始没有抛出异常,则再抛出异常调用terminate。
●即使处理器能处理异常,不管这个异常是否进行处理,处理器仍然可以再抛出异常以便在处理器之外继续处理。再抛出异常由外层try块检测,由所在try块之后列出的异常处理器处理。
●不带异常指定的函数可以抛出任何异常。
●函数unexpected调用set_unexpected函数指定的函数。如果没有用set_unexpected函数指定函数,则默认调用terminate。
●函数terminate可以显式调用,也可以在无法捕获抛出的异常时、在异常处理期间打乱堆栈时、作为调用unexpected的默认操作时或在异常导致堆栈解退时析构函数抛出异常的情况下调用terminate。
●函数set_terminate和set_unexpected的原型分别在头文件<terminate.h>和<unexpected.h>中。
●函数set_terminate和set_unexpected分别返回terminate和unexpected调用的最后一个函数的指针。这样就使程序员可以保存函数指针,以便后面恢复。
●函数set_terminate和set_unexpected取函数指针为参数。每个参数指向返回类型为void和无参数的函数。
●如果用户自定义终止函数的最后一个操作不是退出程序,则执行用户自定义终止函数的其他语句之后自动调用abort函数终止程序。
●try块之外抛出的异常会使程序终止。
●如果try块后面找不到处理器,则继续堆栈解退,直到找到相应处理器。如果最终找不到处理器.则调用terminate(默认用abort)退出程序。
●异常指定列出可抛出的异常。函数可以抛出指定异常或派生类型。如果抛出异常指定中没有指定的异常,则调用函数unexpected。
●如果函数抛出特定类类型的异常,则函数也可以抛出用public继承从该类派生的所有类异常。
●要捕获异常,异常处理器要访问所抛出对象的复制构造函数。
●构造函数中抛出异常时,对所有已构造的基类对象和抛出异常之前构造的成员对象调用析构函数。
●如果发生异常时部分构造了对象数组.则只调用已构造数组元素的析构函数。
●要捕获析构函数中抛出的异常,可以将调用析构函数的函数放在try块中,并提供相应类型的catch处理器。
●利用异常继承使异常处理器可以用相当简单的符号捕获相关错误。虽然可以捕获每个派生类的异常对象,但更简练的方法是捕获基类的异常对象。
●ANSI/ISO C++草案标准指定,出现new故障时抛出bad_alloc异常(在头文件<new>中定义)。
●许多编译器目前还不支持草案标准,仍然在ncw故障时返回0。
●函数set_new_handler(原型在头文件<new>或<new.h>中)取一个函数指针参数,所指函数不取参数并返回void。函数指针注册为new失败时要调用的函数。用set_new_handler注册new处理器之后,new不会在发生故障时抛出bad_alloc。
●auto_ptr类对象维护动态分配内存的指针。当auto_ptr对象超出范围时,对指针数据成员进行一个delete操作,auto_ptr类模板提供了*和->运算符,因此auto_ptr对象可以像普通指针变量一样使用。
●C++草案标准提供了标准库异常层次。这个层次以基类exception开始(在头文件<exception>中定义),该基类提供服务what(),在每个派生类中重定义。发出相应错误消息。
●如果发生意外异常时,通过在函数的抛出表中加上std::bad_exception,unexpected()抛出bad_exception而不是(默认)终止程序或调用set_unexpected指定的另一函数。
术 语
abort() catch an exception 捕获一个异常
assert macro assert 宏 catch argument catch参数
auto_ptr catch block catch块
bad_alloc catch(...)
bad_cast dynamic_cast
bad_typeid empty exception specification 空异常指定
catch a group of exceptions 捕获一组异常 empty throw specification 空throw指定
enclosing try block 所在try块 nothrow
exception异常 out_of_range
exception decimation 异常声明 overflow_error
exception handler 异常处理器 rethrow anexception再抛出异常
<exception>header file <exception>头文件 robustness 健壮性
exception list异常表 mntime—error
exception object 异常对象 set_new_handler()
exception specification 异常指定 set_terminate()
exceptional condition 异常条件 set_unexpected()
exit() stack unwinding 堆栈解退
fault tolerance 容错 std::bad_exception
function with no exception specification 不带异常 <stdexcept>header file <stdexcept>头文件
指定的函数terminate()
handle an exception 处理异常 throw an exception 抛出异常
handier for a base class 基类处理器 throw an unexpected exception 抛出意外异常
handler for a derived class 派生类处理器 throw expression throw 表达式
invalid_argument throw list throw表
length_error throw point throw点
logic_error throw without arguments 不带参数抛出
<memory>header file <memory>头文件 throw()
mission-critical application 任务关键的应用 try block try块
nested exception handlers 嵌套的异常处理器 underflow_error
new_handler unexpected()
<new>header file <new>头文件 uncaught exception 未捕获异常
自测练习
13.1 列出五个常见的异常例子。
13.2 说明异常处理方法不能用于传统程序控制的原因。
13.3 为什么异常适合处理库函数产生的错误?
13. 4 什么是“资源泄漏”?
13.5 如果try块中不抛出异常,try块执行完毕之后控制转到哪里?
13.6 如果在try块之外抛出异常,会发生什么情况?
13.7 说明使用catch(...)的主要优点和主要缺点。
13.8 如果没有匹配所抛出对象类型的catch处理器,会发生什么情况?
13.9 如果有多个匹配所抛出对象类型的catch处理器,会发生什么情况?
13.10 为什么程序员要指定基类类型为catch处理器类型,然后抛出派生类类型的对象?
13.11 catch处理器如何编写成处理相关错误类型而不用异常类之间的继承?
13.12 catch处理器中用哪种指针类型捕获任何指针类型的所有异常?
13.13 假设有准确匹配异常对象类型的catch处理器,什么情况下该异常对象类型会执行不同的catch处理器?
13.14 抛出异常是否一定终止程序?
13. 15 catch处理器抛出异常时会发生什么情况?
13. 16 throw;语句有什么用?
13. 17 程序员如何限制函数可以抛出的异常类型?
13. 18 如果函数抛出函数异常指定中不允许的异常类型,会发生什么情况?
13.19 try块抛出异常时,其中已经构造的自动对象发生什么情况?
自测练习答案
13. 1 内存不足以满足new的请求、数组下标超界、运算溢出、除数为0、无效函数参数。
13.2 (a)异常处理是用于处理不常发生的情况,通常在程序准备终止时使用。因此,C++编译器的编写人员不像对正常应用程序代码一样对其实现最优化性能。(b)使用传统控制结构的控制流通常比使用异常更清晰更有效。(c)另一危险是堆栈解退,异常发生之前分配的资源可能无法释放。(d)这些“增加的”异常可能打乱真正的错误类型异常。程序员更难跟踪异常种类。例如,程序处理大量异常时,无法确定catch(...)捕获的是什么异常。
13.3 库函数很难满足用户特殊需求的错误处理。
13. 4 退出程序会使其他程序无法使用其资源,从而造成资源泄漏。
13.5 忽略该时块的异常处理器(catch块中),程序在最后一个catch块后重新执行。
13.6 try块之外抛出的异常会使程序终止。
13.7 catch(…)能捕获时块中抛出的所有错误。其优点是可以捕获所有错误,缺点是catch没有参数,无法引用抛出的所有错误中的信息,无法知道错误原因。
13.8 这时匹配搜索会继续到外面一层try块。这个过程一直继续,也许最终还是没有任何匹配的异常处理器。这时调用terminate(默认调用abort终止程序)。可以用set_terminate的参数提供另一terminate函数。
13.9 执行时块后面第一个匹配的异常处理器。
13.10 这样可以很好地捕获相关类型的异常。
13. 11 可以用一个异常类和catch处理器处理一组异常。发生每个异常时,可以生成具有不同private数据的异常对象。catch处理器通过检查private数据区分异常的类型。
13. 12 void*。
13.13 要求标准转换的处理器可能出现在具有准确匹配的处理器之前。
13. 14 不一定,但它终止抛出异常的块。
13. 15 异常由导致异常的catch处理器所在try块(如果有)的相关catch处理器(如果有)处理。
13.16 再抛出异常。
13.17 提供从函数可抛出异常类型的异常指定表。
13.18 调用函数unexpected。
13.19 通过堆栈解退过程,调用每个对象的析构函数。
练 习
13. 20 列出文中所列的程序中发生的各种异常条件,尽量多列几个异常条件。对每个异常条件,简单描述程序如何用本章介绍的异常处理方法处理这个异常。典型的异常有:内存不足以满足new的请求、数组下标超界、运算溢出、除数为0、无效函数参数。
13.21 什么情况下程序员在定义处理器捕获对象类型时不提供参数名?
13. 22 程序包含下列语句:
throw;
这种语句通常出现在什么地方?如果出现在其他地方,会发生什么情况?
13.23 下列语句通常出现在什么情况下?
catch(...){throw};
13.24 比较异常处理与各种其他错误处理方法。
13.25 列出异常处理比传统错误处理方法的优势。
13.26 说明为什么不宜用异常作为程序控制的替换方法。
13.27 说明处理相关异常的方法。
13.28 到本章为止,我们发现处理构造函数所发现的错误比较麻烦。异常处理提供了处理这种错误更好的办法。考虑String类的构造函数。这个构造函数用new取得自由空问。假设new操作失败,说明不用异常时如何处理这种情况,讨论关键点。说明使用异常时如何处理这种情况。说明异常处理的优点在哪里。
13.29 假设程序抛出异常,开始执行相应的异常处理器。再假设异常处理器本身抛出相同的异常。这样会生成无穷递归吗?编写一个C++程序,测试得出的结论。
13.30 用继承方法生成异常基类和各种派生类。然后显示指定基类的catch处理器能捕获派生类的异常。
13.31 列出返回double或int的条件异常。提供一个int catch处理器和一个double catch处理器。
说明不管返回double或int,都只执行double catch处理器。
13.32 编写一个C++程序,产生和处理内存溢出错误。程序通过运算符new循环请求生成动态存储空间。
13.33 编写一个C++程序列出调用块中构造的所有对象的析构函数之后再从块中抛出异常。
13.34 编写一个C++程序,演示只调用发生异常之前构造的成员对象的成员对象析构函数。
13.35 编写一个C++程序,演示catch(...)如何捕获任何异常。
13.36 编写一个C++程序,表明异常处理器的顺序很重要。执行的是第一个匹配的异常处理器。编译和运行程序,显示执行不同异常处理器时的不同效果。
13.37 编写一个C++程序,表明构造函数传递构造失败信息给try块后面的异常处理器。
13.38 编写一个C++程序,用异常类的多重继承层次生成要考虑异常处理器顺序的情况。
13.39 用setjmp/Iongjmp时,程序可以立即从深层嵌套函数调用将控制转移到错误程序。但由于堆栈解退,不能调用嵌套函数调用期间生成的自动对象的析构函数。编写一个C++程序,演示的确没有调用嵌套函数调用期间生成的自动对象的析构函数。
13.40 编写一个C++程序,演示再抛出异常。
13.41 编写一个C++程序,用set_unexpected对unexpected设置用户自定义函数,再次用set_unexpected,然后将unexpected复位为原先的函数。编写一个C++程序,测试set_terminate和terminate。
13.42 编写一个C++程序,显示本身有时块的函数不必捕获try块中产生的每个错误。有些错误留给外层范围处理。
13.43 编写一个C++程序,从深层嵌套函数调用抛出错误,并让调用链所在try块后面的catch处理器捕获这个异常。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -