【Effective C++】不要让异常逃离析构函数

首先先抛出结论:

  • 析构函数绝对不要抛出异常。如果在一个析构函数中某个函数可能调用失败,抛出异常,这个析构函数应该捕捉这个异常,然后“吞下”他们,或者结束程序。
  • 如果客户端需要对某个操作函数运行期间抛出的异常做出反应,那么这个 class 应该提供一个普通函数接口,而不是在析构函数中自己处理异常。


    假如有以下的代码片段:
class Widget {
public:
	...
	~Widget() { }	//假设抛出异常
};

void DoSomething()
{
	std::vector<Widget> v;
	...
}

当 vector 容器被销毁的时候,它是有责任销毁它内含的所有的 Widget 对象的。但是假如,v 在析构第一个 Widget 对象的时候,Widget 对象的析构函数抛出异常,但是其他的 Widget 对象仍然需要正常被销毁。如果多个 Widget 对象都抛出异常,这个时候就会造成程序不是结束执行就是会导致不明确行为。

那如果在析构函数中必须要执行某个动作,但是这个动作又可能会在失败的时候抛出异常呢?比如说下面这一种情况:
有一个 DBConnection 类负责与数据库的连接:

class DBConnection {
public:
	static DBConnection create();
	void close();
	//...
};

然后又一个 DBConnectionManager 类负责管理 DBConnection 对象的连接状态,为了保证在用户在忘记调用 DBConnection 对象的 close() 函数的时候,数据库的连接也能够正确的关闭,一个合理的想法就是在 DBConnectionManager 类的析构函数中调用 DBConnection 的 close() 函数,大概是这个样子:

class DBConnectionManager {
public:
	DBConnectionManager(DBConnection db)
	{
		dbConnection = db;
	}

	~DBConnectionManager()
	{
		dbConnection.close();
	}

private:
	DBConnection dbConnection;
};

这个时候就会存在一个问题:如果 DBConnection 的 close 函数抛出异常,DBConnectionManager 的析构函数就会传播这个异常,也就是会允许程序离开这个析构函数,这个时候就会造成潜在的问题。


有两个办法可以避免这个问题:
第一个就是让这个析构函数把异常给“吞掉”,但是有一个前提条件就是在这个析构函数把异常“吞掉”之后也要保证程序能够继续可靠的执行。

~DBConnectionManager()
{
	try {
		dbConnection.close();
	}
	catch (std::exception)
	{
		//记录调用失败
	}
}

第二个解决办法就是 在抛出异常的时候就强行结束程序,阻止异常从析构函数中传播出去,产生不明确行为
~DBConnectionManager()
{
	try {
		dbConnection.close();
	}
	catch (std::exception)
	{
		//记录调用失败
		std::abort();
	}
}

前两者的解决方案都会导致客户端无法对“导致 close 抛出异常” 的情况做出反应,所以一个更好的解决方案是重新设计 DBConnectionManager 的接口,使得客户端能够对 close 抛出的异常做出反应,也就是提供客户端手动调用 close 函数的接口,但是为防止客户端忘记手动关闭,又会在其析构函数中调用 close 函数。这样的话,即使真的有错误发生,也是客户端“活该”。(都给你接口了让你手动调用关闭,谁让你忘记的,就不要怪我自己处理的时候出错咯)
class DBConnectionManager {
public:
	DBConnectionManager(DBConnection db)
	{
		dbConnection = db;
	}

	void closeConnection()
	{
		dbConnection.close();
		hasClosed = true;
	}

	~DBConnectionManager()
	{
		if (!hasClosed)
		{
			try {

				dbConnection.close();
			}
			catch (std::exception)
			{
				//记录调用失败
			}
		}
	}

private:
	DBConnection dbConnection;
	bool hasClosed;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值