effective C++ 笔记:条款08 别让异常逃离析构函数

2018-08-14  本文已影响0人  jun_hinokeso

在析构函数里抛出异常是很麻烦的,可能导致内存泄漏。
那么如果必须在析构函数里执行某一操作,并且这个操作可能会抛出异常。举个例子。

class DBConnection {
public:
    static DBConnection create(); //返回数据库连接对象

    void close();      //关闭连接:失败则抛出异常
};

这是一个数据库连接类,为了防止用户忘记调用close(),一般的做法是创建一个用来管理DBConnection资源的类,并在其析构函数中调用close():

class DBConn {
public:
    ~DBConn() {
        db.close();
    }
private:
    DBConnection db;
};

在这种情况下如果调用析构函数中的close()报错,那么很可能导致内存泄漏。解决方法如下:
1)

DBConn::~DBConn(){
    try{ db.close(); }
    catch(...){
        std::abort();
    }
}

这种做法是一旦close()出现异常立刻停止close()以确保析构函数的正常运行。
2)

DBConn::~DBConn(){
    try{ db.close(); }
    catch(...){
    }
}

这种方法可以说是忽略close()执行的异常(最多是记录),并没有针对异常而做的操作,一般来说,这种结果总比析构失败导致的内存泄漏要好。

其实以上做法并不是那么理想,因为都以牺牲异常处理来保证正常的析构。
现在有一个比较好的策略,就是重新设计DBConn。

class DBConn {
public:
    void close(){
        db.close();
        closed = true;
    }
    ~DBConn() {
        if(!closed){
            try{
                db.close();
            }
            catch(...){
                ...
            }
        }       
    }
private:
    DBConnection db;
    bool closed;
};

将调用close()的责任交给用户,用户可以主动调用close(),这样在析构函数内就不会再次调用close(),也就不会因为发生了异常而导致的析构失败。
你可能会问,这也算解决方法吗?
实际上这种方法的思想是,将避免以上问题的方法教给用户,用户只要认真对待并照做,那么万事大吉。假如用户真的忘记调用close(),那么析构函数里的代码成了“第二个保险”,但是是牺牲了close()异常处理来保障对象正确析构的方法。
总的来说就是,只要用户记得调用close(),那么处理异常和析构对象的目的都达到了;如果忘记,那么也起码能保障对象正确析构(起码让最麻烦的事情不会发生)。

上一篇下一篇

猜你喜欢

热点阅读