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

C++ 異常處理機制:try/catch/throw、標準異常體系、自定義異常、RAII 與 noexcept 完整指南

     編輯:C++ 入門

C++ 異常提供錯誤傳播的結構化機制,配合 RAII 保證資源安全釋放。本文系統講解 try/catch/throw、標準異常層次、自定義異常、異常安全保證、noexcept、異常規範演進、效能與最佳實踐。

基礎語法

try {
    // 保護代碼
    risky();
} catch (const std::exception& e) { // 捕獲基類引用,避免切片
    std::cerr << "Error: " << e.what() << std::endl;
} catch (...) { // 捕獲所有其他 (C 異常、SEH 等)
    std::cerr << "Unknown error" << std::endl;
}

void risky() {
    if (bad) throw std::runtime_error("Something went wrong");
    // throw 42; // 可拋出任意類型,但強烈建議繼承 std::exception
}

標準異常層次結構 (C++11/17/20)

std::exception
├── std::logic_error           // 邏輯錯誤 (理論上可預防)
│   ├── std::domain_error      // 數學域錯誤
│   ├── std::invalid_argument  // 無效參數
│   ├── std::length_error      // 長度超限
│   ├── std::out_of_range      // 越界訪問
│   └── std::future_error      // future/promise 錯誤 (C++11)
├── std::runtime_error         // 運行時錯誤 (難以預防)
│   ├── std::range_error       // 範圍錯誤
│   ├── std::overflow_error    // 上溢
│   ├── std::underflow_error   // 下溢
│   ├── std::system_error      // 系統錯誤 (errno/code) (C++11)
│   │   └── std::ios_base::failure // IO 流失敗
│   └── std::regex_error       // 正則錯誤 (C++11)
├── std::bad_alloc             // 記憶體分配失敗
├── std::bad_cast              // dynamic_cast 失敗
├── std::bad_typeid            // typeid 失敗
├── std::bad_exception         // unexpected() 拋出
├── std::bad_weak_ptr          // weak_ptr 鎖定失敗 (C++11)
├── std::bad_function_call     // 空 function 調用 (C++11)
├── std::bad_optional_access   // optional 空值訪問 (C++17)
├── std::bad_variant_access    // variant 類型不匹配 (C++17)
├── std::bad_any_cast          // any 類型轉換失敗 (C++17)
└── std::format_error          // format 錯誤 (C++20)

自定義異常類


class AppException : public std::runtime_error {
public:
    using std::runtime_error::runtime_error; // 繼承構造函數
    virtual int code() const noexcept { return -1; }
};

class NetworkException : public AppException {
    int err_code_;
public:
    NetworkException(const std::string& msg, int code) 
        : AppException(msg), err_code_(code) {}
    int code() const noexcept override { return err_code_; }
    const char* what() const noexcept override { 
        return (std::string(AppException::what()) + " [code=" + std::to_string(err_code_) + "]").c_str(); 
    }
};

// 使用
try {
    if (socket_error) throw NetworkException("Connection refused", ECONNREFUSED);
} catch (const NetworkException& e) {
    if (e.code() == ECONNREFUSED) retry();
} catch (const AppException& e) {
    log(e.what());
}

異常安全保證等級 (Exception Safety Guarantees)

等級保證典型實現
無保證可能洩漏資源、破壞不變量裸指針、手動釋放
基本保證不洩漏資源、對象保持有效狀態 (但狀態可能改變)智能指針、RAII、標準容器
強烈保證操作失敗時狀態回滾到調用前 (事務語義)Copy-and-Swap、交易式修改、不可變數據結構
不拋出保證絕不拋出異常 (noexcept)移動構造、swap、析構函數、基本類型操作

Copy-and-Swap 惯用法 (強烈保證)


class Widget {
    std::unique_ptr<Impl> pImpl;
public:
    Widget& operator=(Widget other) { // 參數傳值:自動調用拷貝/移動構造
        swap(other); // 交換內部指針,舊資源隨 other 析構釋放
        return *this;
    }
    void swap(Widget& other) noexcept { pImpl.swap(other.pImpl); }
};

noexcept 與異常規範演進


// C++11 noexcept
void func() noexcept;              // 承諾不拋出
void func() noexcept(true);        // 同義
void func() noexcept(false);       // 可能拋出 (默認)

// 條件 noexcept
template <typename T>
void swap(T& a, T& b) noexcept(std::is_nothrow_move_constructible_v<T> && 
                                std::is_nothrow_move_assignable_v<T>) {
    T tmp = std::move(a); a = std::move(b); b = std::move(tmp);
}

// 違反 noexcept 調用 std::terminate()
void noexcept_func() noexcept { throw 1; } // 運行時崩潰

// noexcept 運算符 (編譯期查詢)
static_assert(noexcept(std::swap(int, int)), "int swap noexcept");

效能考量

  • 零開銷原則:不拋出異常時,正常路徑零額外指令 (編碼在側表 .eh_frame)。
  • 拋出路徑昂貴:棧展開、尋找 catch、調用析構函數、RTTI 類型匹配。異常僅用於異常情況,非控制流。
  • 錯誤碼 vs 異常:高頻、可預期失敗 (查找失敗、解析失敗) 用 std::expected<T, E> (C++23) / std::optional / 錯誤碼;不可預期、需跨層傳播用異常。

最佳實踐清單

  • ✅ 異常類繼承 std::exception,覆寫 what() 返回有意義訊息。
  • ✅ catch 以 const 引用 捕獲,避免切片、拷貝。
  • ✅ 捕獲順序:具體類在前,基類在後。
  • ✅ 析構函數、移動操作、swap 必須 noexcept (C++11 標準要求)。
  • ✅ 資源管理全面 RAII (智能指針、標準容器、lock_guard、自定義 RAII)。
  • ✅ 邊界轉換:C API / C++ 回調 / 線程入口 捕獲所有異常轉為錯誤碼/日誌,不讓異常逃逸。
  • ✅ 異常中攜帶上下文:錯誤碼、檔案/行、參數快照、堆疊跟蹤 (Boost.Stacktrace / C++23 std::stacktrace)。
  • ✅ 測試異常路徑:注入故障 (斷網、磁盤滿、記憶體耗盡) 驗證強烈保證/基本保證。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved