Skip to content

在程式語言中,錯誤處理 (例外情況) 是非常重要的一件事,這能在程式出現一些超出預期的狀況時直接通知報錯,避免問題擴大。

錯誤處理

這邊簡單介紹一下,錯誤處理主要有兩種流派,一種是將錯誤與回傳綁定 (Rust),另外一種則是使用 try / catch 語法,相當於在回傳值外加了另一種消息通道。

拋出例外

想像你寫了一個函數,之後會用到很多次,為了防止出錯,所以你使用 if else 將例外狀況全部拒之門外,但呼叫者也不知道出錯了,因為他們收到了正確的結果。

這時 try catch 語法的優勢就展現了出來,你只要在這個部分加入一個 throw 拋出例外,就可以讓呼叫的函數知道問題,如果沒有函數處理問題,那就會直接報錯。

cpp
int divide(int a, int b) {
    if (b == 0) throw std::runtime_error("b cannot be 0");
    return a / b;
}

throw 的時候,如果有更明確的問題分類,可以使用一些自定義類別,或是直接使用 C++ 自帶的 exception

處理例外情況

如果要使用一段別人寫的函數,他可能總共使用了幾十個其他的函數,這明顯無法準確預測到底會出什麼問題,因此可以在這段程式的外面包裝一層 try catch 在出問題的時候使用備用方案,或是進一步的處理。

只要在 try catch 語法的範圍中有 throw 都會由最接近的 catch 處理,通常是父函數

cpp
int main() {
    int a = 1, b = 3, c;
    try {
        c = divide(a, b);
    } catch (const std::exception& e) {
        cout << "something go wrong: " << e.what() << endl;
    }
}

try catch hell

雖然說 try catch 語法看起來很強大與安全,但其實在大型的專案中很容易產生 try catch hell,如果你再呼叫的每一層函數都加上了一層 try catch 而他們又沒有能力解決這個例外情況,只能再次拋出異常,那最後要追蹤問題時就需要看很多層才能知道問題。

更好的使用

  • 原則 1: 只有當你「真的能處理」這個問題時,才 catch 它。
  • 原則 2: 對於「預料中」的失敗(如:使用者帳密打錯),請使用 if-else 或回傳值,不要浪費昂貴的例外機制。