首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

More Effective C++:審慎使用異常規格

     編輯:關於C++

異常規格的誘人表象

void f() throw(int, MyException); 看似完美:文檔化函數可能拋出的異常、編譯期部分檢查、執行期違規觸發 unexpected()。但深層陷阱眾多,需極度謹慎使用。

預設行為:違規即終止

  • 函數拋出非規格內異常 → 呼叫 std::unexpected()。
  • unexpected 預設呼叫 std::terminate()。
  • terminate 預設呼叫 abort() — 立即終止程式,不解構區域物件,不清理資源。
  • 一個微小的規格違規,引發災難性後果。

陷阱一:編譯器檢查極其有限


extern void f1();           // 無規格,可拋出任何異常
void f2() throw(int) {      // 規定只拋 int
    f1();                   // 合法!編譯器不檢查 f1 可能拋出非 int
}

標準禁止編譯器拒絕此類調用 (僅能警告)。新舊代碼整合時雖需此彈性,但極易埋下隱患。

陷阱二:模板與異常規格水火不容


template 
bool operator==(const T& lhs, const T& rhs) throw() { // 宣稱不拋出
    return &lhs == &rhs;
}

若 T 重載 operator& 且會拋出異常 → 違反 throw() → 呼叫 unexpected。
模板幾乎無法提供有意義的異常規格,因不知型別參數會做何操作。原則:模板內避免異常規格。

陷阱三:回調函數與未知代碼


typedef void (*Callback)(int, int, void*);
class System {
    Callback cb;
public:
    void trigger() throw() { cb(x, y, data); } // 危險!cb 可能拋出
};

解決:在函數指標型別加規格 typedef void (*Callback)(...) throw();,強制回調不拋出 (需編譯器支援)。否則移除觸發函數的異常規格。

陷阱四:系統異常 (bad_alloc) 無處不在

任何 new 可能拋出 std::bad_alloc。若函數規格未含它,配置失敗即觸發 unexpected。
幾乎所有配置記憶體的函數都不應有限制性異常規格。

補救:自訂 unexpected 處理器

若無法預防違規 (如必須呼叫無規格舊庫),可替換 unexpected 行為:


class UnexpectedException {}; // 統一轉換型別

void convertUnexpected() { throw UnexpectedException(); }
std::set_unexpected(convertUnexpected); // 安裝

// 或保留原始異常類型 (轉為 bad_exception)
void convertUnexpected() { throw; } // 重新拋出當前異常
std::set_unexpected(convertUnexpected);
// 需在所有規格含 bad_exception 或基類 exception

違規異常將被轉換為指定型別繼續傳播,避免 terminate。

陷阱五:阻攔上層合法處理器


class Session {
    static void logDestruction(Session*) throw(); // 宣稱不拋出
public:
    ~Session() {
        try { logDestruction(this); }
        catch (...) { } // 想處理所有異常
    }
};

若 logDestruction 內部違規拋出 → unexpected → terminate → 析構函數的 catch 永無機會執行。異常規格反而阻礙了正確的錯誤處理。

總結:何時使用、何時避免

場景建議
Leaf 函數 (不呼叫其他可拋出代碼)、明確不拋出可用 throw() (C++11 noexcept)
模板函數絕不使用
呼叫無規格/第三方代碼不要加規格 或移除既有規格
配置記憶體 (new)不要限制規格 (含 bad_alloc)
虛擬函數介面謹慎:衍生類覆寫需遵守基類規格 (嚴格)
需要文檔化異常改用註解 // Throws: MyException, std::bad_alloc

異常規格是「帶刺的玫瑰」:文檔價值有限,執行風險極高,編譯器檢查又極弱。現代 C++ (C++11 起) 以 noexcept 取代,語意更清晰 (是否拋出而非列舉型別),優化機會更大。除非極度確定,否則寧缺勿濫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved