effective_别让异常逃离析构函数
发布日期:2021-05-04 06:33:45 浏览次数:36 分类:精选文章

本文共 2168 字,大约阅读时间需要 7 分钟。

条款08:别让异常逃离析构函数

1.析构函数绝对不要吐出异常。如果一个析构函数内调用的函数可能抛出异常,析构函数应该捕捉任何出现的异常,然后选择吞下它们或结束程序,别让异常逃离析构函数。
2.如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中),让客户自己去调用该操作函数,给客户一个处理错误的机会。(见例2)

例1:

class Widget {   public:    //......    ~Widget() {       // 假定这个析构函数可能会吐出异常        //......    }    //......};void doSomething(){       //......    std::vector
v; //......}

当 doSomething 函数执行完毕的时候,会开始析构容器 v 中存放的各个Widget对象。

然而,由于我们假设Widget析构函数可能会吐出异常。在这种情况下,假如v内含10个Widget的对象,当v被销毁时,它有责任销毁内含的所有Widget对象。而在执行第一个Widget对象的析构函数时有个异常被抛出。其它九个Widget对象还是应该被销毁,但执行第二个Widget对象的析构函数时又有个异常被抛出。这对c++而言太多了,在两个异常同时存在的情况下,程序若不是结束执行就是导致不明确的行为。

例2:

如果在对象结束的时候,必须在析构函数中执行一个可能抛出异常的动作,那该怎么办呢?
首先,定义两个类分别负责数据库连接以及该类资源的管理:

// 该类负责数据库的连接class DBConnection {   public:    //......    static DBConnection create();    // 静态成员函数,建立一个DBConnection对象    //......    void close();    //......};// 该类负责类DBConnection的资源管理class DBConn {   public:    //......    ~DBConn() {       // 析构函数确保数据库连接总是会被关闭        db.close();    }    //......private:    //......    DBConnection db;    //......};

这样,客户可以写出如下代码以管理数据库连接(代码很好看懂):

{        //......     DBConn dbc(DBConnection::create());     //......}

该段代码结束时,会自动调用~DBConn(),如果close调用成功,那最好,如果close调用失败,那么DBConn析构函数就会传播该异常,即允许异常离开这个析构函数,会造成问题。

两个办法可以避免这个问题:1.如果close抛出异常就结束程序,通常通过调用abort完成。2.吞下close抛出的异常。
solution1.

DBConn::~DBConn() {   	try {   		db.close();	}	catch (...) {   		//记下对close的调用失败		std::abort();	}}

solution2.

DBConn::~DBConn() {   	try {   		db.close();	}	catch (...) {   		//记下对close的调用失败	}}

更好的方法是将 close的执行移交给用户,由客户自己调用close,然后再在析构函数中try and catch,代码如下:

// 修改后的DBConn类实现class DBConn {   public:    //......    void close() {       // 要求用户自己关闭数据库对象        db.close();        closed = true;    }    //......    ~DBConn() {           if (!closed) {       // 如果用户忘记了这么做,就采用 try catch 机制吞下异常。            try {                   db.close();            }            catch (...) {                   // 记录此次 close 失败                //......            }        }    }    //......private:    //......    DBConnection db;    bool closed;    // 增设此变量用以判断用户是否已经自行调用 close(),用户也可根据此变量判断 close() 是否顺利执行并作出相应的异常处理。    //......};

这里DBConn析构函数则起到一个双保险的作用。

上一篇:派生类的构造函数
下一篇:避免异常发生时的堆内存泄漏——智能指针auto_ptr

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2025年04月10日 13时41分34秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章