C++筆記 --- 異常捕獲


 

目錄

abort 異常終止程序
異常捕獲機制(try-catch)

 (本章節中例子都是用 VS2005 編譯調試的)


 

調用 abort 函數終止程序

調用 abort() 函數來終止發現異常的程序. abort() 函數將直接終止程序而不是首先返回到主函數(在 VC 下的入口函數 main[控制台] 或 WinMain[窗體程序])中

例子:

View Code
 1 #include <iostream>
 2 #include <cstdlib>
 3 
 4 double hmean(double a,double b)
 5 {
 6     if(a == -b)
 7     {
 8         std::cout<<"untenable arguments to hmean()"<<std::endl;
 9         std::abort();  //發現錯誤,終止程序.
10     }
11     return 2.0*a*b/(a+b);
12 }
13 void main()
14 {
15     double x,y,z;
16     std::cout<<"Enter two number:  \n";
17     while(std::cin>>x>>y)
18     {
19         z = hmean(x,y);
20         std::cout<<"Barmonic mean of "<<x<<" and "<<y<<" is "<<z<<std::endl;
21         std::cout<<"Enter next set of numbers < q to quit >:";
22     }
23     std::cout<<"Bye!"<<std::endl;
24     system("pause");
25 }

當輸入為 12 -12 時候輸出結果( 編譯器為 VS2005 ):

 [返回目錄]


 

 

throw-catch 異常捕獲

[工作原理][聲明原型][工作流程][異常機制與函數返回機制區別][優點][注意問題][將對象作為異常類型][異常規范][exception類][bad_alloc類]

工作原理:

  • throw 語句實際上是跳轉即命令跳轉到另一條語句. throw 關鍵字表示引發異常緊隨其后的值指出了異常的特征,catch 關鍵字表示捕獲異常處理程序,以關鍵字 catch 開頭隨后位於括號中的類型聲明它指出了處理程序要響應的異常類型(即響應對應的 throw 拋出的異常類型)然后用一個花括號括起代碼塊指定要采取的措施. catch 關鍵字和異常類型作為標簽指出異常被 throw 拋出以后程序根據異常類型找到對應的 catch 的異常處理並跳轉到此位置開始執行try 語句塊表示其中特定的異常可能被激活的代碼,此后它后面跟着一個或多個 catch 塊,在 try 塊中的代碼段表明需要這里注意在這些代碼塊可能會引發的異常.
  • 在 throw 語句后寫上異常對象時, throw 先通過 copy 構造函數構造一個新對象,再把該新對象遞給 catch. 那么當異常拋出后新對象如何釋放?異常處理機制保證:異常拋出的新對象並非創建在函數棧上,而是創建在專用的異常棧上,因此它才可以跨接多個函數而傳遞到上層,否則在棧清空的過程中就會被銷毀.所有從 try 到 throw 語句之間構造起來的對象的析構函數將被自動調用.但如果一直上溯到 main 函數后還沒有找到匹配的 catch 塊,那么系統調用 terminate() 終止整個程序,這種情況下不能保證所有局部對象會被正確地銷毀.
  • 引發的異常對象將被第一個與之匹配的 catch 塊捕獲,這個意味着 catch 塊的排列順序應該與派生順序相反.所以只有通過真氣地排列 catch 塊的順序,才能讓程序在處理異常方面有選擇余

聲明原型:

try{
  ... //try block
  throw typeX_argX;//拋出異常
  ... //try block
}
catch(type1 arg1){
  ... //catch block
}
catch(type2 arg2){
  ... //catch block
}
...
catch(typeN argN) {
  ... //catch block
}
catch(...) //用於捕獲任何異常,所以切記要把它放最后一個.
{
  ... //catch block
}

try-catch 使用例子:

View Code
 1 #include <iostream>  
 2 using namespace std;  
 3 
 4 void main()  
 5 {
 6     double x(0),y(0),z(0);
 7     cout<<"enter two number:"<<endl;
 8 
 9     while(cin>>x>>y)
10     {
11         try{
12             if(x == -y)
13                 throw "x == -y isn't allowed!";
14             z = 2.0*x*y/(x+y);
15         }
16         catch(const char* s)
17         {
18             cout<<s<<endl;
19             cout<<"Enter a new pair of numbers:\n";
20             continue;
21         }
22         std::cout<<"Harmonic mean of "<<x<<" and "<<y<<" is "<<z<<endl;
23         std::cout<<"Enter next set of numbers< q to quit >:\n";
24     }
25     cout<<"Bye!\n";
26     system("pause");  
27 }

