C++不反对但也不鼓励在析构函数中吐出异常。
假设有一个负责连接数据库资源的class:
class DBConnection {
public:
...
static DBConnection create();
void close();
};
然后有一个负责DBConnection资源管理的class:
class DBConn { public: ... ~DBConn() { db.close(); } private: DBConnection db; };
然后有这样一句调用情况:
DBConn dbc(DBConnection::create()); //开启一个区块,建立 //DBConnection对象,并 //交给DBConn对象管理。 //通过DBConn接口使用 //DBConnection对象。在区块 //结束点,DBConn对象被销毁 //自动调用DBConnection对象 //调用close()
只要close调用成功,那就没问题。但是如果调用失败,DBConn函数就会抛出异常,造成问题。形成不明确的操作。
有两个不是很好的方法可以避免该问题:
1.在出现异常的是立马结束程序:
DBConn::~DBConn() { try{db.close();} catch(...) { //记录,这里出现异常拉!!!close()调用失败拉!!! std::abort(); //掐死程序 } }
2.忍气吞声,吞下异常
DBConn::~DBConn() { try { db.close(); } catch(...) { 记录,这里出现异常拉!!!close()调用失败拉!!! } }
一个比较好的方法是重新设计DBConn的接口,使客户机会对可能出现的问题作出反应:
class DBConn{ public: ... void close() { db.close(); closed = true; } ~DBConn() { if(!closed) { try { db.close(); } catch { 记录,这里出现异常拉!!!close()调用失败拉!!! } } } private: DBConnection db; bool closed; };
DBConn自己提供了一个close函数,所以客户有一个机会得以处理”因该操作而发生异常“。并且追踪管理之DBConnection是否已被关闭。并在答案为否的情况下由析构函数关闭。
析构函数绝对不要吐出异常,如果一个析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们或者结束程序。
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。