1. 程式人生 > >10 More Effective C++—條款13(catch異常引用的好處)

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,實現了多型
}