1. 当异常发生时,如果异常发生在一个try块内部,程序就会跳出该try块,并逐层寻找匹配的catch,跳出try块的过程中,会销毁该try内创建的对象并调用析构函数,如果调用析构函数的过程中又发生异常,程序就会调用标准库terminate函数(terminate函数调用abort函数)结束执行,例如:
class A{ public: ... void func(){ ... } ~A{ func(); ... } private: ... }
由于A类的析构函数需要调用func函数,而func函数极有可能抛出异常,这时就需要避免异常逃离析构函数.
2. 方法一:在析构函数内设置try块,一旦发生异常,立即终止程序,例如:
class A{ public: ... void func(){ ... } ~A{ try{ func(); } catch(...){ std::abort(); 制作运转记录,记下对func调用失败 } } private: ... }
这样可以在func函数发生异常时避免异常从析构函数内释放出去.
方法二:吞下func发生的异常,例如:
class A{ public: ... void func(){ ... } ~A{ try{ func(); } catch(...){ 制作运转记录,记下对func调用失败 } } private: ... }
方法三:前两者都无法对"导致func抛出异常"做出什么反应,另一个方法是避免func()在析构函数内执行,由客户来调用func函数,为避免客户忘记执行,需设立flag标记客户是否调用,如果客户没有调用,在析构函数内调用该函数:
class A{ public: ... void func(){ flagg=true; ... } ~A{ if(!flagg){ try{ tmp.func(); } catch(...){ std::abort(); 制作运转记录,记下对func调用失败 } } } private: ... bool flagg; }
这样把调用func的责任从A类的析构函数转移到客户,从而给客户一个“提前处理异常”的机会.
3. 总结:
1). 析构函数绝对不要吐出异常.如果一个被析构函数调用的函数可能吐出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或终止程序.
2). 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作.