Chapter 5 Statements
5.6 try 语句块和异常处理
异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围。异常处理包括:
-
throw 表达式(throw expression),异常检测部分使用 throw 表达式来表示它遇到了无法处理的问题。我们说 throw 引发(raise)了异常。
-
try 语句块(try block),异常处理部分使用 try 语句块处理异常。try 语句块以关键字 try 开始,并以一个或多个 catch 子句(catch clause)结束。try 语句块中代码抛出的异常通常会被某个 catch 子句处理。因为 catch 子句 “处理” 异常,所以它们也被称作异常处理代码(exception handler)
-
一套异常类(exception class),用于在 throw 表达式和相关的 catch 子句之间传递异常的具体信息。
5.6.1 throw 表达式
程序的异常检测部分使用 throw 表达式引发一个异常。throw 表达式包含关键字 throw 和紧随其后的一个表达式,其中表达式的类型就是抛出异常的类型。throw 表达式后面通常紧跟一个分号,从而构成一条表达式语句。
在真实的程序中,应该把对象相加的代码和用户交互的代码分离开来。
5.6.2 try 语句块
try 语句块的通用语法形式是
try {
program-statements
} catch (exception-declaration) {
handler-statements
} catch (exception-declaration) {
handler-statements
} // ...
跟在 try 语句块中的 program-statements 组成程序的正常逻辑,像其他任何块一样,program-statements 可以有包括声明在内的任意 C++ 语句。一如往常,try 语句快内声明的变量在块外部无法访问,特别是在 catch 子句内也无法访问。
复杂系统中,程序在遇到抛出异常的代码前,其执行路径可能已经经过了多个 try 语句块。例如,一个 try 语句块可能调用了包含另一个 try 语句块的函数,新的 try 语句块可能调用了包含又一个 try 语句块的新函数,以此类推。
寻找处理代码的过程与函数调用链刚好相反。当异常被抛出时,首先搜索抛出该异常的函数。如果没有找到匹配的 catch 子句,终止该函数,并在调用该函数的函数中继续寻找。如果还是没有找到匹配的 catch 子句,这个新的函数也被终止,继续搜索调用它的函数。以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的 catch 子句为止。
如果最终还是没能找到任何匹配的 catch 子句,程序转到名为 terminate 的标准函数库函数。该函数的行为与系统有关,一般情况下,执行该函数将导致程序非正常退出。
对于那些没有任何 try 语句块定义的异常,也按照类似的方式处理:毕竟,没有 try 语句块也就意味着没有匹配的 catch 子句。如果一段程序没有 try 语句块且发生了异常,系统会调用 terminate 函数并终止当前程序的运行。
5.6.3 标准异常
C++ 标准库定义了一组类,用于报告标准库函数遇到的问题。这些异常也可以在用户编写的程序中使用,它们分别定义在 4 个头文件中:
- exception 头文件定义了最通用的异常类 exception。它只报告异常的发生,不提供任何额外信息。
- stdexcept 头文件定义了几种常用的异常类,见表 5.1。
- new 头文件定义了 bad_alloc 异常类型。
- type_info 头文件定义了 bad_cast 异常类型。
标准库异常类只定义了集中运算,包括创建或拷贝异常类型的对象,以及为异常类型的对象赋值。我们只能以默认初始化的方式初始化 exception、bad_alloc 和 bad_cast 对象,不允许为它们提供初始值。
其他异常类型的行为则恰好相反:应该使用 string 对象或者 C 风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。当创建此类对象时,必须提供初始值,该初始值包含有错误相关的信息。
异常类型只定义了一个名为 what 的成员函数,该函数没有任何参数,返回值是一个指向 C 字符串的 const char*。该字符串的目的是提供关于异常的一些文本信息。
what 函数返回的 C 风格字符串的内容与其他异常对象的类型有关。如果异常类型有一个字符串初始值,则 what 返回该字符串。对于其他无初始值的异常类型来说,what 返回的内容也由编译器决定。