例外是程式在執行期間產生的問題,C++ 例外是指在程式運行時發生的特殊情況,比如嘗試除以零的操作,
函式的例外宣告串列
為了增強程式的可讀性和可維護性,使程式員在使用一個函式時就能看出這個函式可能會拋出哪些例外,C++ 允許在函式宣告和定義時,加上它所能拋出的例外的串列,具體寫法如下:
void func() throw (int, double, A, B, C);
或
void func() throw (int, double, A, B, C){...}
上面的寫法表明 func 可能拋出 int 型、double 型以及 A、B、C 三種型別的例外,例外宣告串列可以在函式宣告時寫,也可以在函式定義時寫,如果兩處都寫,則兩處應一致,
如果例外宣告串列如下撰寫:
void func() throw ();
則說明 func 函式不會拋出任何例外,
一個函式如果不交待能拋出哪些型別的例外,就可以拋出任何型別的例外,
函式如果拋出了其例外宣告串列中沒有的例外,在編譯時不會引發錯誤,但在運行時, Dev C++ 編譯出來的程式會出錯;用 Visual Studio 2010 編譯出來的程式則不會出錯,例外宣告串列不起實際作用,
例外提供了一種轉移程式控制權的方式,C++ 例外處理涉及到三個關鍵字:try、catch、throw,
- throw: 當問題出現時,程式會拋出一個例外,這是通過使用 throw 關鍵字來完成的,
- catch: 在您想要處理問題的地方,通過例外處理程式捕獲例外,catch 關鍵字用于捕獲例外,
- try: try 塊中的代碼標識將被激活的特定例外,它后面通常跟著一個或多個 catch 塊,
如果有一個塊拋出一個例外,捕獲例外的方法會使用 try 和 catch 關鍵字,try 塊中放置可能拋出例外的代碼,try 塊中的代碼被稱為保護代碼,使用 try/catch 陳述句的語法如下所示:
try { // 保護代碼 }catch( ExceptionName e1 ) { // catch 塊 }catch( ExceptionName e2 ) { // catch 塊 }catch( ExceptionName eN ) { // catch 塊 }
如果 try 塊在不同的情境下會拋出不同的例外,這個時候可以嘗試羅列多個 catch 陳述句,用于捕獲不同型別的例外,
C++例外處理基本語法
C++ 通過 throw 陳述句和 try...catch 陳述句實作對例外的處理,throw 陳述句的語法如下:
throw 運算式;
該陳述句拋出一個例外,例外是一個運算式,其值的型別可以是基本型別,也可以是類,
try...catch 陳述句的語法如下:
try { 陳述句組 } catch(例外型別) { 例外處理代碼 } ... catch(例外型別) { 例外處理代碼 }
catch 可以有多個,但至少要有一個,
不妨把 try 和其后{}中的內容稱作“try塊”,把 catch 和其后{}中的內容稱作“catch塊”,
拋出例外
您可以使用 throw 陳述句在代碼塊中的任何地方拋出例外,throw 陳述句的運算元可以是任意的運算式,運算式的結果的型別決定了拋出的例外的型別,
以下是嘗試除以零時拋出例外的實體:
double division(int a, int b) { if( b == 0 ) { throw "Division by zero condition!"; } return (a/b); }
捕獲例外
catch 塊跟在 try 塊后面,用于捕獲例外,您可以指定想要捕捉的例外型別,這是由 catch 關鍵字后的括號內的例外宣告決定的,
try...catch 陳述句的執行程序是:
- 執行 try 塊中的陳述句,如果執行的程序中沒有例外拋出,那么執行完后就執行最后一個 catch 塊后面的陳述句,所有 catch 塊中的陳述句都不會被執行;
- 如果 try 塊執行的程序中拋出了例外,那么拋出例外后立即跳轉到第一個“例外型別”和拋出的例外型別匹配的 catch 塊中執行(稱作例外被該 catch 塊“捕獲”),執行完后再跳轉到最后一個 catch 塊后面繼續執行,
try { // 保護代碼 }catch( ExceptionName e ) { // 處理 ExceptionName 例外的代碼 } 上面的代碼會捕獲一個型別為 ExceptionName 的例外,如果您想讓 catch 塊能夠處理 try 塊拋出的任何型別的例外,則必須在例外宣告的括號內使用省略號 ...,如下所示: try { // 保護代碼 }catch(...) { // 能處理任何例外的代碼 }
下面是一個實體,拋出一個除以零的例外,并在 catch 塊中捕獲該例外,
#include <iostream> using namespace std; double division(int a, int b) { if( b == 0 ) { throw "Division by zero condition!"; } return (a/b); } int main () { int x = 50; int y = 0; double z = 0; try { z = division(x, y); cout << z << endl; }catch (const char* msg) { cerr << msg << endl; } return 0; }
由于我們拋出了一個型別為 const char* 的例外,因此,當捕獲該例外時,我們必須在 catch 塊中使用 const char*,當上面的代碼被編譯和執行時,它會產生下列結果:
Division by zero condition!
如果拋出的例外沒有被 catch 塊捕獲,例如,將catch(int e),改為catch(char e),當輸入的 n 為 0 時,拋出的整型例外就沒有 catch 塊能捕獲,這個例外也就得不到處理,那么程式就會立即中止,try...catch 后面的內容都不會被執行,
C++ 標準的例外
C++ 提供了一系列標準的例外,定義在 中,我們可以在程式中使用這些標準的例外,它們是以父子類層次結構組織起來的,如下所示:
下表是對上面層次結構中出現的每個例外的說明:
下面分別介紹以上幾個例外類,本節程式的輸出以 Visual Studio 2010為準,Dev C++ 編譯的程式輸出有所不同,
1) bad_typeid
使用 typeid 運算子時,如果其運算元是一個多型類的指標,而該指標的值為 NULL,則會拋出此例外,
2) bad_cast
在用 dynamic_cast 進行從多型基類物件(或參考)到派生類的參考的強制型別轉換時,如果轉換是不安全的,則會拋出此例外,程式示例如下:
#include <iostream> #include <stdexcept> using namespace std; int main() { try { char * p = new char[0x7fffffff]; //無法分配這么多空間,會拋出例外 } catch (bad_alloc & e) { cerr << e.what() << endl; } return 0; }
程式的輸出結果如下:
Bad dynamic_cast!
在 PrintObj 函式中,通過 dynamic_cast 檢測 b 是否參考的是一個 Derived 物件,如果是,就呼叫其 Print 成員函式;如果不是,就拋出例外,不會呼叫 Derived::Print,
3) bad_alloc
在用 new 運算子進行動態記憶體分配時,如果沒有足夠的記憶體,則會引發此例外,程式示例如下:
#include <iostream> #include <stdexcept> using namespace std; int main() { try { char * p = new char[0x7fffffff]; //無法分配這么多空間,會拋出例外 } catch (bad_alloc & e) { cerr << e.what() << endl; } return 0; }
程式的輸出結果如下:
bad allocation
ios_base::failure
在默認狀態下,輸入輸出流物件不會拋出此例外,如果用流物件的 exceptions 成員函式設定了一些標志位,則在出現打開檔案出錯、讀到輸入流的檔案尾等情況時會拋出此例外,此處不再贅述,
4) out_of_range
用 vector 或 string 的 at 成員函式根據下標訪問元素時,如果下標越界,則會拋出此例外,例如:
#include <iostream> #include <stdexcept> #include <vector> #include <string> using namespace std; int main() { vector<int> v(10); try { v.at(100) = 100; //拋出 out_of_range 例外 } catch (out_of_range & e) { cerr << e.what() << endl; } string s = "hello"; try { char c = s.at(100); //拋出 out_of_range 例外 } catch (out_of_range & e) { cerr << e.what() << endl; } return 0; }
程式的輸出結果如下:
invalid vector <T> subscript invalid string position
如果將v.at(100)換成v[100],將s.at(100)換成s[100],程式就不會引發例外(但可能導致程式崩潰),因為 at 成員函式會檢測下標越界并拋出例外,而 operator[] 則不會,operator [] 相比 at 的好處就是不用判斷下標是否越界,因此執行速度更快,
定義新的例外
您可以通過繼承和多載 exception 類來定義新的例外,下面的實體演示了如何使用 std::exception 類來實作自己的例外:
#include <iostream> #include <exception> using namespace std; struct MyException : public exception { const char * what () const throw () { return "C++ Exception"; } }; int main() { try { throw MyException(); } catch(MyException& e) { std::cout << "MyException caught" << std::endl; std::cout << e.what() << std::endl; } catch(std::exception& e) { //其他的錯誤 } }
這將產生以下結果:
MyException caught
C++ Exception
在這里,what() 是例外類提供的一個公共方法,它已被所有子例外類多載,這將回傳例外產生的原因

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/244533.html
標籤:C++
上一篇:請教一個關于多執行緒設計問題,每一個類都需要加鎖嗎?請大佬幫忙看下
下一篇:Worms