輸出結果:

一個函數引發多個異常:

View Code
 1 #include <iostream>  
 2 using namespace std;  
 3 
 4 void hmean(double x,double y)throw(const char*,int)
 5 {
 6     if(x > y)
 7         throw "x > y isn't allowed!";
 8     if(x<0 || y<0)
 9         throw 0;
10 }
11 void main()  
12 {
13     double x(0),y(0),z(0);
14     cout<<"enter two number:"<<endl;
15 
16     while(cin>>x>>y)
17     {
18         try{
19             hmean(x,y);
20             cout<<"these numbers is ok!"<<endl;
21         }
22         catch(const char* s)
23         {
24             cout<<s<<endl;
25             cout<<"Enter a new pair of numbers:\n";
26             continue;
27         }
28         catch(int)
29         {
30             cout<<"invalid number!"<<endl;
31             cout<<"Enter a new pair of numbers(greater than zero):\n";
32             continue;
33         }
34         std::cout<<"Enter next set of numbers< q to quit >:\n";
35     }
36     system("pause");  
37 }

輸出結果:

工作流程:

throw-catch 塊是可以嵌套分層的,並通過異常對象的數據類型來進行匹配,以找到正確的 catch-block 異常錯誤處理代碼,過程如下:

  • 首先在拋出異常的 throw-catch 塊中查找 catch block,按順序先是與第一個 catch block 塊匹配,如果拋出的異常對象的數據類型與 catch block 中傳入的異常對象的臨時變量(就是catch語句后面參數)的數據類型完全相同,或是它的子類型對象,則匹配成功,進入到catch block中執行,否則到二步;
  • 如果有二個或更多的 catch block,則繼續查找匹配第二個、第三個,乃至最后一個 catch block,如匹配成功,則進入到對應的 catch block 中執行;否則到三步;
  • 返回到上一級的 throw-catch 塊中,按規則繼續查找對應的 catch block.如果找到,進入到對應的 catch block 中執行,否則到四步.
  • 再到上上級的 throw-catch 塊中,如此不斷遞歸,直到匹配到頂級的 throw-catch 塊中的最后一個catch block,如果找,進入到對應的catch block中執行,否則程序將會執行 terminate()退出.

[返回子目錄]

流程圖如下:

在一個異常捕獲的中的 try-catch 搜索過程:
(所謂的第一個 catch-block表示的是在 try 語句塊后第一個緊跟的 catch-block,第二第三 catch-block 也是根據 try 位置而定)

例子:

例1 -- 基類在子類之前

View Code
 1 #include <iostream>  
 2 using namespace std;  
 3 
 4 class A
 5 {
 6 public:
 7     void show(){cout<<"this is class A"<<endl;}
 8 };
 9 class B:public A
10 {
11 public:
12     void show(){cout<<"this is class B"<<endl;}
13 };
14 class C:public B
15 {
16 public:
17     void show(){cout<<"this is class C"<<endl;}
18 };
19 void main()  
20 {
21     int i;
22     A a;
23     B b;
24     C c;
25     cout<<"enter a number(0 is throw class A; 1 is throw class B; 2 is throw class C):\n";
26     while(cin>>i)
27     {
28         try{
29             switch(i)
30             {
31             case 0:
32                 throw a;
33                 break;
34             case 1:
35                 throw b;
36                 break;
37             case 2:
38                 throw c;
39                 break;
40             default:
41                 cout<<"the number is invalid!"<<endl;
42             }    
43         }
44         catch(A a)
45         {
46             a.show();
47         }
48         catch(B a)
49         {
50             a.show();
51         }
52         catch(C a)
53         {
54             a.show();
55         }
56         cout<<"enter a number(0 is throw class A; 1 is throw class B; 2 is throw class C):\n";
57     }
58     system("pause");  
59 }

 輸出結果:

例2 -- 基類在子類之后

View Code
 1 #include <iostream>  
 2 using namespace std;  
 3 
 4 class A
 5 {
 6 public:
 7     void show(){cout<<"this is class A"<<endl;}
 8 };
 9 class B:public A
