簡介例外處理


撰寫一個程式從某些角度來說其實並不困難,一個程式的撰寫的過程中,避免程式執行時的錯誤,往往佔了程式開發時程的絕大多數時間,有很多方法可以避免程式 執行時的錯誤,這也與您所使用的程式語言有關,C++則提供了「例外處理」(Exception handling)機制。

在之前還沒有學習例外處理之前,如果要撰寫一個除法程式,並由使用者輸入除數與被除數,由於被除數除以0沒有意義,所以之前可能必須像以下這樣避免錯誤:
if(b != 0)  {
    cout << "a/b = "
             << double(a / b)
             << endl;
}
else {
   cout << "除數不能為0"
            << endl;
}

這樣的方式過去在一些程式語言中會很常被利用,利用判斷式來避免錯誤的發生,然而這樣的方式會讓錯誤處理與程式的處理邏輯混在一起,使得程式難以閱讀,在 C++中可以利用例外處理機制,將程式邏輯與錯誤處理適當的分開撰寫,增加程式的可讀性與安全性。

C++中的例外處理使用三個關鍵字來進行:try、catch、throw,其語法架構如下:
try {
    // 程式
    throw Type
} catch(Type 1) {
    // 錯誤處理
} catch(Type 2) {
    // 錯誤處理
}

在程式中,當檢查到錯誤發生時,使用throw丟出一個數值,這個數值可能是一個整數、浮點數、字串或是物件,丟出數值之後,程式邏輯會離開丟出點,然後 開始比對catch中設定的資料型態,如果找到對應的型態,就執行該區塊中的程式碼,執行完後就離開整個try...catch,這有些類似switch 語法,執行完對應的case之後設定break來離開switch區塊。

先以一個簡單的程式作示範:

#include <iostream> 
using namespace std;

int main() {
int a = 0;
int b = 0;

cout << "請輸入被除數: ";
cin >> a;
cout << "請輸入除數: ";
cin >> b;

try {
if(b == 0)
throw 0;
cout << "a / b = "
<< static_cast<double>(a) / b
<< endl;
}
catch(int err) {
cout << "除數為: " << err << endl;
cout << "結果無限大" << endl;
}

return 0;
}

執行結果:

請輸入被除數: 10
請輸入除數: 0
除數為: 0
結果無限大


當使用者輸入除數為0時,程式會檢查出來,此時丟出一個整數錯誤代碼,程式中catch有一個捕捉整數的區塊,當捕捉到錯誤代碼時,就會執行catch區 塊中的程式碼。

發生錯誤時,執行哪一段catch區塊是由您所丟出的資料型態而定,丟出整數時就由設定catch整數的區塊捕捉,丟出浮點數時就用設定catch浮點數 的區塊捕捉,您也可以直接丟出一個代表錯誤訊息的字串,以說明錯誤的原因,例如:

#include <iostream> 
using namespace std;

int main() {
int a = 0;
int b = 0;

cout << "請輸入被除數: ";
cin >> a;
cout << "請輸入除數: ";
cin >> b;

try {
if(b == 0)
throw "發生除零的錯誤";
cout << "a / b = "
<< static_cast<double>(a) / b
<< endl;
}
catch(int err) {
cout << "除數為: " << err << endl;
cout << "結果無限大" << endl;
}
catch(const char* str) {
cerr << "錯誤: " << str << endl;
}

return 0;
}

執行結果:

請輸入被除數: 9
請輸入除數: 0
錯誤: 發生除零的錯誤


如果打算在catch中處理完例外之後,再度將例外丟出,則再使用throw即可,例如:
catch(...) {
    // 處理例外
    throw; // 再度丟出
}

如果您丟出例外而catch中並沒有相對捕捉該例外的區塊,則程式會呼叫標準函式庫中的terminate()函式,而預設terminate()會呼叫 abort()函式來終止程式。

如果您想捕捉所有型態的例外,您可以使用catch(...),通常這會放在所有catch之後,以捕捉所有尚未考慮到的例外狀況:
catch(...) {
    // 處理所有的例外
}

事實上,C++的例外處理鼓勵您自訂例外類別階層體系,將這些類別可以當作catch的資料型態設定條件,以應付各種不同的錯誤處理。