C++在语法上并不禁止从dtor中引发异常。但这在实践中会构成隐患。如下面:
在某种条件下同时产生两个异常,会导致程序的提前中止,或者引发未定义行为。上例中,如果v的某两个元素在析构时同时产生异常,会引发未定义行为。这是隐患。
在dtor里使用STL中的容器、TR1中的容器、甚至数组,都可能引发未定义行为;即使不用容器/数组,产生异常时也会导致程序提前中止,或引发未定义行为。
在两种情况下会调用dtor。第一种是正常删除一个对象,例如对象超出了作用域或被显式地delete。第二种是在异常传递的stack-unwinding过程中,由异常处理系统删除一个对象。
在上述两种情况下,调用dtor时异常可能处于激活状态也可能没有处于激活状态。遗憾的是没有办法在dtor内部区分出这两种情况。
因此在写dtor时你必须保守地假设"有"异常被激活,因为如果在一个异常被激活的同时,dtor也抛出异常,并导致程序控制权转移到dtor以外,C++将调用terminate函数,立即中止程序。
结论:C++不喜欢会引发异常的dtor。
但是,有时在dtor中进行的操作确实有引发异常的危险。如关闭数据库:
一般为了确保数据库的连接被关闭,都会用“管理器”在dtor里加个“保险”:
这样,客户在使用时,即使忘记关闭连接,该操作也会自动进行:
但是,如果关闭失败,就会引发异常,那么上面在dtor中的“保险”就会把异常传播出去,从而离开了dtor的控制,导致析构不能全部完成。“保险”失效了!
有两种方法控制这种情况。
第一种方法:强行中止程序,以防止未定义行为。
第二种方法:抑制住所有异常。这样,当异常发生时,还可以在dtor里做一些事情,让程序继续运行。
表面上catch没有做任何事情,实际上它阻止了任何异常被传递到dtor外面。无论对象是不是在stack unwinding中被释放,terminate函数都不会被调用。
一个更好的办法:重新设计数据库管理器,让客户主动去处理异常。