在〈定義類別〉中,Account
的 deposit
及 withdraw
,在參數值 amount
不正確時,都是直接顯示文字模式下的訊息後直接 return
,如果這個類別不只使用在文字模式中呢?
這就需要有個方式,令 deposit
及 withdraw
的呼叫方程式碼,可以得知引數不正確,方式之一是透過傳回值,例如,令 deposit
及 withdraw
可以傳回 int
,以不同的傳回值代表發生了什麼問題,呼叫方必須檢查傳回值,知道方法是否順利執行成功,並在傳回值代表發生錯誤時予以處理,基於 C 風格的錯誤處理,就是這麼一回事。
不過,因為 C/C++ 的傳回值只能有一個,若方法本身就會傳回執行結果,例如,若 withdraw
本身就會傳回提領的數目,型態為 double
,那麼就無法再以傳回 int
值來通知錯誤是否發生,當然,透過適當的設計,還是可以達到通知錯誤的效果,例如傳回負的 double
表示提領失敗,或者是以類別封裝提領款項與錯誤碼,令 withdraw
傳回該類別實例,透過檢查實例中代表錯誤的值域來確認執行成功與否,若成功則提取實例中的款項欄位,若失敗進行錯誤處理。
然而,有時開發者會忘了要檢查錯誤,程式也可能因此在發生錯誤時,繼續往下執行,因而在後續發生不可預期的結果;另一方面,也許會希望有一種方式,在錯誤發生時,直接中斷流程,而且傳播錯誤,在每個上層的呼叫點都中斷,直到有某個呼叫點處理錯誤為止。
當然,這也可以透過逐層撰寫檢查錯誤的程式碼來達成,不過,若想令傳播錯誤,在每個上層的呼叫點都中斷,直到有某個呼叫點處理錯誤為止。這件事成為某些誤發生時的預設行為呢?
C++ 可以藉由拋出錯誤來達到,錯誤值可以是任何型態,例如拋出錯誤訊息的字串:
...略
void Account::deposit(double amount) {
if(amount <= 0) {
throw "必須存入正數";
}
this->balance += amount;
}
void Account::withdraw(double amount) {
if(amount > this->balance) {
throw "餘額不足";
}
this->balance -= amount;
}
被拋出的錯誤稱為例外(exception),在呼叫 deposit
、withdraw
時,若指定了錯誤的引數,例外會被拋出,執行流程就會中斷:
Account acct = {"123-456-789", "Justin Lin", 1000};
cout << acct.to_string() << endl;
acct.deposit(-500); // terminate called after throwing an instance of 'char const*'
實際執行執行程式時,流程從拋出例外後就整個中斷了,程式整個掛點,若想處理被拋出的例外,可以使用 try-catch
語法,例如:
Account acct = {"123-456-789", "Justin Lin", 1000};
try {
acct.deposit(-500);
}
catch(char const* error) {
cout << error << endl; // 顯示「必須存入正數」
}
可能拋出例外的程式碼,可以撰寫在 try
區塊之中,如果沒有發生錯誤,執行完 try
區塊後,就不會執行 catch
區塊,若執行時發生錯誤,流程會從例外拋出處中斷,然後對應的 catch
區塊可以指定例外型態來捕捉,例外捕捉後會指定給括號中的變數,接著執行 catch
區塊。
若 Account
是某程式庫的類別,由於錯誤發生時的處理方式並沒有寫死,而是拋出例外,讓呼叫方自行決定是否捕捉處理;這邊在文字模式執行時,捕捉例外後顯示錯誤值;想像一下,如果 Account
被用於圖形介面環境,捕捉例外後就可以操作圖形介面,來顯示相對應的錯誤訊息。