異常是 C++ 標準化的錯誤處理機制,解決了返回值可被忽略、errno 執行緒不安全、setjmp/longjmp 不呼叫解構子等歷史痛點。本文系統涵蓋語法規則、堆疊展開機制、RTTI 動態匹配、std::exception 體系、RAII 資源管理、建構子/解構子異常安全、noexcept 規格與實戰決策指南。
try {
// 可能拋出異常的代碼
} catch (const MyException& e) { // 捕獲特定型別(引用避免切割)
// 處理
} catch (...) { // 捕獲所有其他異常
// 記錄日誌、轉換型別後重拋
throw; // 重拋當前異常
}
// 函數異常規格 (C++11 棄用動態規格,改用 noexcept)
void func() noexcept; // 承諾不拋出
void func2() noexcept(false); // 可能拋出 (預設)
throw expr 建立異常物件(複製/移動至編譯器保留緩衝區)。catch,沿途**銷毀所有區域物件**(呼叫解構子),此過程稱「堆疊展開」。catch → 呼叫 std::terminate → std::abort 終止程式。catch 出現順序,**第一個型別相容**者勝出(支援繼承多型、... 兜底)。區域物件在堆疊展開時自動解構,是唯一可靠的資源釋放機制。
void foo() {
std::unique_ptr p(new Resource); // 或 std::lock_guard lk(mtx);
bar(p.get()); // 若 bar 拋出異常,p 自動釋放 Resource
}
// 無需手寫 try/catch/finally
| 場景 | 規則 | 對策 |
|---|---|---|
| 建構子拋出 | **不會呼叫該物件解構子**,但**已完成建構的成員/基類解構子會被呼叫** | 成員改用 RAII 智慧指標 (unique_ptr, vector) 自動管理 |
| 解構子拋出 | **嚴禁**;若解構子期間再拋出異常 → std::terminate | 解構子內部 try/catch 吞噬或轉換為錯誤碼/日誌 |
| 建構子資源洩漏 | new char[10]; throw; 導致記憶體洩漏 | auto_ptr (C++11 用 unique_ptr) |
assert:僅用於**程式邏輯錯誤**(前置條件違反、不可達程式點),Release 版移除,不可用於處理執行時錯誤。void process() {
Resource* r1 = nullptr, *r2 = nullptr, *r3 = nullptr;
try {
r1 = acquire1(); step1(r1);
r2 = acquire2(); step2(r2);
r3 = acquire3(); step3(r3);
} catch (...) {
release(r1); release(r2); release(r3); // 集中釋放
throw; // 保持異常傳播
}
}
// 對比:無異常版需在每個 if (!step) 重複釋放代碼
「異常不是控制流工具,是『失敗時的安全網』。善用 RAII 讓資源管理隱形,讓業務邏輯專注於成功路徑。」