-
析构函数绝对不能抛出任何异常,如果一个被析构函数调用的函数可能会抛出异常,析构函数应该能够捕捉任何异常,然后吞下他们(不传播),或者结束程序。
-
如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么classe应该提供一个普通函数执行(而非析构函数)该操作
列子:一个负责数据库连接的class
class DBConnection{
public:
...
static DBConnection create();//这个函数返回一个连接
void close(); //关闭连接,失败抛出异常
};
为了防止客户忘记调用close(),可以设计一个如下类,让他在析构函数中自动调用close();
class DBconn{//这个class用来管理DBConnection
public:
...
~DBconn()//确保数据库连接总会关闭
{
db.close();
}
private:
DBConnection db;
};
此时,可以用DBconn来使用DBConnection,
...
{
...
DBConn dbc(DBConnection::create());
...
}//离开作用域,自动释放连接(调用close())
...
如果close()正常关闭,一切完美,如果抛出异常,DBConn析构函数会传播该异常,也就是异常离开了析构函数,那会造成难以驾驭的麻烦;
有两个方法可以避免这个问题:
1. 如果close()抛出异常,就结束程序。通过调用abort()完成
DBConn::~DBConn()
{
try{db.close();}
catch(...){
//制作运转记录,记下对close()的调用失败,
std::abort();
}
}
2. 吞下因调用close而发生的异常:
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;
};