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
}
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());
}
| 等級 | 保證 | 典型實現 |
|---|---|---|
| 無保證 | 可能洩漏資源、破壞不變量 | 裸指針、手動釋放 |
| 基本保證 | 不洩漏資源、對象保持有效狀態 (但狀態可能改變) | 智能指針、RAII、標準容器 |
| 強烈保證 | 操作失敗時狀態回滾到調用前 (事務語義) | Copy-and-Swap、交易式修改、不可變數據結構 |
| 不拋出保證 | 絕不拋出異常 (noexcept) | 移動構造、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); }
};
// 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");
std::expected<T, E> (C++23) / std::optional / 錯誤碼;不可預期、需跨層傳播用異常。std::exception,覆寫 what() 返回有意義訊息。catch 以 const 引用 捕獲,避免切片、拷貝。std::stacktrace)。