在〈簡介例外處理〉示範了如何以字串作為例外拋出,catch
例外時型態指明為 char const*
,這表示相同型態的例外被拋出時,會執行對應的 catch
區塊,如果有多個型態的話,可以指定多個 catch
:
try {
foo();
}
catch(int errCode) {
...
}
catch(char const* errorMsg) {
...
}
只不過直接 catch
基本型態之類的例外,在程式的意圖上不清楚,而且這類例外可以表現的資訊有限,int
型態的例外被捕捉後,還得比對錯誤碼,字串的例外被捕捉後,不一定只是想顯示字串,可能還要比對字串內容等,以做其他相應的處理。
你可以自訂例外類型,並令類型名稱具有代表例外意涵的實質意義,例如:
class Exception {
const string message;
public:
explicit Exception(const string &message) : message(message) {}
virtual const char* what() {
return this->message.c_str();
}
virtual ~Exception() = default;
};
class InvalidArgument : public Exception {
using Exception::Exception;
};
class Insufficient : public Exception {
int balance;
public:
explicit Insufficient(const string &message, int balance)
: Exception(message), balance(balance) {}
int getBalance() {
return balance;
}
};
這麼一來,就可以依不同的例外型態來捕捉例外實例了:
Account acct = {"123-456-789", "Justin Lin", 1000};
try {
acct.withdraw(10200);
acct.deposit(-500);
}
catch(InvalidArgument &ex) {
cout << "引數錯誤:" << ex.what() << endl;
}
catch(Insufficient &ex) {
cout << "帳號錯誤:" << endl
<< "\t" << ex.what() << endl
<< "\t餘額 " << ex.getBalance() << endl;
}
catch
時若型態有繼承關係,父型態不能寫在子型態的 catch
之前,因為會先被比對到,導致後續的子型態 catch
寫了等於沒寫,編譯時也會提出警訊:
Account acct = {"123-456-789", "Justin Lin", 1000};
try {
acct.withdraw(10200);
acct.deposit(-500);
}
catch(Exception &ex) {
cout << ex.what() << endl;
}
// warning: exception of type 'InvalidArgument' will be caught by earlier handler for 'Exception'
catch(InvalidArgument &ex) {
cout << "引數錯誤:" << ex.what() << endl;
}
catch(Insufficient &ex) {
cout << "帳號錯誤:" << endl
<< "\t" << ex.what() << endl
<< "\t餘額 " << ex.getBalance() << endl;
}
如果例外的型態具有相同父類,那可以將父類寫在最後,作為同類型例外的捕遺:
try {
acct.withdraw(10200);
acct.deposit(-500);
}
catch(InvalidArgument &ex) {
cout << "引數錯誤:" << ex.what() << endl;
}
catch(AccountException &ex) {
cout << "帳號操作錯誤:" << ex.what() << endl;
}
catch(Exception &ex) {
cout << ex.what() << endl;
}
C++ 有個全捕捉語法 catch(...)
,用來捕捉全部型態的例外,不過並不鼓勵使用,這之後再來談。