# 异常
- 异常是程序运行过程中出现问题
- "异常" 并不经常出现
- 处理异常使程序像没有发生过问题一样继续执行
# 何时使用异常处理
- 异常处理 用于处理同步错误 (语句执行时发生的错误)
- 常见的异常处理错误:
数组下标越界,算法溢出,被零除,函数参数不合法,内存不够引起的内存分配不成功
磁盘读写结束,网络信息到达,点击鼠标或键盘,这些与程序的控制流程并行,独立
# 异常处理的基本思想
# 异常处理的语法
| try |
| 复合语句(保护段) |
| catch (异常声明) |
| 复合语句(异常处理程序) |
| catch (异常声明) |
| 复合语句 |
| ... |
- 若有异常则通过
throw
创建一个异常对象并抛掷 - 将可能抛出异常的程序段嵌在
try
块之中。通过正常的顺序执行到达 try
语句,然后执行 try 块内的保护段 - 如果在保护段执行期间没有引起异常,那么跟在 try 块后的 catch 子句就不执行。程序从 try 块后的最后一个 catch 子句后面的语句继续执行
- catch 子句按其在 try 块后出现的顺序被检查。匹配的 catch 子句将被捕获并处理异常 (或继续抛掷异常)
- 如果匹配的处理器未找到,则库函数
terminate
将被自动调用,其默认是调用 abort终止程序
| |
| 处理除零异常 |
| */ |
| #include <iostream> |
| using namespace std; |
| |
| int divide(int x, int y) |
| { |
| if (y == 0) |
| throw x; |
| return x / y; |
| } |
| int main(void) |
| { |
| try |
| { |
| cout << "5 / 2 = " << divide(5, 2) << endl; |
| cout << "8 / 0 = " << divide(8, 0) << endl; |
| cout << "7 / 1 = " << divide(7, 1) << endl; |
| } |
| catch (int e) |
| { |
| cout << e << " is divided by zero !" << endl; |
| } |
| cout << "That is ok." << endl; |
| return 0; |
| } |
| |
| 5 / 2 = 2 |
| 8 is divided by zero ! |
| That is ok. |
| */ |
| |
| 带析构语句的类的 C++ 异常处理 |
| */ |
| #include <iostream> |
| #include <string> |
| using namespace std; |
| class MyException |
| { |
| public: |
| MyException(const string& message) :message(message) |
| { |
| |
| } |
| ~ MyException() {} |
| const string& getMessage() const |
| { |
| return message; |
| } |
| private: |
| string message; |
| }; |
| class Demo |
| { |
| public: |
| Demo() |
| { |
| cout << "Constructor of Demo" << endl; |
| } |
| ~Demo() |
| { |
| cout << "Destructor of Demo" << endl; |
| } |
| }; |
| void func() throw(MyException) |
| { |
| Demo d; |
| cout << "Throw MyException in func() " << endl; |
| throw MyException("exception thrown by func()"); |
| } |
| int main(void) |
| { |
| cout << "In main function" << endl; |
| try |
| { |
| func(); |
| } |
| catch (MyException& e) |
| { |
| cout << "Caught an exception: " << e.getMessage() << endl; |
| } |
| cout << "Resume the exception of main()" << endl; |
| return 0; |
| } |
| |
| In main function |
| Constructor of Demo |
| Throw MyException in func() |
| Destructor of Demo |
| Caught an exception: exception thrown by func() |
| Resume the exception of main() |
| */ |
# 异常接口声明
异常声明 (异常规范, Exception specifications
) 一个函数显式声明可能抛出的异常,有利于函数的调用者为异常处理做好准备
- 可以在函数的声明中列出这个哈数可能抛掷的所有异常类型
| void fun() throw(A,B,C,D); |
- 若无异常接口声明,则此函数可以抛掷
任何类型
的异常 - 不抛掷任何异常的函数声明如下
# noexcept
异常说明
- 对明确
不会
抛出异常的函数使用 noexcept
说明修饰 - 声明方式:
| 返回值类型 func(形参列表) noexcept; |
- 异常处理使编译和运行时有
额外开销
,省去异常处理可优化 加速调用
- 需保持该函数内部调用函数和定义语句均不会抛出异常的一致性
- noexcept 运算符,可判断函数是否使用了 noexcept 说明
| void f() noexcept; |
| noexcept(f()); |
# 慎用异常声明的情况
- 对于带类型参数的函数模版,要尽量避免使用 exception specifications, 因为不同类型对于相同行为的定义不同,抛出的异常也就不同,因而函数模版很难或不可能确定它具现代的函数实体所可能抛出的异常
- 使用回调 (callback) 函数时
- 系统可能抛出的异常
# 异常处理中的构造与析构
# 自动的析构
- 找到一个匹配的 catch 异常处理后
- 初始化异常参数
- 将从对应的 try 块开始到异常被抛掷之间构造 (且尚未析构) 的所有自动对象进行析构
- 从最后一个 catch 处理之后开始恢复执行
# 标准程序库继承关系
# C++ 标准库各种异常类所代表的异常
异常类 | 头文件 | 异常的含义 |
---|
bad_alloc | exception | 用 new 动态分配空间失败 |
bad_cast | new | 执行 dynamic_cast 失败(dynamic_cast 参见 8.7.2 节) |
bad_typeid | typeinfo | 对某个空指针 p 执行 typeid (*p)(typeid 参见 8.7.2 节) |
bad_exception | typeinfo | 当某个函数 fun () 因在执行过程中抛出了异常声明所不允许的异常而调用 unexpected () 函数时,若 unexpected () 函数又一次抛出了 fun () 的异常声明所不允许的异常,且 fun () 的异常声明列表中有 bad_exception,则会有一个 bad_exception 异常在 fun () 的调用点被抛出 |
ios_base::failure | ios | 用来表示 C++ 的输入输出流执行过程中发生的错误 |
underflow_error | stdexcept | 算术运算时向下溢出 |
overflow_error | stdexcept | 算术运算时向上溢出 |
range_error | stdexcept | 内部计算时发生作用域的错误 |
out_of_range | stdexcept | 表示一个参数值不在允许的范围之内 |
length_error | stdexcept | 尝试创建一个长度超过最大允许值的对象 |
invalid_argument | stdexcept | 表示向函数传入无效参数 |
domain_error | stdexcept | 执行一段程序所需要的先决条件不满足 |
# 标准异常类的基础
exception
:标准程序库异常类的 公共基类
logic_error
:表示可以在程序中被预先检测到的异常runtime_error
: 表示 难以
被预先检测的异常
| |
| 计算三角形的面积 |
| */ |
| #include <iostream> |
| #include <cmath> |
| #include <stdexcept> |
| using namespace std; |
| |
| double area(double a, double b, double c) |
| { |
| |
| if (a <= 0 || b <= 0 || c <= 0) |
| { |
| throw invalid_argument("the side length should be positive"); |
| } |
| |
| if (a + b <= c || b + c <= a || c + a <= b) |
| { |
| throw invalid_argument("the side length should fit the triangle inequation"); |
| } |
| |
| double s = (a + b + c) / 2; |
| return sqrt(s * (s - a) * (s - b) * (s - c)); |
| } |
| int main(void) |
| { |
| double a, b, c; |
| cout << "Please input the side lengths of a triangle:"; |
| cin >> a >> b >> c; |
| try |
| { |
| double s = area(a, b, c); |
| cout << "Area:" << s << endl; |
| } |
| catch (exception& e) |
| { |
| cout << "Error:" << e.what() << endl; |
| } |
| return 0; |
| } |