C++:异常机制
异常机制简介:
语法:
try{ throw exp; } catch (Exp exp){ ... }
1. 在需要抛出异常的代码段外用try包裹住。
2. 在try内部,需要抛出异常的地方调用 ‘throw + 表达式’,程序不再执行throw后面的代码,直接跳转到catch处,与catch处进行类型匹配,成功则执行catch处代码。
3. catch内部执行完后继续执行catch后续的代码。
异常对象:
通过throw关键字抛出的表达式为一个异常对象。
1. 异常对象在throw后会被复制拷贝一份到一个特殊的区域,即TIB区,为所有catch语句可以访问的空间。
2. 由于异常对象会被拷贝,则异常对象应有共有的拷贝构造函数和析构函数。、
3. 由于会拷贝异常对象的数据类型,所以抛出的对象不能是指针或引用,否则在使用时对象本身已被销毁,跟函数对象类似。
4. 异常对象的析构是在catch语句处理完后析构的。
catch过程:
1. catch的参数如果是引用,则会直接更改异常对象;如果是值传递,则会再次进行拷贝。
2. 在catch匹配异常对象类型的过程中不会做任何隐式的转换。除非以下情况:
a. 非const转const,const转非const。
b. 允许派生类到基类的转换,即允许基类捕获派生类;但不允许派生类捕获基类。
如果用基类的值传递来catch派生类的对象,则会发生派生类的截断。用引用则不会。
3. 在catch匹配时并不是按照最匹配的,而是匹配最先可以匹配到的。
4. 如果在catch中没有处理好异常,可以接着将异常向外部抛出,此时可以直接使用throw即可,不需要加异常对象。
5. catch (...) 可以捕获所有的异常,应该放在所有catch最后。
异常机制与构造析构函数:
1. 在析构函数中不应抛出异常,否则程序将会终止;应该在析构函数内部处理异常。
noexcept关键字
作用:防止异常的传播,提高安全性
c++11 用noexcept关键字修饰函数,表明这个函数不会抛出异常。
这个禁止抛异常的检查是在运行时期,而不是在编译时期。
任然可以在声明noexpect函数内部写throw的语句,只是在运行时会直接中止程序。
异常机制的好处
1. 统一地进行错误处理,由于异常机制会使代码直接跳转到统一的地方,不需要一层层返回函数。
2. 更加灵活丰富的错误信息,错误信息直接是以对象的形式抛出,可以方便的涵盖更多的信息。
3. 使不应该被忽略的逻辑错误通过抛异常强制终止程序,提高了程序的健壮性。
异常机制的坏处
增加了程序的开销:
为了实现代码跳转后,调用堆栈中临时变量的正常析构,需要在每个函数体内记录一些表格,和throw语句所在的偏移量。还有寻找catch匹配的一些信息。
异常与线程
在c++11之前,异常是没有办法跨线程传递的。c++11之后的一些语法特性给跨线程传递异常提供了可能。
C++11之前需要在各个线程内部做catch来捕获异常。
C++11之后:
Future
标准库提供了一些工具来获取异步任务(即在单独的线程中启动的函数)的返回值,并捕捉其所抛出的异常。这些值在共享状态中传递,其中异步任务可以写入其返回值或存储异常,而且可以由持有该引用该共享态的 std::future 或 std::shared_future 实例的线程检验、等待或是操作这个状态。
详见下一篇介绍std::future