在析构函数里抛出异常是很麻烦的,可能导致内存泄漏。
那么如果必须在析构函数里执行某一操作,并且这个操作可能会抛出异常。举个例子。
class DBConnection {
public:
static DBConnection create(); //返回数据库连接对象
void close(); //关闭连接:失败则抛出异常
};
这是一个数据库连接类,为了防止用户忘记调用close(),一般的做法是创建一个用来管理DBConnection资源的类,并在其析构函数中调用close():
class DBConn {
public:
~DBConn() {
db.close();
}
private:
DBConnection db;
};
在这种情况下如果调用析构函数中的close()报错,那么很可能导致内存泄漏。解决方法如下:
1)
DBConn::~DBConn(){
try{ db.close(); }
catch(...){
std::abort();
}
}
这种做法是一旦close()出现异常立刻停止close()以确保析构函数的正常运行。
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(...){
...
}
}
}
private:
DBConnection db;
bool closed;
};
将调用close()的责任交给用户,用户可以主动调用close(),这样在析构函数内就不会再次调用close(),也就不会因为发生了异常而导致的析构失败。
你可能会问,这也算解决方法吗?
实际上这种方法的思想是,将避免以上问题的方法教给用户,用户只要认真对待并照做,那么万事大吉。假如用户真的忘记调用close(),那么析构函数里的代码成了“第二个保险”,但是是牺牲了close()异常处理来保障对象正确析构的方法。
总的来说就是,只要用户记得调用close(),那么处理异常和析构对象的目的都达到了;如果忘记,那么也起码能保障对象正确析构(起码让最麻烦的事情不会发生)。
网友评论