条款8 别让异常逃离析构函数
析构函数绝对不能吐出异常
C++不禁止析构函数抛出异常。
例如一个负责连接数据库的class
class DBConnection
{
public:
...
static DBConnection create();
void close(); //关闭连接 失败则会抛出异常
}
为了确保使用者不忘记在DBConnection上调用close,可以在析构函数中调用。设计一个资源管理的class。
class DBConn
{
public:
...
~DBConn() //确保数据库关闭了
{
db.close();
}
private:
DBConnection db;
}
所以可以允许这样使用。
DBConn dbc(DBConnection::create());
但是如果close调用出现了异常,DBConn的析构函数则会传播这个异常。
有两个方法可以避免这个问题。
- 若close抛出异常就结束程序。
DBConn::~DBConn()
{
try{ db.close(); }
catch(...)
{
//记录异常
std::abort(); //直接终止程序运行
}
}
- 或者直接吞掉异常
DBConn::~DBConn()
{
try{ db.close(); }
catch(...)
{
//记录异常
}
}
异常处理应该在普通函数中
一个比较好好的设计,就是重新设计DB接口,让客户有机会对可能出现的问题作出反应。
class DBConn
{
public:
...
void close()
{
db.close();
closed = true;
}
~DBConn()
{
if(!closed)
{
try{
db.close();
}
catch(...)
{
//制作运行记录,记录close调用失败
}
}
}
private:
DBConnection db;
bool closed;
};
这样把close的任务交给了客户,析构函数只是一个双保险。