# 异常处理 ## 三种处理方式 - throw 表达式 - try-catch 语句块 - 一套异常类 ## throw表达式 实现操作对象的代码和用户交互的代码相互分离。 ```C++ #include if(item1.isbn() != item2.isbn()){ throw runtime_error("Data must refer to same ISBN"); } cout << item1+item2 << endl; ``` 注意抛出的类型 `runtime_error` 是标准库异常类型的一种,定义在 `stdexcept` 头文件中。注意这里的 `runtime_error` 是一个小写字母的类名,说明其是一个很老的类设计。 执行一个 `throw` 抛出异常,那么异常后面的程序将不再执行。 ## try-catch语句块 ```C++ try{ program-statements }catch(exception-declaration){ handler-statements; }catch(exception-declaration){ handler-statements; throw; // 如果单独的catch不能完全处理好该异常,那么可以重新抛出该异常 } ... ``` 当抛出一个异常后,程序暂停了当前函数的运行,并立即开始寻找与抛出异常相匹配的 `catch` 子句。多数情况,当 `throw` 出现在一个 `try` 语句块内时,检查与该语句块并联的 `catch` 子句。如果找到匹配的异常类型,就使用该 `catch` 子句来处理该异常。如果没有找到与之匹配的异常类型的 `catch` 子句且该语句块嵌套在其他 `try` 语句块中,则继续检查与外层 `try` 匹配的 `catch` 子句块。如果还是没有匹配的那么继续重复往外层抛出该异常。这就是栈展开。最后如果始终没有与该异常类型相匹配的,程序将调用标准库函数 `terminate` ,这个函数负责终止程序的执行过程。也就是说,如果一个异常始终没有被捕获,那么它将终止当前的程序。 ## 标准异常 标准库定义了一组类,用于报告标准库函数遇到的问题,分别在4个头文件中。 - `exception` 头文件定义了最通用的异常类 `exception` 。这些异常只报告异常的发生,不提供任何额外的信息。 - `stdexcept` 头文件定义了几种常用的异常类。 - `new` 头文件定义了`bad_alloc` 异常类型。 - `type_info` 头文件定义了 `bad_cast` 异常类型。 | ``异常类 | 详细说明 | | ------------------- | ---------------------------------------------- | | `exception` | 通用异常类 | | `runtime_error` | 只有在运行时才能检测的异常 | | `range_error` | 运行时错误:生成的结果超出了有意义的值域范围 | | `overflow_error` | 运行时错误:计算上溢出 | | `underflow_error` | 运行时错误:计算下溢出 | | `logic_error` | 程序逻辑错误 | | `domain_error` | 逻辑错误:参数对应的结果值不存在 | | `invalid_argument` | 逻辑错误:无效参数 | | `length_error` | 逻辑错误:试图创建一个超出该类型最大长度的对象 | | `out_of_range` | 逻辑错误:使用一个超出有效范围的值 | 标准库异常类继承体系 ```mermaid classDiagram exception <|-- bad_cast exception <|-- runtime_error exception <|-- logic_error exception <|-- bad_alloc runtime_error <|-- range_error runtime_error <|-- overflow_error runtime_error <|-- underflow_error runtime_error <|-- system_error logic_error <|-- domain_error logic_error <|-- invalid_argument logic_error <|-- length_error logic_error <|-- out_of_range logic_error <|-- future_error ``` ## noexcept异常说明 对于用户及编译器来说,预先知道某个函数不会抛出异常显然大有脾益。首先,知道函数不会抛出异常有助于简化调用该函数的代码;其次如果编译器确认函数不会抛出异常,它就能执行某些特殊的优化操作。C++11新标准中引入noexcept关键字。其关键字是紧跟在参数列表后面,用来标识该函数不会抛出异常。 ```c++ void recoup(int) noexcept; //不会抛出异常 void alloc(int); //可能抛出异常 ``` 带实参的说明 ```c++ void recoup(int) noexcept(true); //recoup不会抛出异常 void alloc(int) noexcept(false); //alloc可能会抛出异常 ```