条款8: 不要让异常逃离析构函数
举个例子:
class DBConnection { public: ... static DBConnection create(); void close(); };//这个class负责数据库连接。 //为了防止用户忘了close这个数据库连接,很容易想起来定义一个辅助类: class DBCon{ public: .. ~DBCon(){dbc.close();} private: DBConnection dbc; }
close如果调用成功的情况下,是没问题的。但是如果调用导致异常的话,DBConn的析构函数会传播这个异常,导致不可预料的后果。
在析构函数中产生的异常通常按照两种方式来解决:
异常产生就结束:(这样可以防止不明确的行为带来的伤害)
DBConn::~DBConn() { try{
dbc.close();
}catch(...){ std:abort(); } }
或者是吞下这个异常:
DBConn::~DBConn()
{ try{ dbc.close(); }catch(...){ //制作运转记录,表明调用的失败 } }
更好一点的解决方法是给DBConn自己也定义一个close函数,再用户不自己close的情况下在使用上面的做法,这样就给了用户一个处理异常的机会了:
class DBConn{ public: ... void close() { db.close(); close = true; } ~DBConn() { if(close == false){ try{
dbc.close();
}catch(...){ std::abort(); } } } private: bool close; DBConnection dbc; };
小结:析构函数绝对不应该吐出异常,如果一个被析构函数调用的函数可能会抛出异常,析构函数应该捕捉他们然后再吞下他们,像前面的程序做的那样。
还有如果客户应该对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通的函数做这个操作,像上面的close那样。最后的析构函数中再做进一步的亡羊补牢