异常
异常的概念及应用
异常的概念
异常是程序运行时出现不符预期的反常情况,有时由于异常的广泛性我们无法考虑到所有的异常情况因此处理异常(不然怎么叫作"异常"?),所以我们是尽量在可检测原因的范围内进行捕获与抛出,所以异常的检测只是初步检测真正的异常原因常常需要亲自去查找。
异常的抛出与捕获
例:在try内检测可能异常抛出(throw)的对象,接收(catch)对象+输出异常原因
//除法的函数定义 double Divide(int a, int b) { try { // 当b == 0时抛出异常 if (b == 0) { string s("Divide by zero condition!"); throw s; } else { return ((double)a / (double)b); } } catch (string errid)//异常的值会被存储到 errid变量中,供 catch 块内部使用 { cout << errid << endl; } return 0; }注:throw不一定在try内,抛出throw的本质是为了寻找匹配的catch语句。
异常的接收规则:
接收被抛出对象时,其匹配类型匹配且离抛出位置最近的那个接收(catch)对象(可能在同一作用域也可能在不同作用域)。
抛出(throw)后(抛出的是对象的拷贝资源)直接跳转到catch语句,因此throw后的语句不会再执行,若出函数则相应函数栈帧销毁。在开发时要额外注意此点。
抛出对象的拷贝资源在catch语句结束后会销毁。
栈展开
抛出异常后,程序暂停当前函数的执行,开始寻找与之匹配的catch语句,首先检查throw本身是否在try内若不在且位于被外界调用函数内,那么跳转出当前函数继续寻找……如果到达main()函数依旧没有找到那么调用库内terminal函数来抛出异常:
由此库内函数的调用机制可以看出这种异常抛出是脱离于操控范围内、极其危险的程序性错误。
catch的全捕捉
因此catch提供catch(...)全捕捉函数来一定捕获程序出现的所有异常:
catch (...) { cout << "未知异常,请及时查找!"; }catch匹配的宽泛型处理代码
what()是异常抛出的库内函数处理,检测调用对象的异常情况并抛出。
1.允许非常量向常量类型转换(权限缩小)。
2.允许数组转换为数组的指针,函数被转换为函数指针。
※3.允许派生类向基类的转换。
class Base { public: virtual void what() const { cout << "Base exception" << endl; } }; // Base衍生的 class Derived : public Base { public: void what() const override { cout << "Derived exception" << endl; } }; int main() { try { Derived d; if(…………) throw d; } catch (const Base& b)//隐式转换为基类对象 { cout << b.what(); //或者: b.what(); } return 0; }异常重新抛出
"重新抛出"的性质是再次重复调用尝试解决异常:(如:网络波动、手机信号)
下面代码展示:发送信息失败后重复三次再不成功抛异常
void _SeedMsg(const string & s) { if (……) { //内部抛异常 throw HttpException("⽹络不稳定,发送失败", 102, "put"); } else if (……) { //内部抛异常 throw HttpException("你已经不是对象的好友,发送失败", 103, "put"); } else { cout << "发送成功" << endl; } } void SendMsg(const string & s) { // 发送消息失败,则再重试3次 for (size_t i = 0; i < 4; i++) { try { _SeedMsg(s); break; }//外部catch catch (const Exception& e) { // 捕获异常,if中是102号错误,⽹络不稳定,则重新发送 // 捕获异常,else中不是102号错误,则将异常重新抛出 if (e.getid() == 102) { // 重试三次以后否失败了,则说明⽹络太差了,重新抛出异常 if (i == 3) throw; cout << "开始第" << i + 1 << "重试" << endl; } else { throw; } } } }异常规范(noexcept)
在函数后加"noexcept"明确表示此函数不会抛异常不用进行try内检测。
noexcept的不规范使用:
引发原因:编译器不会再编译期间检查noexcept,也就是说如果一个声明了noexcept的函数内部使用throw抛出异常经编译器不会catch寻找会直接调用terminate库函数终止程序。
noexcept(expression)可作为一个运算符检测一个表达式是否可能会抛出异常,可能返回true,不可能返回false。