(一)先看下面的代码:
class Widget {
public:
...;
~Widget();
};
void doSomething() {
vector<Widget> v;
}
如果析构函数会抛出异常,当doSomething函数结束的时候,会调用n次Widget的析构函数,每次都抛出异常的话,程序会过早结束或出现不明确的行为!
也就是说:当我们的析构函数必须执行一个动作!而该动作可能会在失败时抛出异常。
所以必须要解决!再看下面的代码:
<pre class="cpp" name="code">class DBConnection {
public:
...;
static DBConnection create();
void close();
};
class DBConn {
public:
~DBConn() {
db.close();
}
private:
DBConnection db;
};
DBConn dbc(DBConnection::creat());
如果dbc对象被析构的时候,db.close();发出异常的话,那就会造成麻烦!有两种解决办法,第一种是抛出异常的时候利用abort()来强迫结束程序!第二种是吞下因调用close()而发出的异常!但是这两种方法都不是很好的方法!
//第一种方法:
DBCon::~DBCon() {
try{
db.close();
}catch(...) {
制作运转记录,记下调用异常
std::abort();
}
};
//第二种方法:
DBCon::~DBCon() {
try{
db.close();
}catch(...) {
制作运转记录,记下调用异常
}
};
但是我们有更好的方法来解决这个问题。因为当析构函数抛出异常时,时间确实太晚了,我们希望尽量早的发现异常,这样及早发现问题,让我们有时间来解决。一个较佳的策略是重新设计DBCon借口,提供一个接口让用户可以先手动的去调用可能发生异常的函数,具体代码如下:
<pre class="cpp" name="code">class DBConnection {
public:
...;
static DBConnection create();
void close();
};
class DBConn {
public:
void close() { //设置给用户的函数!用户必须主动调用,虽然客户不一定要来调用这个函数,但是至少我们给了他这样的机会
db.close();
closed = true;
}
~DBConn() {
if(!closed) {
try {
db.close();
}
catch(...) {
制作运转记录,记下对close的调用失败;
}
}
}
private:
DBConnection db;
bool closed;
};
DBConn dbc(DBConnection::creat());
把调用close的责任从DBCon析构函数的手上转移到了用户手中,这样就给客户及早的发现异常提供了机会,虽然客户不一定要去调用,但是至少我们给了他这样的机会。
请记住:
(1)析构函数绝对不要吐出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下他们(不传播)或结束程序。
(2)如果客户需要某个操作函数运行期间抛出异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。