条款08:别让异常逃离析构函数
问题:如果一个类的析构函数必须执行一个动作,而该动作可能会在失败时抛出异常,该怎么办?举个例子,假设使用过一个class负责数据库连接:
class DBConnection { public: ... static DBConnection create(); //此函数返回DBConnection对象 void close(); //关闭联机;失败则抛出异常 };
一个较佳策略是创建一个用来管理DBConnection资源的DBConn类,DBConn类自己提供一个close函数,因而赋予客户一个机会得益处理“因该操作而发生的异常”。DBConn也可以追踪其所管理的DBConnection是否已被关闭,若没有被关闭,则由DBConn的析构函数关闭它。这可防止遗失数据连接。然而如果DBConnection析构函数调用close失败,则可使用“强制结束程序”或“吞下异常”的方法:
class DBConn { public: DBConn(); ~DBConn(); void close(); private: DBConnection db; bool closed; }; DBConn::DBConn() { } DBConn::~DBConn() { if(!closed) { try { db.close(); //关闭连接 } catch(...) //如果关闭动作失败 { 写日志,记下对close的调用失败; //记录下来并结束程序 ... //或者吞下异常; } } } void DBConn::close() //供客户使用的新函数 { db.close(); closed = true; }
如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数。因为析构函数抛出异常及时危险,总会带来“过早结束程序”或“发生不明确行为”的风险。
请牢记:
1、析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获异常,然后吞下它们或结束程序。
2、如果客户需要对某个操作函数进行运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行操作。