对于一个被throw出的异常,根据调用链一层层向外返回(栈展开)找处理代码,也就是寻找try对应的catch,并将控制权交予这个catch对应的处理代码。当找到最外层也未找到时,直接调用std::terminate()(执行的挺慢的)终止程序。

由于可能提早退出函数,函数中创建的局部存在的对象需要被销毁,而此时折构函数与其他等等会自动被调用,但如果涉及到了构造函数内部或者动态内存,则需要我们手动进行处理,以确保正确的销毁。

但如果折构函数本身抛出了异常,那么后续的销毁就不能正常的进行了,所以程序一般需要保证折构函数内部的异常要在折构函数内部处理完成。

异常对象

实际上,当我们抛出一个异常表达式时,会通过这个异常抛出语句来拷贝初始化一个异常对象,这个异常对象会存在于编译器管理的空间中,在全局都能访问,并在异常被处理后被销毁。

由于要通过这个表达式进行初始化,因此表达式必须是完全类型的,类的话必须拥有可访问的折构函数和拷贝或移动构造函数。而函数和数组将会被转为指针类型。

另外,这个表达式不应该是指向局部的指针。且该表达式拷贝时的类型会根据静态编译时的类型而决定,不会因为指向派生类而变为派生类,所以可能会切掉派生部分。

catch

catch就像一个函数一样,每次抛出异常,就像是调用catch这个函数,而最终选择哪一个catch来处理,则是根据catch的参数和调用时也就是抛出时表达式的类型。其像函数一样可以使左值引用。一切catch中的操作真的就可以完全当catch时一个函数,除了没有右值引用。

当进行“函数匹配”时,只允许三种转换,const,派生与基,数组与函数转为指针。

当catch匹配的类型中存在继承关系,则应该把最派生的类放前面,最基的类放后面。

catch(…)可以捕获所有异常。

重新抛出

有的时候,多个catch会一起用于处理一个错误,如派生类部分处理完交给基类继续处理。此时我们就可以通过throw;来重新抛出,为了能让在这个catch中处理的保留到下面一个catch中,应使用引用。

初始化列表的try

由于初始化列表不是函数体内的东西,所有写这个构造函数的时候不能将其异常处理。

此时可以使用函数try语句块。

$$ returntype\ fun(...)try:初始化列表\{\}catch... $$

noexcept

我们可以使用noexcept来声明函数不会抛出异常,并且,一旦定义就必须在所有声明和定义处声明noexcept,也可以给函数指针声明。