[Effective C++]条款08:别让异常逃离析构函数

  • 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序
  • 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么 class 应该提供一个普通函数(而非在析构函数中)执行该操作

如果在析构函数中必须要执行某个动作,这个动作可能会在失败时抛出异常:

// DBConnection类负责与数据库的连接
class DBConnection {
public:
	static DBConnection create();
	void close();
	//...
};

为了保证在用户在忘记调用 DBConnection 对象的 close() 函数时,数据库的连接也能够正确的关闭,一个合理的想法就是创建一个用来管理 DBConnection 资源的 class DBConn,并在 DBConn 的析构函数中调用 DBConnection 的 close() 函数,大概是这个样子:

//DBConn类负责管理DBConnection对象的连接状态
class DBConn {
public:
	...

	~DBConn()
	{
		db.close();
	}

private:
	DBConnection dbConnection;
};

当使用 DBConn 时,被销毁的时候调用析构函数,但是如果导致异常,DBConn 析构函数会传播该异常,也就是允许它离开这个析构函数。这样会抛出难以驾驭的麻烦。

两个办法可以避免这一问题。DBConn 的析构函数可以:

  • 如果 close 抛出异常就结束程序。通常通过调用 abort 完成:
DBConn::~DBConn()
{
    try {db.close();}
    catch (...) {
        制作运转记录,记下对close的调用失败;
        std::abort();	//阻止异常从析构函数传播出去
    }
}
  • 吞下因调用close而发生的异常:
DBConn::~DBConn()
{
    try {db.close();}
    catch (...) {
        制作运转记录,记下对close的调用失败;
    }
}

上面两种办法都没什么吸引力。因为它们都无法对“导致 close 抛出异常”的情况做出反应。

一个较佳的选择是重新设计 DBConn 接口,专门提供一个 close 函数,赋予客户一个处理异常的机会,如果客户不愿处理,则再按照之前的方式执行:

class DBConn {
public:
    ...
    void close() 	//供客户使用的新函数,客户可以通过检查closed来判断关闭过程中是否出现异常,并能够加以处理
    {
        db.close();
        closed = true;
    }
    ~DBConn()
    {
        if (!closed) {
            try {
                db.close();
            } catch (...) {
                // 制作运转记录,记下对close的调用失败
                ...
            }
        }
    }
private:
    DBConnection db;
    bool closed;
};

如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数。因为析构函数吐出异常就是危险的行为。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值