条款8:别让异常逃离析构函数;
我们先来参看如下代码:
class A{
public:
...
~A(){...}
};
void showA(){
std::vector<A> v;
...
}
考虑如下问题,当showA函数调用结束时,vector v这个局部变量需要被销毁,他讲负责销毁所有的A,加入v中有n个A,第一个A调用的时候发生异常,其他n-1个对象还是应该被销毁,如果n-1个对象中继续有对象在析构的时候发生异常,这两个异常同时存在的情况下,程序若不是结束执行就是导致不明确的行为,这显然不是我们想要的。
那么应该怎样解决这种问题呢?举个例子,看看如下代码:
class DatabaseCon{
public:
static DatabaseCon create();
...
void close();
};
class Con{
public:
...
~Con(){
db.close();
}
private:
DatabaseCon db;
};
调用成功,当然是我们所期待的,调用失败呢,很可怕的是Con的析构函数会传播这个异常,即允许异常离开析构函数,造成内存泄露,资源泄露等一系列可怕的问题!
两个方法可以处理该问题:
1)程序抛出异常则直接结束程序,通常通过调用abort完成;
Con::~Con(){
try{db.close();}
catch(...)[
... //制作运转记录,记下对close的调用失败,这样可以将不明确行为提前扼杀掉
std::abort();
}
2)吞下函数抛出的异常;
Con::~Con()
{
try{db.close();}
catch(...){
... //制作运转记录,记下close的调用失败
}
总结如下:
1)析构函数绝对不要吐出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下他们(不传播)或者结束程序;
2)如果客户需要对某个操作函数运行期间抛出的异常作出反隐,那么class至少应该提供一个普通函数执行该操作,而并不是在析构函数中执行该操作。