c++编程习惯七(不要让析构函数抛出异常)

c++之中,异常处理是允许析构函数抛出异常的,但是,并不鼓励那样做,考虑一下这种情况:

class Student{
    ...
    ~Student();//在这里吐出一个异常
};

void dosomething()
{
    std::vector<Student> ss;
    ...
}

当容器ss被销毁时,会销毁其内所含有的Student。假设ss内存了十个,在第一个Student析构时抛出异常,那这就很尴尬了,其他的九个就不能被销毁了,这时候就会出现不明确行为。所以c++不喜欢析构函数吐出异常。这是很容易理解的。

 

但是如果你的析构函数必须执行一个动作,而该动作可能会在失败时抛出异常,该怎么办?

假设使用一个class负责数据库连接:

class Dbconnection{
public:
	...
	static Dbconnection create();//这个函数返回Dbconnection对象
	void close();

}

为了确保客户不忘记调用close();一个合理的想法是创建一个用来管理资源的class,然后在析构函数中调用close:

class Dbconn{//用来管理Dbconnection对象
public:
	...
	~Dbconn()//确保数据库连接总是会被关闭
	{
		db.close();
	}
private:
	Dbconnection db;
}

这样就好啦,只要close正常调用,一切都很好,但是如果该调用导致异常,这时就很麻烦了,在析构函数出了异常。

 

我们有两个方法可以避免这一问题:

1、如果close抛出异常就结束程序,通常通过调用abort完成:

Dbconn::~Dbconn()
{
	try{db.close();}
	catch(...)
	{
		//制作运转记录,记录下对close的调用失败;
		std::abort();
	}
}

如果程序遭遇了一个在析构期间发生的错误后无法继续运行,那么强迫程序结束是一个合理的选项,毕竟可以组织异常从析构函数传播出去(会出现不明确行为),也就是说调用abort可以抢先置“不明确行为”于死地。

 

2、吞下因调用close而发生的异常:

Dbconn::~Dbconn()
{
	try{db.close();}
	catch(...)
	{
		//制作运转记录,记录下对close的调用失败;
		
	}
}

一般来说,吞掉异常是一个不得已的坏主意,因为它压制了“某些动作失败”的重要信息,但是要比出现不明确行为好得多!

这两种方法都无法对“导致close抛出异常”的情况做出反应。

有一个比较好的方法就是重新设计Dbconn的接口,让客户有机会对可能出现的问题做出反应。例如Dbconn自己可以提供一个close函数,因而赋予客户一个机会得以处理“因该操作而发生的异常”。Dbconn也可以追踪其所管理的Dbconnection是否已被关闭,并在答案为否的情况下由其析构函数关闭。但是如果Dbconnection析构函数调用close失败,我们又将回到之前的情况:

class Dbconn
{
public:
	...
	void close()
	{
		db.close();
		closed=true;
	}
	~Dbconn()
	{
		if(!closed)
		{
			try{
				db.close();
			}
			catch(...)
			{
				...
			}
		}
	}
private:
	Dbconnection db;
	bool closed;
	
};

把调用close的责任从Dbconn析构函数手上给客户,有点甩锅的意思,其实并不是这样的,因为如果某个操作可能在失败时抛出异常,而又是存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数。因为析构函数吐出异常就是危险,总会带来“过早结束程序”或“发生不明确行为”的风险。

 

总结:

1、析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或者结束程序;

2、如果客户需求对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值