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

C++ 異常機制演進史:設計初衷、RAII 誕生與最佳實踐

     編輯:C++ 入門

異常設計假定

Bjarne Stroustrup 闡述異常設計基於四點假定:

  1. 主要用於**錯誤處理**而非控制流。
  2. 異常處理代碼**遠少於**正常函數定義。
  3. 異常拋出頻率**遠低於**函數調用。
  4. 異常純屬**語言級**概念,不依賴作業系統支援。

異常 vs 返回值:語義差異

  • **返回值**:函數可能出錯,**調用者可選擇忽略**,程序繼續執行。
  • **異常**:函數出錯時**強制調用者處理**(捕獲),否則程序終止(`std::terminate`)。

建議:若錯誤屬於「預期內可恢復」(如文件不存在),用返回值/錯誤碼;若屬於「嚴重違反前置條件/不變量」(如除零、合約失效),用異常。

為何無異常規範

早期提議 `void f() throw(E1, E2)` 標註拋出異常類型,但導致:

  • 修改底層函數異常類型需連鎖修改上層聲明與實現,破壞模塊獨立編譯。
  • 模板實例化時異常類型難以靜態確定。

最終 C++ 標準**保留但棄用**動態異常規範,C++11 引入 `noexcept` 僅表達「不拋出」承諾。

異常核心:資源管理與 RAII

異常拋出會跳過後續代碼,若資源(文件、鎖、內存)在拋出點後釋放則洩漏。**RAII(Resource Acquisition Is Initialization)**應運而生:

  • 資源獲取即初始化(構造函數)。
  • 資源釋放綁定對象生命週期(析構函數)。
  • 棧展開時自動調用析構函數,**保證無洩漏**。

構造函數中拋出異常的爭議

  • Stroustrup:異常是構造函數報告失敗**唯一**途徑(構造函數無返回值)。
  • Meyers/Sutter:構造函數拋出異常可能導致**已分配內存洩漏**(`new T()` 中 `T` 構造拋出,`operator new` 分配的內存無人釋放)。建議用雙階段初始化(`create()` + `init()`)或工廠函數返回 `optional`/`expected`。

折中:構造函數內部使用 RAII 管理子資源,若自身拋出,外層 `new` 需配合智能指針或 `try-catch` 釋放。

無法恢復執行點

異常**不支援**從拋出點恢復繼續執行(resumption model)。歷史證明恢復模型極難正確實現(上下文保存、副作用回滾),C++ 採用**終止模型**(termination model):捕獲後只能修復狀態並重試或傳播。

現代最佳實踐

  1. **異常安全保證**:基本承諾(不洩漏、不破壞不變量)、強烈承諾(事務性回滾)、不拋出承諾(`noexcept`)。
  2. **標準異常體系**:繼承 `std::exception`、`std::runtime_error`、`std::logic_error` 等。
  3. **邊界處理**:模塊邊界捕獲所有異常轉為錯誤碼/日誌,避免跨模塊拋出(C 接口、插件、線程入口)。
  4. **性能**:正常路徑零開銷(零成本異常模型),拋出路徑較慢但罕見。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved