命名空間
變體
動作

處理例外

出自 cppreference.com
< cpp‎ | language
 
 
C++ 語言
一般主題
流程控制
條件執行陳述式
if
疊代陳述式 (迴圈)
for
範圍 for (C++11)
跳躍陳述式
函式
函式宣告
Lambda 函式運算式
inline 指定符
動態例外規範 (直到 C++17*)
noexcept 指定符 (C++11)
例外

catch 處理常式
命名空間
型別
指定符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期指定符
初始化
 
 
例外
try 區塊
拋出例外
處理例外
例外規格
    noexcept 規格 (C++11)
    動態規格 (C++17 前*)
noexcept 運算子 (C++11)
 

例外狀況 (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)
1) 帶有名稱參數的處理常式。
2) 帶有無名參數的處理常式。
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 的左值參照」,且滿足下列任一條件:
  • ET 是相同類型(忽略頂層 cv 限定符)。
  • TE 的明確公有 (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例外物件 初始化,方式如下:

  • 如果 TE 的基底類別,則該參數將由類型為 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 運算式拋出的例外狀況,無法與指標或成員指標類型的處理常式匹配。

  • 可以使用 throw nullptr 來拋出一個與此類處理常式匹配的空指標。
(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
}

[編輯] 關鍵字

catch

[編輯] 範例

以下範例展示了處理常式的幾種用法。

#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]

[編輯] 參見

English Deutsch 日本語 한국어 中文(简体) 中文(繁體)