10 More Effective C++—条款13(catch异常引用的好处)

1 前言

其实本篇文章应与前一篇文章合写,但是,鉴于上一篇文章文字较多,已经基本成稿,因此,关于为什么使用catch reference的讨论,留在本章进行讨论。

同样,本文章将依照书中顺序进行讨论

2 异常指针传递带来的麻烦

上篇文章介绍了使用指针进行异常传递的方法,如下面3中形式。

形式1:一旦离开局部变量的作用域,局部变量就会被销毁,此种方式是错误的。
形式2:无法时刻谨记,同时长期保存一个函数内的局部变量会带来很大空间开销。
形式3:外界需要维护指针,当指针用完后,就需要外界调用delete来销毁,维护成本过高。

void func() {
	Widget error; // 形式1
	static Widget errorStatic; // 形式2
	Widget *errorHeap = new Widget; // 形式3
	
	throw &error; // 形式1
	throw &errorStatic; // 形式2
	throw errorHeap; // 形式3
}
catch (Widget *widget) {
	...
}

3 异常对象传递带来的麻烦

如果直接catch异常对象,同样也存在以下两个问题:

1,问题1:复制两次异常对象。抛出异常对象一次,catch对象时又被复制一次。
2,问题2:不能使用虚函数实现多态,从而产生不同的行为。

void func() {
	Widget widget;
	throw widget; // 第一次复制
}
catch (Widget widget) { // 第二次复制
}

下面例子说明了问题2对应的情况。显然,被抛出的widget只能被catch(exception widget)来接收,而复制以后,就变成了exception对象,因此调用的what()函数也是exception的函数,而不是DerivedException的what()。

class exception {
	public:
		virtual const char *what() throw(); // ”throw()“关键字声明该函数不会抛出任何异常
}
class DerivedException : public exception {
	public:
		virtual const char *what() throw(); // 虚函数实现多态
}
void func() {
	DerivedException widget;
	throw widget;
}
catch (exception widget) {
	widget.what(); // 调用的是exception::what(),这种情况叫做“slicing(切割)——子类信息被切割掉,只留下基类的信息”
}

4 异常引用传递带来的好处

由于上面三个方面的原因,我们选择使用引用方式类捕获异常。如下所示。虽然抛出异常时的复制无法避免,但是catch时采用引用方式,因此可以避免复制异常——总共复制了一次异常对象。同时,引用使得我们可以顺利调用虚函数,实现多态。

void func() {
	DerivedException widget;
	throw widget;
}
catch (exception& widget) {
	widget.what(); // 调用的是DerivedException的what,实现了多态
}

猜你喜欢

转载自blog.csdn.net/zhizifengxiang/article/details/83378424
今日推荐