MoreeffectiveC++:審慎使用異常規(guī)格

字號:

毫無疑問,異常規(guī)格是一個引人注目的特性。它使得代碼更容易理解,因為它明確地描述了一個函數(shù)可以拋出什么樣的異常。但是它不只是一個有趣的注釋。編譯器在編譯時有時能夠檢測到異常規(guī)格的不一致。而且如果一個函數(shù)拋出一個不在異常規(guī)格范圍里的異常,系統(tǒng)在運行時能夠檢測出這個錯誤,然后一個特殊函數(shù)unexpected將被自動地調用。異常規(guī)格既可以做為一個指導性文檔同時也是異常使用的強制約束機制,它好像有著很誘人的外表。
    不過在通常情況下,美貌只是一層皮,外表的美麗并不代表其內在的素質。函數(shù)unexpected缺省的行為是調用函數(shù)terminate,而terminate缺省的行為是調用函數(shù)abort,所以一個違反異常規(guī)格的程序其缺省的行為就是halt(停止運行)。在激活的stack frame中的局部變量沒有被釋放,因為abort在關閉程序時不進行這樣的清除操作。對異常規(guī)格的觸犯變成了一場并不應該發(fā)生的災難。
    不幸的是,我們很容易就能夠編寫出導致發(fā)生這種災難的函數(shù)。編譯器僅僅部分地檢測異常的使用是否與異常規(guī)格保持一致。一個函數(shù)調用了另一個函數(shù),并且后者可能拋出一個違反前者異常規(guī)格的異常,(A函數(shù)調用B函數(shù),因為B函數(shù)可能拋出一個不在A函數(shù)異常規(guī)格之內的異常,所以這個函數(shù)調用就違反了A函數(shù)的異常規(guī)格 譯者注)編譯器不對此種情況進行檢測,并且語言標準也禁止它們拒絕這種調用方式(盡管可以顯示警告信息)。
    例如函數(shù)f1沒有聲明異常規(guī)格,這樣的函數(shù)就可以拋出任意種類的異常:
    extern void f1(); // 可以拋出任意的異常
    假設有一個函數(shù)f2通過它的異常規(guī)格來聲明其只能拋出int類型的異常:
    void f2() throw(int);
    f2調用f1是非常合法的,即使f1可能拋出一個違反f2異常規(guī)格的異常:
    void f2() throw(int)
    {
    ...
    f1(); // 即使f1可能拋出不是int類型的
    //異常,這也是合法的。
    ...
    }
    當帶有異常規(guī)格的新代碼與沒有異常規(guī)格的老代碼整合在一起工作時,這種靈活性就顯得很重要。
    因為你的編譯器允許你調用一個函數(shù)其拋出的異常與發(fā)出調用的函數(shù)的異常規(guī)格不一致,并且這樣的調用可能導致你的程序執(zhí)行被終止,所以在編寫軟件時采取措施把這種不一致減小到最少。一種好方法是避免在帶有類型參數(shù)的模板內使用異常規(guī)格。例如下面這種模板,它好像不能拋出任何異常:
    // a poorly designed template wrt exception specifications
    template
    bool operator==(const T& lhs, const T& rhs) throw()
    {
    return &lhs == &rhs;
    }
    這個模板為所有類型定義了一個操作符函數(shù)operator==。對于任意一對類型相同的對象,如果對象有一樣的地址,該函數(shù)返回true,否則返回false。