C++ exception 例外處理

本篇介紹 c++ exception 例外處理,程式在執行過程中可能會發生一些特殊情況的異常,例如:記憶體不夠、越界存取,所以我們可以用 try catch 來處理這種例外情形

C++ 基本的 try… catch… 例外處理

最簡單的例外處理的寫法如下,使用 try... catch... 所組成,
用 try 包住可能會發生例外的程式碼區段,
用 catch 來處理這個 try 區域所發生的例外,
what() 是 exception 類別提供的一個 public method,exception 類別會被所有子類重載,所以不管發生哪種例外,只要呼叫 exception 類的 what() 方法,就會取得該種例外的發生的原因,這個機制是用 C++ virtual 所完成的,程式碼中的 e.what() 將回傳例外產生的原因字串。

另外,catch 裡面的 exception 變數應該要用 reference 的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <exception>

using namespace std;

int main()
{
try {
// ...
} catch (std::exception &e) { // exception should be caught by reference
cout << "exception: " << e.what() << "\n";
}

return 0;
}

接下來我們來看看實戰中的例外範例,

C++ vector 的 out_of_range 例外處理

C++ vector 存取元素的方式有兩種,一種是使用 [] operator,例如:v[0] = 0
另一種是使用 at(),例如:v.at(0) = 0
這兩個的差別在於使用 [] operator 存取超界時不會丟出例外 exception,
所以像下面這個例子中的 v[3] 實際上是讀取了不知名的記憶體位置的數值,
使用 at() 存取超界時會丟出 std::out_of_range 的例外 exception,
所以下面這個範例我們嘗試捕捉看看例外,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// g++ cpp-exception.cpp -o a.out -std=c++11
#include <iostream>
#include <vector>
#include <exception>

using namespace std;

int main()
{
vector<int> v = {1,2,3};
try {
cout << v[0] << "\n";
cout << v[1] << "\n";
cout << v[2] << "\n";
cout << v[3] << "\n";
cout << v.at(0) << "\n";
cout << v.at(1) << "\n";
cout << v.at(2) << "\n";
cout << v.at(3) << "\n";
} catch (std::exception &e) {
cout << "exception: " << e.what() << "\n";
}

return 0;
}

程式輸出如下,可以發現 使用 v[3] operator 存取超界用 try... catch... 是捕捉不到例外的,
但是當程式執行到 v.at(3) 時存取超界拋出了例外 exception,

1
2
3
4
5
6
7
8
1
2
3
0
1
2
3
exception: vector::_M_range_check: __n (which is 3) >= this->size() (which is 3)

常見的 exception 例外

以下幾乎是所以有的 exception 例外種類,C++11 以及之後加入的 exception 未列在下面,

例外種類 說明
std::exception 所有標準 C++ 例外的父類
std::bad_alloc new 配置記憶體失敗時拋出,可能是記憶體不足
std::bad_cast dynamic_cast 失敗時拋出
std::bad_exception 無法預期的例外
std::bad_typeid typeid 拋出
std::logic_error 程式碼邏輯異常拋出
std::runtime_error 執行期間的例外
std::domain_error Domain error exception
std::invalid_argument 無效的參數時拋出
std::length_error 建立長度太長時拋出,例外:std::string
std::out_of_range 存取越界時拋出,例如:std::vector
std::overflow_error 溢位時拋出
std::range_error Range error exception
std::underflow_error 算術下溢時拋出

重新拋出例外 exception

在 C++ 中有時候這個例外 exception 捕捉到了,但是某些原因未去處理或還不想處理,想要將該例外重新拋出,就需要使用 throw 這個關鍵字,
重新拋出例外 exception 後,讓該例外交給更外層的例外捕捉處理,

1
2
3
4
5
6
try {
// ...
} catch (execption &e) {
cout<< e.what() << "\n";
throw; // 重新拋出例外
}

參考
Exceptions - C++ Tutorials
http://www.cplusplus.com/doc/tutorial/exceptions/
exception - C++ Reference
http://www.cplusplus.com/reference/exception/exception/
std::exception - cppreference.com
https://en.cppreference.com/w/cpp/error/exception
簡介例外處理
https://openhome.cc/Gossip/CppGossip/ExceptionHandling.html
C++ 异常处理 | 菜鸟教程
https://www.runoob.com/cplusplus/cpp-exceptions-handling.html
Throwing out of range exception in C++ - Stack Overflow
https://stackoverflow.com/questions/2709719/throwing-out-of-range-exception-in-c/2709733
XYZ的筆記本: C++ 捕捉不到 vector 超出範圍的例外 ?
https://xyz.cinc.biz/2013/10/c-vector-try-catch-out-of-range-exception.html

其它相關文章推薦
C/C++ 新手入門教學懶人包
C++ virtual 的兩種用法
C/C++ 字串反轉 reverse
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別
C++ 類別樣板 class template
std::sort 用法與範例
std::find 用法與範例
std::queue 用法與範例
std::map 用法與範例