异常机制提供程序中错误检测与错误处理部分之间的通信。C++ 的异常处理中包括:
-
由标准库定义的一组异常类,用来在 throw 和相应的 catch 之间传递有关的错误信息。
throw 表达式
回顾将两个 Sales_item 类型对象相加的程序,就是一个简单的例子。该程序检查读入的记录是否来自同一本书。如果不是,就输出一条信息然后退出程序。
Sales_item item1, item2; std::cin >> item1 >> item2; // first check that item1 and item2 represent the same book if (item1.same_isbn(item2)) { std::cout << item1 + item2 << std::endl; return 0; // indicate success } else { std::cerr << "Data must refer to same ISBN" << std::endl; return -1; // indicate failure }
在使用 Sales_items 的更简单的程序中,把将对象相加的部分和负责跟用户交互的部分分开。在这个例子中,用 throw 抛出异常来改写检测代码:
// first check that data is for the same item if (!item1.same_isbn(item2)) throw runtime_error("Data must refer to same ISBN"); // ok, if we're still here the ISBNs are the same std::cout << item1 + item2 << std::endl;
这段代码检查 ISBN 对象是否不相同。如果不同的话,停止程序的执行,并将控制转移给处理这种错误的处理代码。
throw 语句使用了一个表达式。在本例中,该表达式是 runtime_error 类型的对象。runtime_error 类型是标准库异常类中的一种,在 stdexcept 头文件中定义。在后续章节中很快就会更详细地介绍这些类型。我们通过传递 string 对象来创建 runtime_error 对象,这样就可以提供更多关于所出现问题的相关信息。
try 块
try 块的通用语法形式是:
try { program-statements } catch (exception-specifier) { handler-statements } catch (exception-specifier) { handler-statements } //...
try 块以关键字 try 开始,后面是用花括号起来的语句序列块。try 块后面是一个或多个 catch 子句。每个 catch 子句包括三部分:关键字 catch,圆括号内单个类型或者单个对象的声明——称为异常说明符,以及通常用花括号括起来的语句块。如果选择了一个 catch 子句来处理异常,则执行相关的块语句。一旦 catch 子句执行结束,程序流程立即继续执行紧随着最后一个 catch 子句的语句。
try 语句内的 program-statements 形成程序的正常逻辑。这里面可以包含任意 C++ 语句,包括变量声明。与其他块语句一样,try 块引入局部作用域,在 try 块中声明的变量,包括 catch 子句声明的变量,不能在 try 外面引用。
在前面的例子中,使用了 throw 来避免将两个表示不同书的 Sales_items 对象相加。想象一下将 Sales_items 对象相加的那部分程序与负责与用户交流的那部分是分开的,则与用户交互的部分也许会包含下面的用于处理所捕获异常的代码:
while (cin >> item1 >> item2) { try { // execute code that will add the two Sales_items // if the addition fails, the code throws a runtime_error exception } catch (runtime_error err) { // remind the user that ISBN must match and prompt for another pair cout << err.what() << "\nTry Again? Enter y or n" << endl; char c; cin >> c; if (cin && c == 'n') break; // break out of the while loop } }
关键字 try 后面是一个块语句。这个块语句调用处理 Sales_item 对象的程序部分。这部分也可能会抛出 runtime_error 类型的异常。
上述 try 块提供单个 catch 子句,用来处理 runtime_error 类型的异常。在执行 try 块代码的过程中,如果在 try 块中的代码抛出 runtime_error 类型的异常,则处理这类异常的动作在 catch 后面的块语句中定义。本例中,catch 输出信息并且询问用户是否继续进行异常处理。如果用户输入'n',则结束 while;否则继续循环,读入两个新的 Sales_items 对象。
通过输出 err.what() 的返回值提示用户。大家都知道 err 返回 runtime_error 类型的值,因此可以推断出 what 是 runtime_error 类的一个成员函数(1.5.2 节)。每一个标准库异常类都定义了名为 what 的成员函数。这个函数不需要参数,返回 C 风格字符串。在出现 runtime_error 的情况下,what 返回的 C 风格字符串,是用于初始化 runtime_error 的 string 对象的副本。如果在前面章节描述的代码抛出异常,那么执行这个 catch 将输出。
Data must refer to same ISBN Try Again? Enter y or n
-
在 <stdexcept> 头文件中定义的标准异常类
exception
最常见的问题。
runtime_error
运行时错误:仅在运行时才能检测到问题
range_error
运行时错误:生成的结果超出了有意义的值域范围
overflow_error
运行时错误:计算上溢
underflow_error
运行时错误:计算下溢
logic_error
逻辑错误:可在运行前检测到问题
domain_error
逻辑错误:参数的结果值不存在
invalid_argument
逻辑错误:不合适的参数
length_error
逻辑错误:试图生成一个超出该类型最大长度的对象
out_of_range
逻辑错误:使用一个超出有效范围的值
new头文件定义了 bad_alloc 异常类型,提供因无法分配内在而由 new抛出的异常。
type_info 头文件定义了 bad_cast 异常类型。
标准库异常类
标准库异常类只提供很少的操作,包括创建、复制异常类型对象以及异常类型对象的赋值。 exception、bad_alloc 以及 bad_cast 类型只定义了默认构造函数,无法在创建这些类型的对象时为它们提供初值。其他的异常类型则只定义了一个使用 string 初始化式的构造函数。当需要定义这些异常类型的对象时,必须提供一想 string 参数。string 初始化式用于为所发生的错误提供更多的信息。
异常类型只定义了一个名为 what 的操作。这个函数不需要任何参数,并且返回 const char* 类型值。它返回的指针指向一个 C 风格字符串。使用 C 风格字符串的目的是为所抛出的异常提出更详细的文字描述。
what 函数所返回的指针指向 C 风格字符数组的内容,这个数组的内容依赖于异常对象的类型。对于接受 string 初始化式的异常类型,what 函数将返回该 string 作为 C 风格字符数组。对于其他异常类型,返回的值则根据编译器的变化而不同。
-