例外處理
程式中可能會發生的錯誤有三種,分別是語法錯誤(syntax error) 、執行期間錯誤(runtime error) 及語意錯誤(semantic error) ,其中編譯器會直接檢查出語法錯誤,如果含有語法錯誤的程式無法過編譯,例如
$ g++ u06.cpp
u06.cpp:4:14:錯誤:聲明末尾應有“;”int a = 12^;
生成 1 個錯誤。
$
u06.cpp檔案中漏打一個分號,編譯器挑出錯誤并印出相關資訊。
語意錯誤比較麻煩,若一個程式中有語意錯誤,這個程式可以順利通過編譯,卻會跑出非預期的結果。有語意錯誤的程式必須重新檢查程式的邏輯,手動找出錯誤并修正錯誤,因此大型軟體通常依功能拆分成各個小模組,每個小模組都分開測試,都測試無誤后才重新整合為單一軟體。
至于執行期間錯誤通常不是程式邏輯的問題,像是存檔時檔案不存在或是儲存空間不夠都會造成發生錯誤,這些錯誤發生的原因都不是程式的問題。因此C++ 提供一套例外處理(exception handling) 的機制,讓我們寫程式可以檢查執行期間錯誤。
下面我們分成數個部分介紹C++ 的例外處理
try throw catch?
例外處理為控制程式發生錯誤后的機制, C++ 使用try、throw與catch三個關鍵字(keyword) 進行例外處理。
try后面的大括弧用來放可能會發生錯誤的程式碼,在會發生錯誤的地方用throw丟出例外(exception) ,catch依據例外的型態(type) 進行處理。舉例如下
#include <iostream>int main() {int i = -1; try {if (i < 0) {throw "something wrong...";}}catch (const char* message) {std::cout << message << std::endl;}return 0;
}
假設i小于0會發生錯誤,因此檢查i是否小于0,如果小于0就用throw丟出"something wrong..."的例外
try {if (i < 0) {throw "something wrong...";}
}
對應到catch的部份,例外型態就是const的字元指標(pointer)
catch (const char* message) {std::cout << message << std::endl;
}
意思就是抓到字串(string) 的例外型態,因為throw后面就是丟出字串。
編譯執行,結果如下
$./a.out 復制代碼
有事嗎...
$
例外的型態可以是標準程式庫(standard library) 中的型態,或是自訂的型態,例如
#include <iostream>struct BadValue : public std::exception {};double divide(double a, double b) {if (b == 0) {throw BadValue();}return a / b;
}int main() {try {std::cout << divide(20, 5) << std::endl;std::cout << divide(20, 4) << std::endl;std::cout << divide(20, 3) << std::endl;std::cout << divide(20, 2) << std::endl;std::cout << divide(20, 1) << std::endl;std::cout << divide(20, 0) << std::endl;}catch (BadValue e) {std::cout << "something wrong..." << std::endl;}return 0;
}
這里的BadValue繼承自標準程式庫中的exception
struct BadValue : public std::exception {};
由于除法在分母為0時無法計算,因此在divide()?函數(function) 中遇到分母為0時就throw出一個例外
double divide(double a, double b) {if (b == 0) {throw BadValue();}return a / b;
}
try部份的程式碼會逐一執行,碰到發生例外就會跳到catch的部份,編譯執行結果如下
$ g++ u0601_2.cpp
$./a.out 復制代碼
4
5
6.66667
10
20
有事嗎...
$