本章讨论了 C++ 编译器的异常处理实现。11.2 在多线程程序中使用异常中提供了附加信息。有关异常处理的更多信息,请参见由 Bjarne Stroustrup 编著的《The C++ Programming Language》第三版(Addison-Wesley 出版,1997)。
异常处理设计用于仅支持同步异常,例如数组范围检查。同步异常这一术语意味着异常只能源于 throw 表达式。
C++ 标准支持具有终止模型的同步异常处理。终止意味着一旦抛出异常,控制永远不会返回到抛出点。
异常处理没有设计用于直接处理诸如键盘中断等异步异常。不过,如果小心处理,在出现异步事件时也可以进行异常处理。例如,要用信号进行异常处理工作,您可以编写设置全局变量的信号处理程序,并创建另外一个例程来定期轮询该变量的值,当该变量值发生更改时抛出异常。不能从信号处理程序抛出异常。
有五个与异常有关的运行时错误消息:
没有异常处理程序
未预料到的异常抛出
异常只能在处理程序中重新抛出
在堆栈展开时,析构函数必须处理自身的异常
内存不足
运行时检测到错误时,错误消息会显示当前异常的类型和这五个错误消息之一。缺省情况下,会调用预定义的函数 terminate(),该函数又会调用 abort()。
编译器使用异常规范中提供的信息来优化代码生成。例如,禁止不抛出异常的函数表条目,而函数异常规范的运行时检查在任何可能的地方被消除。
如果知道程序中未使用异常,可以使用编译器选项 -features=no%except 抑制支持异常处理的代码的生成。该选项的使用可以稍微减小代码的大小,并能加快代码的执行速度。不过,用禁用的异常编译的文件链接到使用异常的文件时,在用禁用的异常编译的文件中的某些局部对象在发生异常时不会销毁。缺省情况下,编译器生成支持异常处理的代码。通常都要启用异常,只有时间和空间的开销是考虑的重要因素时才禁止异常。
因为 C++ 标准库 dynamic_cast 和缺省运算符 new 要求使用异常,所以在标准模式(缺省模式)下编译时不应禁用异常。
标准头文件 <exception> 提供了 C++ 标准中指定的类和异常相关函数。仅在标准模式(编译器缺省模式,或使用选项 -compat=5)下编译时才可访问该头文件。以下摘录的部分代码显示了 <exception> 头文件声明。
// standard header <exception> namespace std { class exception { exception() throw(); exception(const exception&) throw(); exception& operator=(const exception&) throw(); virtual ~exception() throw(); virtual const char* what() const throw(); }; class bad_exception: public exception {...}; // Unexpected exception handling typedef void (*unexpected_handler)(); unexpected_handler set_unexpected(unexpected_handler) throw(); void unexpected(); // Termination handling typedef void (*terminate_handler)(); terminate_handler set_terminate(terminate_handler) throw(); void terminate(); bool uncaught_exception() throw(); } |
标准类 exception 是所选语言构造或 C++ 标准库抛出的所有异常的基类。可以构造、复制及销毁类型为 exception 的对象,而不会生成异常。虚拟成员函数 what() 返回描述异常的字符串。
为了与 C++ 4.2 版中所用的异常兼容,还提供了头文件 <exception.h> 以用于标准模式。该头文件允许转换到标准 C++ 代码,并包含了不是标准 C++ 部分的声明。应在开发进度计划许可的情况下,更新代码以遵循 C++ 标准(使用 <exception> 而非 <exception.h>)。
// header <exception.h>, used for transition #include <exception> #include <new> using std::exception; using std::bad_exception; using std::set_unexpected; using std::unexpected; using std::set_terminate; using std::terminate; typedef std::exception xmsg; typedef std::bad_exception xunexpected; typedef std::bad_alloc xalloc; |
在兼容模式 (—compat[=4]) 下,头文件 <exception> 不可用,头文件 <exception.h> 指随 C++ 4.2 版提供的相同头文件。此处不再重述。
可以在会出现异常的程序中使用 setjmp/longjmp 函数,只要它们不会互相影响。
使用异常和 setjmp/longjmp 的所有规则分别适用。此外,仅当在点 A 抛出与在点 B 捕获的异常具有相同的效果时,从点 A 到点 B 的 longjmp 才有效。具体来说,不得使用 longjmp 进入或跳出 try 块或 catch 块(直接或间接),或使用longjmp 跳过自动变量或临时变量的初始化或 non-trivial 销毁。
不能从信号处理程序抛出异常。
切勿将 -Bsymbolic 与包含 C++ 代码的程序一起使用,应改用链接程序映射文件。如果使用 -Bsymbolic,不同模块中的引用会绑定到应是一个全局对象内容的不同副本。
异常机制依赖对地址的比较。如果您具有某项内容的两个副本,它们的地址就不等同且异常机制可能失败,这是由于异常机制依赖对假设为唯一地址内容的比较。
如果是使用 dlopen 打开共享库,必须使用 RTLD_GLOBAL ,异常才能生效。