生命周期 异常在被调函数中触发,不断传向下一级主调函数,直到被捕获
ps
:若异常无法被捕获,程序会调用库函数terminate,由库函数调用abort函数终止程序
关键字 1. throw throw
+ 表达式:抛出异常
ps
:表达式可以是数据,也可以是函数(必须有返回值 )
2. try + catch ps
:catch (...)
表示通用捕获,任何异常均可捕获,但是无法获取异常的值,一般放在所有catch
最后
注:catch (基类异常)
可以捕获派生类异常,若基类异常和派生类异常同存,一般将基类异常的捕获放在后面
1 2 3 4 5 6 7 8 9 10 11 12 try { ... } catch (exception &e){ ... } catch (...){ ... }
实例 除零异常 分析:try
块处理了三个除法(15~17行);第二个除法除数为0,divide
抛出被除数8被捕获并打印异常信息
ps
:try
当中抛出异常后的语句无法执行,对应第三个除法(17行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std;float divide (float x, float y) { if (0 == y) throw x; return x / y; } int main () { try { cout << "5 / 2 = " << divide (5 , 2 ) << endl; cout << "8 / 0 = " << divide (8 , 0 ) << endl; cout << "7 / 1 = " << divide (7 , 1 ) << endl; } catch (float e) { cout << e << " is divided by zero!" << endl; } return 0 ; }
异常接口声明 1 void func () throw (A, B, C, D)
异常处理中的构造与析构 基本原理 从对应的try
块开始到异常被抛出之间构造(且尚未析构)的所有自动对象进行析构
ps
:若异常不被捕获则无法析构
实例 分析:主要观察demo
的析构是否被执行
异常可以被捕获:
demo
在退出try
块之后正常析构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> using namespace std;class MyException { public : MyException (const string &message) : message (message) {} 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 demo; cout << "Throw MyException in func()" << endl; throw MyException ("exception thrown by func()" ); } int main () { try { func (); } catch (MyException &e) { cout << e.getMessage () << endl; } return 0 ; }
正常析构
异常无法被捕获:
库函数terminate调用abort函数终止程序,demo
未被析构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> using namespace std;class MyException { public : MyException (const string &message) : message (message) {} 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 demo; cout << "Throw MyException in func()" << endl; throw MyException ("exception thrown by func()" ); } int main () { try { func (); } catch (int e) { cout << e << endl; } return 0 ; }
无法析构
常见问题
解决方案
在所有catch
最后加一个catch(...)
捕获所有类型的异常
标准程序库的异常类 头文件
异常类汇总 点击查看
常用异常类
exception
:标准程序库异常类的公共基类
logic_error
:表示可以在程序中被预先检测到的异常(这类异常可以避免)
runtime_error
:表示难以被预先检测到的异常
实例 三角形面积计算
判断边长是否合理(为正数且满足三角不等式),不合理抛出invalid_argument
异常;合理则输出三角形面积
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> #include <cmath> #include <stdexcept> using namespace std;double area (double a, double b, double c) throw (invalid_argument) { if (a <= 0 || b <= 0 || c <= 0 ) { throw invalid_argument ("The side length should be positive!" ); } if (a + b <= c || a + c <= b || b + c <= a) { throw invalid_argument ("The side length should fit the triangle inequaltion!" ); } double aver = (a + b + c) / 2 ; return sqrt (aver * (aver - a) * (aver - b) * (aver - c)); } int main () { double a, b, c, res; cout << "请输入三角形边长:" ; cin >> a >> b >> c; try { res = area (a, b, c); } catch (invalid_argument& e) { cout << e.what () << endl; return 0 ; } cout << "三角形的面积为:" << res << endl; return 0 ; }
编写异常安全程序的原则
避免异常引发的资源泄露 常见问题
函数在抛出异常以前没有释放应该由它负责 释放的资源(例如用new
分配的变量是不可以自动析构的,必须使用free
)
解决方案
把一切动态分配的资源都包装成栈上的对象,利用抛掷异常时自动调用对象析构函数的特性来释放资源
对于必须在堆上构造的对象,可以用智能指针auto_ptr
加以包装