拋出例外


throw 用來拋出例外,在捕捉到錯誤時,若要重新拋出錯誤,也是使用 throw;視需求而定,若捕捉錯誤後,當時情境沒有足夠的資訊可以妥善處理,可就現有資訊處理完錯誤後,重新拋出原錯誤,或者是收集資訊後建立新的錯誤拋出。

例如,如果你想將 invalid_argument 也視為一種無法修正的錯誤,可以在捕捉、進行日誌後重新拋出:

try {
    acct.withdraw(10200);
    acct.deposit(-500);
}
catch(invalid_argument &ex) {
    cout << "引數錯誤:" << ex.what() << endl;
    throw;
}
catch(Insufficient &ex) {
    cout << "帳號錯誤:"  << endl
         << "\t" << ex.what() << endl
         << "\t餘額 " << ex.getBalance() << endl;
}

或者是更具體一些,抽取 invalid_argument 訊息,重新建立 runtime_error 後拋出:

try {
    acct.withdraw(10200);
    acct.deposit(-500);
}
catch(invalid_argument &ex) {
    cout << "引數錯誤:" << ex.what() << endl;
    throw runtime_error(ex.what());
}
catch(Insufficient &ex) {
    cout << "帳號錯誤:"  << endl
         << "\t" << ex.what() << endl
         << "\t餘額 " << ex.getBalance() << endl;
}

如果函式或方法確認不會拋出例外,可以使用 noexcept 指明,例如:

void foo() noexcept {
    ...
}

知道一個函式或方法不會拋出例外,就不用費心去準備 try-catch,如果 foo 函式程式碼中不經意撰寫了 throw 試圖拋出例外,編譯器會提出警訊,不過編譯還是會通過,執行時期若真的拋出了例外,那麼程式會直接中斷,是否會有例外的傳播、try-catch 等行為都是不可確定的。

noexcept 可以接受運算式,例如 noexcept(expr),如果 expr 結果是 true,表示該函式不會拋出例外,若為 false,該函式不應拋出例外。

C++ 有個 catch(...) 語言,並不建議使用,它可以用來捕捉全部類型的例外,捕捉之目的通常是為了留下日誌並重新拋出。例如:

try {
    foo();
}
catch(SomeException &e) {
    ...
}
catch(...) {
    cout << "foo 呼叫發生未處理的錯誤" << endl;
    throw;
}

或者設置一個處理各種類型錯誤的函式:

#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;

void what(const exception_ptr &eptr = current_exception()) {
    if (!eptr) { 
        throw bad_exception(); 
    }

    try { 
        rethrow_exception(eptr); 
    }
    catch (const std::exception &e) { 
        cout << e.what() << endl; 
    }
    catch(const string &e) { 
        cout << e << endl;

    }
    catch (const char *e) { 
        cout << e << endl; 
    }
    catch (...) { 
        cout << "unknow error type" << endl;
        throw; 
    }
}

int main() {
    try { 
        throw "XD happens!";
    }
    catch (...) { 
        what();  
    }

    try { 
        throw 42; 
    } catch (...) {
        what();
    }
}

current_exception 可以取得目前捕捉到的例外,並以 exception_ptr 型態傳回,如果沒有指向任何例外,那會傳回 nullptrrethrow_exception 接受 exception_ptr 並重新拋出例外,因此可以在 what 中嘗試進行對應型態的捕捉。