effective_别让异常逃离析构函数

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

例1:

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

void doSomething()
{
    //......
    std::vector<Widget> 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析构函数则起到一个双保险的作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值