【C++ 筆記】例外處理(Exception Handling) - part 28 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 例外是什麼? --- 例外(exception)又稱為異常,是指程式在執行過程當中發生的例外情況或錯誤事件。如將兩數相除的過程中,有一個數除於 0,這會是一個例外,因為可能會導致未定義的錯誤。 而處理例外的過程就稱為例外處理,對 XD。 基本例外處理 --- ### try-catch 語法: ```cpp= try { // 這可能會拋出一個例外 // Code that might throw an exception } catch (ExceptionType e) { // 例外處理的地方 // exception handling code } ``` 當 try 區塊發生例外的時候,於 try 的區塊會停止,catch 就會去捕捉這個例外,然後在 catch 區塊裡面做一些處理。 ExceptionType e:看 catch 要捕捉什麼,如 `int e` 就是捕捉 int 型態的例外。 ### throw 拋出例外 用 throw 關鍵字拋出例外,catch 會偵測這個例外是什麼東西,如果符合的話就會去處理,如: ```cpp= try { throw val } catch (ExceptionType e) { // exception handling code } ``` 範例: ```cpp= #include <iostream> #include <string> using namespace std; int main(){ // 整數型態的例外捕捉 try { int val = 10; if (val > 5){ throw val; } cout << "No Exception." << endl; } catch (int e){ cout << "Caught an integer exception with value : " << e << endl; } // 字串型態的例外捕捉 try{ string error_msg = "Something wrong!"; throw error_msg; } catch (string e){ cout << "Caught a string exception : " << e << endl; } return 0; } ``` Output: ``` Caught an integer exception with value : 10 Caught a string exception : Something wrong! ``` 標準例外 --- 標準例外是 C++ STL 裡面定義的一組例外類別,大多被定義在 `<stdexcept>` 或 `<typeinfo>`(如 `bad_cast`) 中。 主要分成兩大類: - 邏輯錯誤(logic_error) - 執行期錯誤(runtime_error) 以下是標準錯誤的層次結構圖:  Image Source:[GeeksForGeeks](https://www.geeksforgeeks.org/exception-handling-c/) 以下是為以上層次結構圖製作的表格: | 例外類型 | 說明 | | ------------------------ | ------------------------------------------------------------ | | `std::logic_error` | 邏輯錯誤。表示程式中存在不合理的邏輯問題,通常是可預先檢查出的錯誤。 | | `std::invalid_argument` | 無效的參數。通常因傳遞了錯誤或非法的參數值而引發。屬於 `logic_error` 的子類。 | | `std::domain_error` | 數學定義域錯誤。當參數不在函數允許的數學定義域內時拋出,例如平方根的負數輸入。屬於 `logic_error` 的子類。 | | `std::length_error` | 長度錯誤。當容器長度超過其最大容量限制時拋出。屬於 `logic_error` 的子類。 | | `std::out_of_range` | 超出範圍。當使用無效索引存取容器元素時拋出。屬於 `logic_error` 的子類。 | | `std::runtime_error` | 執行期間錯誤。表示在執行階段發生非預期狀況,無法在編譯期間預測。 | | `std::range_error` | 範圍錯誤。當數值運算結果超出有效範圍但仍合法(如浮點精度損失)時拋出。屬於 `runtime_error` 的子類。 | | `std::overflow_error` | 溢位錯誤。當數值計算超過資料型態的上限時拋出。屬於 `runtime_error` 的子類。 | | `std::underflow_error` | 下溢錯誤。當浮點數計算結果太接近零而無法正確表示時拋出。屬於 `runtime_error` 的子類。 | | `std::bad_alloc` | 記憶體配置失敗。當 `new` 無法配置足夠記憶體時拋出。屬於 `std::exception` 的子類。 | | `std::bad_function_call` | 錯誤的函數呼叫。當呼叫尚未設定目標的 `std::function` 物件時拋出。 | | `std::bad_cast` | 錯誤的型態轉換。當使用 `dynamic_cast` 進行不合法的轉型時拋出。 | 在以上每個標準例外中,都有一個 what() 方法提供這些標準例外的相關資訊。 以下是個範例: ```cpp= #include <iostream> #include <stdexcept> #include <cmath> using namespace std; double safe_sqrt(double x){ if (x < 0){ throw domain_error("輸入錯誤!平方根的定義域為正數"); } return sqrt(x); } int main(){ try{ double val = -5.0; cout << "計算 " << val << " 的平方根..." << endl; double result = safe_sqrt(val); cout << "結果是: " << result << endl; } catch (domain_error e){ cout << "捕捉到 domain_error 例外: " << e.what() << endl; } return 0; } ``` Output: ``` 計算 -5 的平方根... 捕捉到 domain_error 例外: 輸入錯誤!平方根的定義域為正數 ``` catch 多個例外 --- 不只是 catch 一個例外而已,也可以多個例外: ```cpp= try { // Code that might throw an exception } catch (type1 e) { // executed when exception is of type1 } catch (type2 e) { // executed when exception is of type2 } catch (...) { // executed when no matching catch is found } ``` 以下是個範例: ```cpp= #include <iostream> #include <stdexcept> using namespace std; int divide(int a, int b){ if (b == 0){ throw invalid_argument("除數不能為0"); } if (a == 0){ throw 0; } return a / b; } int main(){ int x, y; cout << "請輸入兩個整數 (a, b) :"; cin >> x >> y; try{ int result = divide(x, y); cout << "除法運算結果 : " << result << endl; } catch (invalid_argument e){ cout << "捕捉到 invalid_argument 例外: " << e.what() << endl; } catch (int e){ cout << "捕捉到整數型態例外,值為: " << e << endl; } return 0; } ``` ### catch 所有例外 就是 `catch(...)` 即可捕捉所有的例外,延續上個範例,增加這個上去: ```cpp= #include <iostream> #include <stdexcept> using namespace std; int divide(int a, int b){ if (a == 9999){ // 新增處 throw "abc"; } if (b == 0){ throw invalid_argument("除數不能為0"); } if (a == 0){ throw 0; } return a / b; } int main(){ int x, y; cout << "請輸入兩個整數 (a, b) :"; cin >> x >> y; try{ int result = divide(x, y); cout << "除法運算結果 : " << result << endl; } catch (invalid_argument e){ cout << "捕捉到 invalid_argument 例外: " << e.what() << endl; } catch (int e){ cout << "捕捉到整數型態例外,值為: " << e << endl; } catch (...){ // 新增處 cout << "捕捉到未知的例外"; } return 0; } ``` 當輸入 a = 9999, b = 任意數時,就會輸出「捕捉到未知的例外」。 ### catch by reference 這個方法只需要傳遞對拋出例外的參考,不需要建立他的 copy,能減少這部分的效能開銷。除了這之外,主要拿來捕捉多型的例外。 ```cpp= #include <iostream> #include <stdexcept> using namespace std; int main() { try { throw runtime_error("發生錯誤:無效的操作"); } catch (const runtime_error& e) { cout << "捕捉到例外: " << e.what() << endl; } return 0; } ``` Output: ``` 捕捉到例外: 發生錯誤:無效的操作 ``` 例外規格(Exception Specification) --- 1. `noexcept` or `noexcept(true)`:如其名,告訴函數保證不會引發例外。 2. `noexcept(false)`:表示可能會拋出例外。若未使用任何的例外規格,這會是預設的,也就是說可加可不加,加了會提升可讀性。 ```cpp= void func1(int a) noexcept { ... } void func2(int b) noexcept(false) { ... } ``` 總結 --- ### 什麼是例外(Exception)? - 例外是指程式執行過程中發生的異常狀況或錯誤事件,如除數為 0。 - 例外處理是指對這些異常狀況進行捕捉與處理的機制,避免程式異常終止。 ### 基本例外處理語法 - 使用 `try` 區塊包著可能拋出例外的程式碼。 - 使用 `catch` 區塊捕捉並處理特定類型的例外。 語法: ```cpp= try { // 可能拋出例外的程式碼 } catch (ExceptionType e) { // 例外處理程式碼 } ``` `throw` 關鍵字用於拋出例外: ```cpp throw val; ``` ### 標準例外類別 定義於 `<stdexcept>` or `<typeinfo>`。 分為兩大類: - 邏輯錯誤(logic_error):程式邏輯錯誤,可預先檢查。 - 執行期錯誤(runtime_error):執行時非預期錯誤。 標準例外表: | 例外類型 | 說明 | | ------------------------ | ------------------------------------------------------------ | | `std::logic_error` | 邏輯錯誤。表示程式中存在不合理的邏輯問題,通常是可預先檢查出的錯誤。 | | `std::invalid_argument` | 無效的參數。通常因傳遞了錯誤或非法的參數值而引發。屬於 `logic_error` 的子類。 | | `std::domain_error` | 數學定義域錯誤。當參數不在函數允許的數學定義域內時拋出,例如平方根的負數輸入。屬於 `logic_error` 的子類。 | | `std::length_error` | 長度錯誤。當容器長度超過其最大容量限制時拋出。屬於 `logic_error` 的子類。 | | `std::out_of_range` | 超出範圍。當使用無效索引存取容器元素時拋出。屬於 `logic_error` 的子類。 | | `std::runtime_error` | 執行期間錯誤。表示在執行階段發生非預期狀況,無法在編譯期間預測。 | | `std::range_error` | 範圍錯誤。當數值運算結果超出有效範圍但仍合法(如浮點精度損失)時拋出。屬於 `runtime_error` 的子類。 | | `std::overflow_error` | 溢位錯誤。當數值計算超過資料型態的上限時拋出。屬於 `runtime_error` 的子類。 | | `std::underflow_error` | 下溢錯誤。當浮點數計算結果太接近零而無法正確表示時拋出。屬於 `runtime_error` 的子類。 | | `std::bad_alloc` | 記憶體配置失敗。當 `new` 無法配置足夠記憶體時拋出。屬於 `std::exception` 的子類。 | | `std::bad_function_call` | 錯誤的函數呼叫。當呼叫尚未設定目標的 `std::function` 物件時拋出。 | | `std::bad_cast` | 錯誤的型態轉換。當使用 `dynamic_cast` 進行不合法的轉型時拋出。 | 註:每個標準例外都有 `what()` public 方法可取得錯誤訊息。 ### 多重 catch 與 catch 所有例外 可以有多個 catch: ```cpp= try { // 可能拋出多種例外 } catch (type1 e) { // 處理 type1 例外 } catch (type2 e) { // 處理 type2 例外 } catch (...) { // 捕捉所有未匹配的例外 } ``` - `catch(...)` 用來捕捉所有未知例外。 ### 以參考捕捉例外(catch by reference) 使用 `catch (ExceptionType& e)` 避免 copy,提高效率,也能捕捉多型例外。 ### 例外規格(Exception Specification) - noexcept:保證函數不會拋出例外。 - noexcept(false):函數可能會拋出例外,為預設行為。 參考資料 --- [例外規格(Exception Specifications)](https://openhome.cc/Gossip/CppGossip/ExceptionSpecifications.html) [How to Catch All Exceptions in C++? - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/how-to-catch-all-exceptions-in-cpp/) [Exception Handling in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/exception-handling-c/) [C++ 异常处理 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-exceptions-handling.html)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.