C++ 使用 try-catch 語句來捕獲和處理異常。try 塊包含可能發生錯誤的程式碼,而 catch 塊則用來捕獲並處理錯誤。
try-catch 語句的基本結構
try {
// 可能丟擲異常的程式碼
} catch (exception_type1 e1) {
// 處理異常型別 1
} catch (exception_type2 e2) {
// 處理異常型別 2
} catch (...) {
// 捕獲所有型別的異常
}
具體解釋:
-
try 塊
try 塊包含可能發生異常的程式碼。
如果 try 塊中的程式碼發生了異常,程式會跳轉到相應的 catch 塊進行異常處理。
-
catch 塊
catch 用來捕獲由 try 塊丟擲的異常,並進行處理。
可以有多個 catch 塊,每個 catch 塊負責處理特定型別的異常。
如果 catch 中的異常型別與丟擲的異常型別匹配,程式會執行該 catch 塊中的程式碼。
-
catch (...)
catch(...) 是一個萬用字元,用來捕獲所有型別的異常。
這在無法確定丟擲異常的具體型別時特別有用
舉兩個例子說明
-
第一個例子
#include <iostream> using namespace std; int main() { try { int a = 5; int b = 0; if (b == 0) { throw "Division by zero error!"; } cout << a / b << endl; } catch (const char* e) { cout << "Error: " << e << endl; // 捕獲並處理除零錯誤 } return 0; }
輸出如下:
解釋:
在 try 塊中,程式嘗試執行可能出錯的程式碼。
如果發生異常(這裡是透過 throw 手動丟擲除零錯誤),catch 塊會捕獲到異常並進行處理。
-
第二個例子
#include <iostream> #include <stdexcept> // 包含異常類 using namespace std; int main() { try { int a = 10, b = 0; if (b == 0) { throw std::runtime_error("Division by zero error!"); // 丟擲執行時錯誤 } cout << a / b << endl; } catch (const std::runtime_error& e) { cout << "Caught a runtime error: " << e.what() << endl; // 捕獲並處理 std::runtime_error } catch (const std::exception& e) { cout << "Caught a general exception: " << e.what() << endl; // 捕獲所有 std::exception 型別的異常 } catch (...) { cout << "Caught an unknown error!" << endl; // 捕獲其他所有型別的異常 } return 0; }
輸出如下:
解釋:
try 塊中的程式碼試圖執行可能會丟擲異常的操作。例如,在這個例子中,我們故意將 b 設定為 0,模擬除零錯誤。
如果程式遇到除零錯誤,丟擲一個 std::runtime_error 型別的異常。
catch (const std::runtime_error& e) 塊捕獲 std::runtime_error 型別的異常,並輸出錯誤資訊。
catch (const std::exception& e) 是更廣泛的異常型別,它會捕獲所有繼承自 std::exception 類的異常(包括 std::runtime_error)。
catch (...) 捕獲所有其他型別的異常,確保即使沒有明確處理某種異常型別,程式也不會崩潰。
throw 語句:丟擲異常
在 C++ 中,throw 關鍵字用於丟擲異常。可以在任何地方丟擲異常,通常是在遇到錯誤或不符合預期的條件時。
throw some_exception; // 丟擲一個異常
常見的異常型別
C++ 標準庫定義了幾種常見的異常型別。可以選擇丟擲這些異常型別之一,或者建立自己的異常類。
-
std::exception:這是所有標準異常類的基類,所有其他標準異常類都繼承自它。
-
std::runtime_error:執行時錯誤,表示程式執行時發生的錯誤。
-
std::logic_error:邏輯錯誤,表示程式的邏輯上有問題,比如越界訪問陣列等。
-
std::out_of_range:越界錯誤,通常用於容器類(如 std::vector)訪問越界元素時丟擲的異常。
-
std::invalid_argument:無效引數錯誤,表示傳遞給函式的引數不合法。
-
std::bad_alloc:記憶體分配錯誤,當 new 操作失敗時丟擲此異常。
自定義異常類
也可以建立自己的異常類,繼承自 std::exception 或其派生類,以便在程式中丟擲和捕獲特定型別的異常。
#include <iostream>
#include <exception>
class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "My custom exception occurred!";
}
};
int main() {
try {
throw MyException(); // 丟擲自定義異常
} catch (const MyException& e) {
std::cout << "Caught custom exception: " << e.what() << std::endl;
}
return 0;
}
輸出如下:
異常的傳播和處理
當 throw 丟擲異常時,異常會沿著呼叫棧向上傳遞,直到找到一個匹配的 catch 塊。
如果沒有合適的 catch 塊捕獲異常,程式會終止並顯示未處理的異常資訊。
比如:
#include <iostream>
#include <stdexcept> // 包含標準異常型別
using namespace std;
int main() {
try {
cout << "Throwing an exception..." << endl;
throw std::runtime_error("This is a runtime error!"); // 丟擲一個執行時錯誤
}
// 這裡只捕獲 std::logic_error 型別的異常,忽略 std::runtime_error
catch (const std::logic_error& e) {
cout << "Caught logic_error: " << e.what() << endl;
}
cout << "This will not be printed if an exception is thrown!" << endl;
return 0;
}
輸出如下:
異常處理的注意事項
異常的安全性:捕獲異常時要確保程式的狀態一致性。如果在 catch 塊中處理異常時發生了額外的錯誤,可能導致程式不穩定。
效能問題:異常處理會增加程式的執行時開銷,尤其是在頻繁丟擲和捕獲異常的情況下。因此,異常處理應該只用於真正的錯誤,而不是正常的程式流控制。