10 {
11 public:
12     void show(){cout<<"this is class B"<<endl;}
13 };
14 class C:public B
15 {
16 public:
17     void show(){cout<<"this is class C"<<endl;}
18 };
19 void main()  
20 {
21     int i;
22     A a;
23     B b;
24     C c;
25     cout<<"enter a number(0 is throw class A; 1 is throw class B; 2 is throw class C):\n";
26     while(cin>>i)
27     {
28         try{
29             switch(i)
30             {
31             case 0:
32                 throw a;
33                 break;
34             case 1:
35                 throw b;
36                 break;
37             case 2:
38                 throw c;
39                 break;
40             default:
41                 cout<<"the number is invalid!"<<endl;
42             }    
43         }
44 
45         catch(C a)
46         {
47             a.show();
48         }
49         catch(B a)
50         {
51             a.show();
52         }
53         catch(A a)
54         {
55             a.show();
56         }
57         cout<<"enter a number(0 is throw class A; 1 is throw class B; 2 is throw class C):\n";
58     }
59     system("pause");  
60 }

 輸出結果:

在多個異常捕獲的嵌套中的 try-catch 搜索過程:

例子:

View Code
 1 #include <iostream>  
 2 using namespace std;  
 3 
 4 void main()  
 5 {
 6     int i;
 7     cout<<"enter a number(0 is throw_int; 1 is throw_double; 2 is throw_char):\n";
 8     while(cin>>i)
 9     {
10         try{
11             try{
12                 switch(i)
13                 {
14                 case 0:
15                     throw 0;
16                     break;
17                 case 1:
18                     throw 1.1;
19                     break;
20                 case 2:
21                     throw 'a';
22                     break;
23                 default:
24                     cout<<"the number is invalid!"<<endl;
25                 }
26             }
27             catch(char)
28             {
29                 cout<<"in the in_catch(char)!"<<endl;
30             }
31             catch(double)
32             {
33                 cout<<"in the catch(double)!"<<endl;
34             }
35         }
36         catch(int)
37         {
38             cout<<"in the catch(int)!"<<endl;
39         }
40         catch(char)
41         {
42             cout<<"in the out_catch(char)!"<<endl;
43         }
44         cout<<"enter a number(0 is throw_int; 1 is throw_double; 2 is throw_char):\n";
45     }
46     system("pause");  
47 }

輸出結果:

 [返回子目錄]

異常機制與函數返回機制區別:

雖然 throw-catch 機制類似於函數參數和函數返回機制,但是還是有寫不同之處.其中之一是函數中的返回語句將控制權返回到調用函數的函數,但是 throw 語句將函數控制權向上返回到第一個這樣的函數:包含能夠捕獲相應異常的 try-catch 組合.

執行流程的區別:

[返回子目錄]

異常捕獲的優點:

  • 把錯誤處理和真正的工作分開來
  • 代碼更易組織更清晰復雜的工作任務更容易實現
  • 毫無疑問更安全了不至於由於一些小的疏忽而使程序意外崩潰了
  • 由於C++中的try catch可以分層嵌套所以它提供了一種方法使得程序的控制流可以安全的跳轉到上層(或者上上層)的錯誤處理模塊中去.(不同於return語句異常處理的控制流是可以安全地跨越一個或多個函數 ) 
  • 還有一個重要的原因就是由於目前需要開發的軟件產品總是變得越來越復雜、越來越龐大如果系統中沒有一個可靠的異常處理模型那必定是一件十分糟糕的局面

[返回子目錄]

異常處理中需要注意的問題:

  • 如果拋出的異常一直沒有函數捕獲(catch),則會一直上傳到c++運行系統那里,導致整個程序的終止.
  • 一般在異常拋出后資源可以正常被釋放,但注意如果在類的構造函數中拋出異常,統是不會調用它的析構函數的,處理方法是:如果在構造函數中要拋出異常,則在拋出前要記得刪除申請的資源.
  • 異常處理僅僅通過類型而不是通過值來匹配的,所以 catch 塊的參數可以沒有參數名稱,只需要參數類型.
  • 函數原型中的異常說明要與實現中的異常說明一致,否則容易引起異常沖突.
  • catch 塊的參數推薦采用地址傳遞而不是值傳遞,不僅可以提高效率,還可以利用對 象的多態性.另外,派生類的異常撲獲要放到父類異常撲獲的前面,否則,派生類的異常無法被撲獲.
  • 編寫異常說明時,要確保派生類成員函數的異常說明和基類成員函數的異常說明一致,即派生類改寫的虛函數的異常說明至少要和對應的基類虛函數的異常說明相同,甚至更加嚴格,更特殊.

 [返回子目錄]

