【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)