C++在语法上并不禁止从dtor中引发异常。但这在实践中会构成隐患。如下面:
class Widget { public: ... ~Widget() { ... } // assume this might emit an exception }; void doSomething() { std::vector<Widget> v; ... } // v is automatically destroyed here
在某种条件下同时产生两个异常,会导致程序的提前中止,或者引发未定义行为。上例中,如果v的某两个元素在析构时同时产生异常,会引发未定义行为。这是隐患。
在dtor里使用STL中的容器、TR1中的容器、甚至数组,都可能引发未定义行为;即使不用容器/数组,产生异常时也会导致程序提前中止,或引发未定义行为。
在两种情况下会调用dtor。第一种是正常删除一个对象,例如对象超出了作用域或被显式地delete。第二种是在异常传递的stack-unwinding过程中,由异常处理系统删除一个对象。
在上述两种情况下,调用dtor时异常可能处于激活状态也可能没有处于激活状态。遗憾的是没有办法在dtor内部区分出这两种情况。
因此在写dtor时你必须保守地假设"有"异常被激活,因为如果在一个异常被激活的同时,dtor也抛出异常,并导致程序控制权转移到dtor以外,C++将调用terminate函数,立即中止程序。
结论:C++不喜欢会引发异常的dtor。
但是,有时在dtor中进行的操作确实有引发异常的危险。如关闭数据库:
class DBConnection { public: ... static DBConnection create(); // function to return DBConnection objects; params omitted for simplicity void close(); // close connection; throw an exception if closing fails };
一般为了确保数据库的连接被关闭,都会用“管理器”在dtor里加个“保险”:
class DBConnMgr { // class to manage DBConnection objects public: ... ~DBConnMgr() // make sure database connections are always closed { db.close(); } private: DBConnection db; };
这样,客户在使用时,即使忘记关闭连接,该操作也会自动进行:
{ DBConnMgr dbc(DBConnection::create()); ... // use the DBConnection object via the DBConnMgr interface } // at end of block, the DBConnMgr object is destroyed, thus automatically calling close on the DBConnection object
但是,如果关闭失败,就会引发异常,那么上面在dtor中的“保险”就会把异常传播出去,从而离开了dtor的控制,导致析构不能全部完成。“保险”失效了!
有两种方法控制这种情况。
第一种方法:强行中止程序,以防止未定义行为。
DBConnMgr::~DBConnMgr() { try { db.close(); } catch (...) { // 把该次失败记录到日志 std::abort(); } }
第二种方法:抑制住所有异常。这样,当异常发生时,还可以在dtor里做一些事情,让程序继续运行。
DBConnMgr::~DBConnMgr() { try { db.close(); } catch (...) { // 把该次失败记录到日志 } }
表面上catch没有做任何事情,实际上它阻止了任何异常被传递到dtor外面。无论对象是不是在stack unwinding中被释放,terminate函数都不会被调用。
一个更好的办法:重新设计数据库管理器,让客户主动去处理异常。
class DBConnMgr { public: ... void close() // 给客户使用 { db.close(); closed = true; } ~DBConnMgr() { if (!closed) { // 如果客户忘了,还是加个保险吧 try { db.close(); } catch (...) { // 把该次失败记录到日志 ... // 中止程序或抑制异常 } } } private: DBConnection db; bool closed; };
探讨C++中析构函数引发异常的问题及解决方案,包括强制中止程序和抑制异常的方法。
4320

被折叠的 条评论
为什么被折叠?



