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
型態傳回,如果沒有指向任何例外,那會傳回 nullptr
,rethrow_exception
接受 exception_ptr
並重新拋出例外,因此可以在 what
中嘗試進行對應型態的捕捉。