將對象作為異常類型:

可以使用不同的異常類型來區分不同的函數在不同情況下引發的異常另外對象可以攜帶信息程序員可以根據這些信息來確定引發異常的原因.並且建議傳遞異常類型用基類的引用或者指針.因為基類的引用或者指針有一個重要的特性:基類引用或者指針可以執行派生類對象.也就是說當有一個異常類層次結構,並要分別處理不同的異常類型,則使用基類引用將能夠捕獲基類和由基類派生出的派生類異常對象,而使用派生類對象只能捕獲他所屬類以及這個類派生而來的類的對象.

[返回子目錄]

異常規范:

說明:

指出它將可能引發那些類型的異常為此可在函數定義后面加上異常規范

聲明形式:

返回類型 函數名(形參列表)throw(異常類型1異常類型2 ... )

注意:

  • 它告訴編譯器該函數引發那些類型的異常.但是如果以后該函數引發了其他類型異常程序(最終)將調用 abort 函數對這種越權做出反應
  • 使用異常規划提醒閱讀該原型的人該函數可能會引發異常應提供 try 塊和處理程序
  • 如果異常規范中的括號為空則表明該函數不會引發異常

例子:

View Code
 1 #include <iostream>  
 2 using namespace std;  
 3 
 4 double hmean(double x,double y)throw(const char*)
 5 {
 6     if(x == -y)
 7         throw "x == -y isn't allowed!";
 8     return 2.0*x*y/(x+y);
 9 }
10 void main()  
11 {
12     double x(0),y(0),z(0);
13     cout<<"enter two number:"<<endl;
14 
15     while(cin>>x>>y)
16     {
17         try{
18             z = hmean(x,y);
19         }
20         catch(const char* s)
21         {
22             cout<<s<<endl;
23             cout<<"Enter a new pair of numbers:\n";
24             continue;
25         }
26         std::cout<<"Harmonic mean of "<<x<<" and "<<y<<" is "<<z<<endl;
27         std::cout<<"Enter next set of numbers< q to quit >:\n";
28     }
29     cout<<"Bye!\n";
30     system("pause");  
31 }

[返回子目錄]

exception 類:

位於 exception 頭文件中,它可以作用於其他異常類的基類,並且有一個名為 what() 的成員函數,它返回一個字符串,該字符串的特性隨派生類的實現而異.

stdexcept 異常類

頭文件 stdexcept 定義了其他幾個異常類.

首先該類定義了 logic_error 和 runtime_error 類

logic_error 異常系列描述了典型的邏輯錯誤.其派生類有:

    • domain_error:    數學函數有定義域(domain)和值域(range).定義域有參數的可能取值組成,值域有函數可能返回值組成,當參數值域不在定義域中便會引發 domain_error 異常
    • invalid_argument:   指出給函數傳遞一個意料之外的值
    • length_error:   用於指出沒有足夠的空間來執行所需要的操作
    • out_of_bounds:  通常用於指示索引錯誤

runtime_error 異常系列描述了可能在運行期間發生但難以預見和防范的錯誤.其派生類有:

    • range_error :   沒有發生上溢和下溢的情況下,計算結果不在范圍之內的錯誤
    • overflow_error:  上溢錯誤
    • underflow_error:  下溢錯誤

[返回子目錄]

bad_alloc 異常類:

在處理使用 new 是可能出現的內存分配問題. C++ 提供了兩種可能選擇的方式.實現只能有一種方式.但也可以使用編譯器開關或者其他一些方法,讓編程者能夠選擇喜歡的方式

  • 讓 new 在無法滿足內存請求時返回一個空指針.
  • 讓 new 引發 bad_alloc 異常,頭文件 new (以前名為 new.h) 中包含 bad_alloc 類聲明,它是從 exception 類公有派生而來的

 [返回目錄]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM