處理例外
例外狀況 (Exception) 可以由處理常式 (handler) 進行處理。
目錄 |
[編輯] 處理常式
catch ( attr (可選) type-specifier-seq declarator ) compound-statement |
(1) | ||||||||
catch ( attr (可選) type-specifier-seq abstract-declarator (可選) ) compound-statement |
(2) | ||||||||
catch ( ... ) compound-statement |
(3) | ||||||||
| 屬性 | - | (C++11 起) 任意數量的 屬性,應用於該參數 |
| type-specifier-seq | - | 形式參數宣告的一部分,與函式 參數列表 中的相同 |
| 宣告子 (declarator) | - | 參數宣告的一部分,與函式 參數列表 中的相同 |
| abstract-declarator | - | 無名參數宣告的一部分,與函式 參數列表 中的相同 |
| compound-statement | - | 複合陳述式 |
處理常式中的參數宣告描述了可能導致進入該處理常式的例外狀況類型。
如果參數被宣告為下列類型之一,則程式格式錯誤:
|
(C++11 起) |
- 指向不完整類型的指標(除了(可能帶有 cv 限定符的)void 之外)
- 指向不完整類型的左值參照
如果參數被宣告為「T 的陣列」或函式類型 T,該類型會被調整為「指向 T 的指標」。
參數類型為 T 的處理常式可縮寫為「類型 T 的處理常式」。
[編輯] 例外狀況匹配
每個 try 區塊都關聯著多個處理常式,這些處理常式組成一個處理常式序列。當例外狀況從 try 區塊拋出時,會按出現順序嘗試序列中的處理常式以匹配該例外狀況。
如果滿足下列任何條件,則處理常式與類型為 E 的 例外物件 匹配:
- 處理常式類型為「可能帶有 cv 限定符的
T」或「指向可能帶有 cv 限定符的T的左值參照」,且滿足下列任一條件:
-
E和T是相同類型(忽略頂層 cv 限定符)。 -
T是E的明確公有 (public) 基底類別。
-
- 處理常式類型為「可能帶有 cv 限定符的
T」或 const T&,其中T是指標或成員指標類型,且滿足下列任一條件:
-
E是可透過下列至少一種轉換轉換為T的指標或成員指標類型:
- 標準指標轉換,不涉及轉換為私有、受保護或歧義類別的指標。
-
| (自 C++17 起) |
|
(C++11 起) |
catch (...) 處理常式可匹配任何類型的例外狀況。若存在,它只能是處理常式序列中的最後一個。此處理常式可用於確保沒有未捕獲的例外狀況會從提供 不拋出保證 (nothrow exception guarantee) 的函式中逸出。
try { f(); } catch (const std::overflow_error& e) {} // this executes if f() throws std::overflow_error (same type rule) catch (const std::runtime_error& e) {} // this executes if f() throws std::underflow_error (base class rule) catch (const std::exception& e) {} // this executes if f() throws std::logic_error (base class rule) catch (...) {} // this executes if f() throws std::string or int or any other unrelated type
如果 try 區塊的處理常式中找不到匹配項,則會在動態包圍的 try 區塊中繼續搜尋匹配的處理常式(位於同一執行緒中)(C++11 起)。
如果找不到匹配的處理常式,則會呼叫 std::terminate;在呼叫 std::terminate 之前堆疊是否進行 解開 (unwound),是由實作定義的。
[編輯] 處理例外狀況
當拋出例外狀況時,控制權會轉移到類型匹配的最接近處理常式;「最接近」意味著由控制執行緒最近進入且尚未退出的 try 關鍵字後的複合陳述式或成員初始化列表(若存在)所對應的處理常式。
[編輯] 初始化處理常式參數
參數列表中宣告的參數(若有),類型為「可能帶有 cv 限定符的 T」或「指向可能帶有 cv 限定符的 T 的左值參照」,將由類型為 E 的 例外物件 初始化,方式如下:
- 如果
T是E的基底類別,則該參數將由類型為T且代表例外物件對應基底類別子物件的左值進行 複製初始化。 - 否則,參數將由代表例外物件類型為
E的左值進行複製初始化。
參數的生命週期在處理常式退出時結束,位於處理常式內初始化的任何具有自動 儲存期 的物件被銷毀之後。
當參數宣告為物件時,對該物件的任何更改都不會影響例外物件。
當參數宣告為物件的參照時,對被參照物件的任何更改都是對例外物件的更改,並且如果該物件被重新拋出 (rethrown),將會產生影響。
[編輯] 啟用處理常式
當處理常式的參數(若有)初始化完成時,處理常式被視為 已啟用 (active)。
此外,當因 throw 而進入 std::terminate 時,隱式處理常式也被視為已啟用。
當處理常式退出時,它將不再被視為已啟用。
具有最近啟用且仍處於啟用狀態的處理常式的例外狀況,被稱為 當前正在處理的例外狀況。此類例外狀況可以 重新拋出。
[編輯] 控制流
處理常式的 compound-statement 是一個 控制流受限的陳述式。
void f() { goto label; // error try { goto label; // error } catch (...) { goto label: // OK label: ; } }
[編輯] 附註
當控制權轉移到處理常式時會發生 堆疊解開。當處理常式變為已啟用時,堆疊解開已完成。
throw 0 這個 throw 運算式拋出的例外狀況,無法與指標或成員指標類型的處理常式匹配。
|
(C++11 起) |
例外物件 絕不會是陣列或函式類型,因此指向陣列或函式類型的參照處理常式永遠不會匹配任何例外物件。
可以編寫永遠無法執行的處理常式,例如將最終衍生類別 (final derived class) 的處理常式放在對應的明確公有基底類別處理常式之後。
try { f(); } catch (const std::exception& e) {} // will be executed if f() throws std::runtime_error catch (const std::runtime_error& e) {} // dead code!
許多實作將 CWG 問題 388 的解決方案過度擴展到指向非 const 指標類型的參照處理常式。
int i; try { try { throw static_cast<float*>(nullptr); } catch (void*& pv) { pv = &i; throw; } } catch (const float* pf) { assert(pf == nullptr); // should pass, but fails on MSVC and Clang }
[編輯] 關鍵字
[編輯] 範例
以下範例展示了處理常式的幾種用法。
#include <iostream> #include <vector> int main() { try { std::cout << "Throwing an integer exception...\n"; throw 42; } catch (int i) { std::cout << " the integer exception was caught, with value: " << i << '\n'; } try { std::cout << "Creating a vector of size 5... \n"; std::vector<int> v(5); std::cout << "Accessing the 11th element of the vector...\n"; std::cout << v.at(10); // vector::at() throws std::out_of_range } catch (const std::exception& e) // caught by reference to base { std::cout << " a standard exception was caught, with message: '" << e.what() << "'\n"; } }
可能輸出
Throwing an integer exception... the integer exception was caught, with value: 42 Creating a vector of size 5... Accessing the 11th element of the vector... a standard exception was caught, with message: 'out_of_range'
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。
| DR | 應用於 | 出版時的行為 | 正確的行為 |
|---|---|---|---|
| CWG 98 | C++98 | switch 陳述式可以將控制權轉移至處理常式內部 | 禁止 |
| CWG 210 | C++98 | throw 運算式與處理常式進行匹配 | 例外物件被 與處理常式匹配 |
| CWG 388 | C++98 | 指標或成員指標類型的例外狀況無法 與不同類型的 const 參照匹配 |
使其可匹配 當可轉換時 |
| CWG 1166 | C++98 | 當匹配類型為抽象類別類型參照的 處理常式時,其行為是不明確的 |
抽象類別類型 不允許用於處理常式 |
| CWG 1769 | C++98 | 當處理常式的類型是例外物件類型的基底時, 轉換建構函式可能會用於初始化 處理常式參數 |
參數是複製初始化的 從例外物件的對應基底類別 子物件 |
| CWG 2093 | C++98 | 指向物件類型的指標例外物件無法透過限定符轉換 與指向物件類型的指標處理常式匹配 |
已允許 |
[編輯] 參考文獻
- C++23 標準 (ISO/IEC 14882:2024)
- 14.4 Handling an exception [except.handle]
- C++20 標準 (ISO/IEC 14882:2020)
- 14.4 Handling an exception [except.handle]
- C++17 標準 (ISO/IEC 14882:2017)
- 18.3 Handling an exception [except.handle]
- C++14 標準 (ISO/IEC 14882:2014)
- 15.3 Handling an exception [except.handle]
- C++11 標準 (ISO/IEC 14882:2011)
- 15.3 Handling an exception [except.handle]
- C++03 標準 (ISO/IEC 14882:2003)
- 15.3 Handling an exception [except.handle]
- C++98 標準 (ISO/IEC 14882:1998)
- 15.3 Handling an exception [except.handle]