美文网首页
effective C++ 笔记:条款08 别让异常逃离析构函数

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

作者: jun_hinokeso | 来源:发表于2018-08-14 22:18 被阅读0次

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

    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(),那么处理异常和析构对象的目的都达到了;如果忘记,那么也起码能保障对象正确析构(起码让最麻烦的事情不会发生)。

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/oasbbftx.html