10 More Effective C++—條款13(catch異常引用的好處)
阿新 • • 發佈:2018-12-26
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,實現了多型
}