Effective-cpp-#8

Prevent exceptions from leaving destructions

动机

  • 一种情况下,如果析构函数抛出了异常,则该异常点后面的程序将无法执行,而如果这些对象不能正常释放,则有可能造成内存泄漏的情况;
  • 另一种情况则是,假设前面有异常被抛出,而后面的对象仍在执行正常的销毁,而此时后面的对象也抛出了异常,在两个异常同时存在的情况下,会导致不明确行为,程序崩溃。

方法

如果析构函数不得已执行一个有可能抛出异常的动作,那可以用两个方法解决:

  • 让析构函数自行处理
1
2
3
4
5
6
7
DBConn::~DBConn()
{
try{db.close();}
catch(...){
....//处理掉异常,并做记录
}
}
  • 直接结束程序
1
2
3
4
5
6
7
8
DBConn::~DBConn()
{
try{db.close();}
catch(...){
....//处理掉异常,并做记录
std::abort();
}
}

有时候,析构函数处理掉异常不是一件好事,因为我们不知道“某些行为失败了”;但有时为了让程序忽略掉错误继续运行,这又是一件好事。

  • 也可以重新设计接口,让客户自身去处理异常,比如上面代码的close()函数;
1
2
3
4
5
6
7
8
9
10
11
12
class DBConn{
public:
void close(){
db.close();
closed = true;
}
~DBCconn(){
if (!closed){
try....
}
}
}

建议

  • 析构函数不能抛出异常。如果实在要抛出异常,可以选择让析构函数捕捉该异常,并使其限制在析构函数内或者直接结束函数;
  • 如果客户需要对某个操作函数的异常做出反应,我们应该为其提供一个普通函数,而不是在析构函数中执行该操作;