二、c++中的異常處理方式(try ... catch ...)c++
五、try..catch 另類寫法 和 函數異常聲明/定義 throw()函數
「異常」是咱們在程序開發中必須考慮的一些特殊狀況,是程序運行時就可預料的執行分支(注:異常是不可避免的,如程序運行時產生除 0 的狀況;打開的外部文件不存在;數組訪問的越界等等);測試
「Bug」是程序的缺陷,是程序運行時不被預期的運行方式(注:Bug是人爲的、可避免的;如使用野指針;堆數組使用結束後未釋放等等);優化
不管是「異常」仍是「Bug」,都是程序正常運行過程當中可能出現的意外狀況。區別是「異常」能夠捕獲,並作出合適的處理,「Bug"所帶來的後果是沒法預測的,須要重寫相應的代碼。this
1 // 異常基本語法 2 try 3 { 4 // 可能產生異常的代碼,若發生異常,經過 throw 關鍵字拋出異常 5 } 6 catch(異常類型) // 異常捕獲,注(...)表明捕獲全部異常 7 { 8 // 處理異常的代碼,該異常由try語句塊產生 9 }
1)同一個 try 語句能夠跟上多個 catch 語句(在工程中,將可能產生異常的代碼放在 try 語句塊中,而後後面跟上多個 catch 語句);spa
2)try 語句中經過 throw 關鍵字 能夠拋出任何類型的異常(int、字符串、對象等等); 指針
3)catch 語句能夠定義具體處理的異常類型,如 catch( int ) 只捕獲 int 類型異常, 但沒法進一步獲取異常信息;catch( int a ) 只捕獲 int 類型異常,能夠進一步獲取異常信息;
4)不一樣類型的異常由不一樣的 catch 語句負責處理;
5)catch(...) 用於處理全部類型的異常(只能放在全部 catch語句 的後面);
6)任何異常都只能被捕獲(catch)一次;
7)只要被 catch 捕獲一次,其它的 catch 就沒有捕獲機會了;
8)throw 拋出異常的類型 與 catch 捕獲異常的類型 必須嚴格匹配(不能進行類型轉換);若匹配成功,則可以捕獲該異常;不然捕獲失敗,程序中止執行。
其實,爲了更好的理解 異常拋出 和 異常捕獲 這兩個動做,咱們能夠將其想象成 函數調用,拋出異常比如是函數中實參,捕獲異常比如是函數中形參,只有當實參的類型 與 形參的類型嚴格匹配時,此次捕獲才能成功。爲何說想象成函數調用,而不是正真的函數調用呢?緣由就是:函數調用時,用實參初始化形參時能夠進行隱式的類型轉換;可是在異常捕獲時,必須嚴格匹配異常類型。
狀況1:異常代碼沒有放到 try{ } 語句中,這也意味着沒有對應的 catch 語句,其實就是普通函數調用,若此時拋出異常(throw),則程序中止執行;
狀況2:異常代碼放到 try{ throw exception... } 語句中,這也意味着有對應的 catch 語句,則拋出異常時就會與 catch語句嚴格匹配;若匹配成功,則能夠捕獲該異常,不然不能捕獲該異常,程序中止執行。
throw 拋出異常後,在發生異常的函數中至上而下的嚴格匹配每個 catch 語句捕獲的異常類型,來判斷是否可以捕獲該異常;
1) 若發生異常的函數中 可以捕獲該異常,則程序接着往下執行;
2) 若發生異常的函數中 不能捕獲該異常,則未被處理的異常會順着函數調用棧向上傳播,直到該異常被捕獲爲止,不然程序將中止執行;
總結:throw 拋出的異常必須被 對應的 catch 捕獲,不然程序將中止執行;
經過上圖來講明異常拋出後的執行順序:
1)在 函數 function3 中 拋出異常 throw 1;可是在 function3 中並不能捕獲該異常,則異常繼續向外層函數 function2 拋出
2)在 函數 function2 中,異常依舊沒有被捕獲,則異常繼續向外層函數 function1 拋出;
3)在 函數 function1 中,異常被 catch 捕獲和處理,而後程序繼續向下執行;
注:若在 函數 function1 中,異常仍是沒有被捕獲,則異常會一直向外層函數拋出,直到該異常被捕獲爲止,否在程序中止執行。
代碼展現:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 double divide(double a, double b) 7 { 8 const double delta = 0.000000001; 9 double ret = 0; 10 11 if( !((-delta < b) && (b < delta)) ) 12 { 13 ret = a / b; 14 } 15 else 16 { 17 throw "Divided by zero..."; 18 } 19 20 cout << "divide(double a, double b)" << endl; 21 22 return ret; 23 } 24 25 double ExceptionFunc() 26 { 27 double d = divide(2, 0); 28 29 cout << "ExceptionFunc()" << endl; 30 31 return d; 32 } 33 34 int main(int argc, char *argv[]) 35 { 36 double d = ExceptionFunc(); 37 38 cout << "result = " << d << endl; 39 40 return 0; 41 } 42 43 /** 44 * 運行結果: 45 * terminate called after throwing an instance of 'char const*' 46 * Aborted (core dumped) 47 */ 48 49 /** 50 * 分析: 51 * throw "Divided by zero..."; 拋出異常後,divide(double a, double b) 函數不能捕獲該異常,則異常繼續拋給 ExceptionFunc(); 52 * 在 ExceptionFunc() 中,也不能捕獲該異常,則異常繼續拋給 main(); 53 * 在 main()中,也不能捕獲該異常,則程序中止執行。 54 */
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 double divide(double a, double b) 7 { 8 const double delta = 0.000000001; 9 double ret = 0; 10 11 try 12 { 13 if( !((-delta < b) && (b < delta)) ) 14 { 15 ret = a / b; 16 } 17 else 18 { 19 throw "Divided by zero..."; 20 } 21 } 22 catch(char const* s) 23 { 24 cout << s << endl; 25 } 26 27 cout << "divide(double a, double b)" << endl; 28 29 return ret; 30 } 31 32 double ExceptionFunc() 33 { 34 double d = divide(2, 0); 35 36 cout << "ExceptionFunc()" << endl; 37 38 return d; 39 } 40 41 int main(int argc, char *argv[]) 42 { 43 ExceptionFunc(); 44 45 cout << "test end!" << endl; 46 47 return 0; 48 } 49 50 /** 51 * 運行結果: 52 * Divided by zero... 53 * divide(double a, double b) 54 * ExceptionFunc() 55 * test end! 56 */ 57 58 /** 59 * 分析: 60 * throw "Divided by zero..."; 拋出異常後,在divide(double a, double b) 中,異常被捕獲,則程序繼續向下執行; 61 * catch(char const* s) // throw "Divided by zero..." 62 * { 63 * cout << s << endl; 64 * } 65 * cout << "divide(double a, double b)" << endl; 66 * 67 * divide(2, 0); 函數調用結束,返回到 ExceptionFunc() 中, 68 * cout << "ExceptionFunc()" << endl; 69 * 70 * ExceptionFunc()調用結束,返回 main()中,繼續向下執行; 71 * cout << "test end!" << endl; 72 */
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 double divide(double a, double b) 7 { 8 const double delta = 0.000000001; 9 double ret = 0; 10 11 if( !((-delta < b) && (b < delta)) ) 12 { 13 ret = a / b; 14 } 15 else 16 { 17 throw "Divided by zero..."; 18 } 19 20 cout << "divide(double a, double b)" << endl; 21 22 return ret; 23 } 24 25 double ExceptionFunc() 26 { 27 double d; 28 29 try 30 { 31 d = divide(2, 0); 32 33 cout << "d = " << d << endl; 34 } 35 catch(char const* s) 36 { 37 cout << s << endl; 38 } 39 40 cout << "ExceptionFunc()" << endl; 41 42 return d; 43 } 44 45 int main(int argc, char *argv[]) 46 { 47 ExceptionFunc(); 48 49 cout << "test end!" << endl; 50 51 return 0; 52 } 53 54 /** 55 * 運行結果: 56 * Divided by zero... 57 * ExceptionFunc() 58 * test end! 59 */ 60 61 /** 62 * 分析: 63 * throw "Divided by zero..."; 拋出異常後,divide(double a, double b) 函數不能捕獲該異常,則異常繼續拋給 ExceptionFunc(); 64 * 在 ExceptionFunc() 中,異常被捕獲,則程序繼續向下執行; 65 * catch(char const* s) // throw "Divided by zero..." 66 * { 67 * cout << s << endl; 68 * } 69 * cout << "ExceptionFunc()" << endl; 70 * ExceptionFunc()調用結束,返回 main()中,繼續向下執行; 71 * cout << "test end!" << endl; 72 */
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 double divide(double a, double b) 7 { 8 const double delta = 0.000000001; 9 double ret = 0; 10 11 if( !((-delta < b) && (b < delta)) ) 12 { 13 ret = a / b; 14 } 15 else 16 { 17 throw "Divided by zero..."; 18 } 19 20 cout << "divide(double a, double b)" << endl; 21 22 return ret; 23 } 24 25 double ExceptionFunc() 26 { 27 double d = divide(2, 0); 28 29 cout << "ExceptionFunc()" << endl; 30 31 return d; 32 } 33 34 int main(int argc, char *argv[]) 35 { 36 try 37 { 38 double d = ExceptionFunc(); 39 40 cout << "d = " << d << endl; 41 } 42 catch(char const* s) 43 { 44 cout << s << endl; 45 } 46 47 cout << "test end!" << endl; 48 49 return 0; 50 } 51 52 /** 53 * 運行結果: 54 * Divided by zero... 55 * test end! 56 */ 57 58 /** 59 * 分析: 60 * throw "Divided by zero..."; 拋出異常後,divide(double a, double b) 函數不能捕獲該異常,則異常繼續拋給 ExceptionFunc(); 61 * 在 ExceptionFunc() 中,也不能捕獲該異常,則異常繼續拋給 main(); 62 * 在 main()中,異常與被捕獲,則程序繼續向下執行 63 * catch(char const* s) // throw "Divided by zero..." 64 * { 65 * cout << s << endl; 66 * } 67 * cout << "test end!" << endl; 68 */
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 void Demo1() 7 { 8 try 9 { 10 throw 2.0; 11 } 12 catch(char c) 13 { 14 cout << "catch(char c)" << endl; 15 } 16 catch(short c) 17 { 18 cout << "catch(short c)" << endl; 19 } 20 catch(double c) // throw 2.0; 21 { 22 cout << "catch(double c)" << endl; 23 } 24 catch(...) // 表示捕獲任意類型的異常 25 { 26 cout << "catch(...)" << endl; 27 } 28 } 29 30 void Demo2() 31 { 32 throw string("D.T.Software"); 33 } 34 35 int main(int argc, char *argv[]) 36 { 37 Demo1(); 38 39 try 40 { 41 Demo2(); 42 } 43 catch(char* s) 44 { 45 cout << "catch(char *s)" << endl; 46 } 47 catch(const char* cs) 48 { 49 cout << "catch(const char *cs)" << endl; 50 } 51 catch(string ss) // throw string("D.T.Software"); 52 { 53 cout << "catch(string ss)" << endl; 54 } 55 56 return 0; 57 } 58 /** 59 * 運行結果: 60 * catch(double c) 61 * catch(string ss) 62 */ 63 64 // 結論:異常類型嚴格匹配,(...)表示捕獲任意類型的異常
總結:
狀況1:只拋出異常,沒有對應地異常捕獲;( 沒有 try ... catch ... 結構 )
狀況2:在try語句塊中拋出異常(直接在try語句塊中使用 throw 拋出異常;或者try語句塊中放入有異常的函數,間接地在函數中使用 throw 拋出異常),而後經過catch語句捕獲同類型的異常並進行異常處理;( 現象: 一個 try ... catch ... 結構 )
那麼如今咱們考慮能不能在狀況2的基礎上,將捕獲到異常繼續拋出呢?(在 catch 語句塊中從新拋出異常?)
能夠在 catch 語句中從新拋出異常,此時須要外層的 try ... catch ...捕獲這個異常;
注:catch 語句中只拋出異常,什麼也不作;當捕獲任意類型的異常時,直接使用 throw 就行。
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 void Demo() 7 { 8 try 9 { 10 throw 'c'; 11 } 12 catch(int i) 13 { 14 cout << "Inner: catch(int i)" << endl; 15 throw i; 16 } 17 catch(...) 18 { 19 cout << "Inner: catch(...)" << endl; 20 throw; 21 } 22 } 23 24 int main(int argc, char *argv[]) 25 { 26 Demo(); 27 28 return 0; 29 } 30 31 /** 32 * 運行結果: 33 * Inner: catch(...) 34 * terminate called after throwing an instance of 'char' 35 * Aborted (core dumped) 36 */ 37 // 錯誤緣由:異常重解釋以後,沒有被捕獲
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 void Demo() 7 { 8 try 9 { 10 try 11 { 12 throw 'c'; 13 } 14 catch(int i) 15 { 16 cout << "Inner: catch(int i)" << endl; 17 throw i; 18 } 19 catch(...) 20 { 21 cout << "Inner: catch(...)" << endl; 22 throw; 23 } 24 } 25 catch(...) 26 { 27 cout << "Outer: catch(...)" << endl; 28 } 29 } 30 31 int main(int argc, char *argv[]) 32 { 33 Demo(); 34 35 return 0; 36 } 37 /** 38 * 運行結果: 39 * Inner: catch(...) 40 * Outer: catch(...) 41 */
爲何要在 catch 語句塊中從新拋出異常?
在工程中,利用在 catch 語句塊中從新解釋異常並拋出這一特性,能夠統一異常類型。(很晦澀,看下面解釋....)
咱們經過上圖來詳細說明這個狀況:
背景介紹:出於開發效率考慮,在工程開發中通常會基於第三方庫來開發,可是,第三方庫中的某些功能並不完善(可讀性差),此時就須要封裝第三方庫中的這個功能。
在上圖中,因爲第三方庫中 func()函數的異常類型是 int 類型,可讀性不好,不可以直接經過異常結果知道該異常所表明的意思;基於這種狀況,咱們經過私有庫中的 MyFunc()函數對第三方庫中func()進行了封裝,使得 MyFunc()函數中的異常類型能夠顯示更多的異常信息(MyFunc() 函數中的異常類型是自定義類型,能夠是字符串、類類型等等),加強代碼的可讀性;爲了將 func()中的異常類型 和 MyFunc() 中異常類型統一塊兒來,咱們能夠在私有庫中的MyFunc() 函數中去捕獲第三方庫中的 func() 函數拋出的異常,而後根據捕獲到的異常從新解釋爲咱們想要的異常,這樣咱們工程開發中所面對的異常類型就是一致的;接下來咱們用代碼復現這個場景:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 /* 7 假設:當前的函數是第三方庫中的函數,所以,咱們沒法修改源代碼 8 9 函數名: void func(int i) 10 拋出異常的類型: int 11 -1 ==》 參數異常 12 -2 ==》 運行異常 13 -3 ==》 超時異常 14 */ 15 void func(int i) 16 { 17 if( i < 0 ) 18 { 19 throw -1; 20 } 21 22 if( i > 100 ) 23 { 24 throw -2; 25 } 26 27 if( i == 11 ) 28 { 29 throw -3; 30 } 31 32 cout << "Run func..." << endl; 33 } 34 35 int main(int argc, char *argv[]) 36 { 37 try 38 { 39 func(11); 40 } 41 catch(int i) 42 { 43 cout << "Exception Info: " << i << endl; 44 } 45 46 return 0; 47 } 48 49 /** 50 * 運行結果: 51 * Exception Info: -3 52 */ 53 54 // 異常顯示結果太簡單,當發生異常時,須要查詢技術文檔才能知道這兒的-3表明的意思,可讀性不好
// 異常的重解釋,在 catch 語句中從新拋出異常
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 /* 7 假設: 當前的函數式第三方庫中的函數,所以,咱們沒法修改源代碼 8 9 函數名: void func(int i) 10 拋出異常的類型: int 11 -1 ==》 參數異常 12 -2 ==》 運行異常 13 -3 ==》 超時異常 14 */ 15 void func(int i) 16 { 17 if( i < 0 ) 18 { 19 throw -1; 20 } 21 22 if( i > 100 ) 23 { 24 throw -2; 25 } 26 27 if( i == 11 ) 28 { 29 throw -3; 30 } 31 32 cout << "Run func..." << endl; 33 } 34 35 /* 不能修改 func() 函數,則咱們定義 MyFunc() 函數重解釋 func() 的異常 */ 36 void MyFunc(int i) 37 { 38 try 39 { 40 func(i); 41 } 42 catch(int i) 43 { 44 switch(i) 45 { 46 case -1: 47 throw "Invalid Parameter"; // 能夠拋出更詳細的數據,如類對象,後續講解 48 break; 49 case -2: 50 throw "Runtime Exception"; 51 break; 52 case -3: 53 throw "Timeout Exception"; 54 break; 55 } 56 } 57 } 58 59 int main(int argc, char *argv[]) 60 { 61 try 62 { 63 MyFunc(11); 64 } 65 catch(const char* cs) 66 { 67 cout << "Exception Info: " << cs << endl; 68 } 69 70 return 0; 71 } 72 /** 73 * 運行結果: 74 * Exception Info: Timeout Exception 75 */
(1)異常的類型能夠是自定義類類型;
(2)對於類類型異常的匹配依舊是自上而下嚴格匹配;
(3)賦值兼容性原則在異常匹配中依然適用;(注:在賦值兼容性中,子類是特殊的父類,父類能夠捕獲子類的異常;一樣知足異常類型嚴格匹配的原則)
(4)在賦值兼容性原則中,通常將
1)匹配子類異常的 catch 放在上部;
2)匹配父類異常的 catch 放在下部;
(5)在定義 catch 語句塊時,推薦使用 const 引用做爲參數,提升程序的運行效率;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Base 7 { 8 }; 9 10 /* 定義異常類 */ 11 class Exception : public Base 12 { 13 int m_id; 14 string m_desc; 15 public: 16 Exception(int id, string desc) 17 { 18 m_id = id; 19 m_desc = desc; 20 } 21 22 int id() const 23 { 24 return m_id; 25 } 26 27 string description() const 28 { 29 return m_desc; 30 } 31 }; 32 33 /* 34 假設: 當前的函數式第三方庫中的函數,所以,咱們沒法修改源代碼 35 函數名: void func(int i) 36 拋出異常的類型: int 37 -1 ==》 參數異常 38 -2 ==》 運行異常 39 -3 ==》 超時異常 40 */ 41 42 void func(int i) 43 { 44 if( i < 0 ) 45 { 46 throw -1; 47 } 48 49 if( i > 100 ) 50 { 51 throw -2; 52 } 53 54 if( i == 11 ) 55 { 56 throw -3; 57 } 58 59 cout << "Run func..." << endl; 60 } 61 62 /* 使用自定義的類類型來優化 */ 63 void MyFunc(int i) 64 { 65 try 66 { 67 func(i); 68 } 69 catch(int i) 70 { 71 switch(i) 72 { 73 case -1: 74 throw Exception(-1, "Invalid Parameter"); // 直接調用構造函數生成異常對象; 75 break; 76 case -2: 77 throw Exception(-2, "Runtime Exception"); 78 break; 79 case -3: 80 throw Exception(-3, "Timeout Exception"); 81 break; 82 } 83 } 84 } 85 86 int main(int argc, char *argv[]) 87 { 88 try 89 { 90 MyFunc(11); 91 } 92 catch(const Exception& e) // 1 使用 const 引用類型做爲參數 2 賦值兼容性原則(參考第4點) 93 { 94 cout << "Exception Info: " << endl; 95 cout << " ID: " << e.id() << endl; 96 cout << " Description: " << e.description() << endl; 97 } 98 catch(const Base& e) 99 { 100 cout << "catch(const Base& e)" << endl; 101 } 102 103 return 0; 104 } 105 /** 106 * 運行結果: 107 * Exception Info: 108 * ID: -3 109 * Description: Timeout Exception 110 */
(1)C++ 標準庫中提供了實用異常類族;
(2)標準庫中的異常都是從 exception 類派生的;
(3)exception 類有兩個主要的分支:
1)logic_error
2)runtime_error
(4)標準庫中的異常類繼承圖:
1 #include <iostream> 2 #include <string> 3 #include <stdexcept> 4 #include <sstream> 5 6 using namespace std; 7 8 /* 9 __FILE__ __FUNCTION__ const char[] 10 __LINE__ int 11 */ 12 13 template 14 <typename T, int N> 15 class Array 16 { 17 private: 18 T arr[N]; 19 public: 20 Array(); 21 T& operator[] (int index); 22 void print() const; 23 }; 24 25 template 26 <typename T, int N> 27 Array<T, N>::Array() 28 { 29 for(int i = 0; i < N; i++) 30 { 31 arr[i] = 0; 32 } 33 } 34 35 template 36 <typename T, int N> 37 T& Array<T, N>::operator[] (int index) 38 { 39 if( (0 <= index) && (index < N) ) 40 { 41 return arr[index]; 42 } 43 else 44 { 45 ostringstream oss; 46 47 throw out_of_range(string(__FILE__) + ":" + static_cast<ostringstream&>(oss << __LINE__).str() + ":" + __FUNCTION__\ 48 49 + "\t >> exception: the index of array is out of range"); 50 } 51 } 52 53 template 54 <typename T, int N> 55 56 void Array<T, N>::print() const 57 { 58 for(int i = 0; i < N; i++) 59 { 60 cout << arr[i] << " "; 61 } 62 cout << endl; 63 } 64 65 int main(int argc, char *argv[]) 66 { 67 try 68 { 69 Array<int, 5> arr; 70 71 arr.print(); 72 arr[-1] = 1; // 異常測試 73 arr.print(); 74 } 75 76 catch(const out_of_range& e) // 1 使用 const 引用類型做爲參數 2 賦值兼容性原則(參考第4點) 77 { 78 cout << e.what() << endl; 79 } 80 81 catch(...) 82 { 83 cout << "other exception ... " << endl; 84 } 85 86 return 0; 87 } 88 89 /** 90 * 運行結果: 91 * 0 0 0 0 0 92 * test.cpp:47:operator[] >> exception: the index of array is out of range 93 */
1 // 模板文件 Array.hpp 的優化 2 #ifndef ARRAY_H 3 #define ARRAY_H 4 5 #include <stdexcept> // 標準庫中的異常類頭文件; 6 7 using namespace std;A 8 9 template 10 < typename T, int N > 11 class Array 12 { 13 T m_array[N]; 14 public: 15 int length() const; 16 bool set(int index, T value); 17 bool get(int index, T& value); 18 T& operator[] (int index); 19 T operator[] (int index) const; 20 virtual ~Array(); 21 }; 22 23 template 24 < typename T, int N > 25 int Array<T, N>::length() const 26 { 27 return N; 28 } 29 30 template 31 < typename T, int N > 32 bool Array<T, N>::set(int index, T value) 33 { 34 bool ret = (0 <= index) && (index < N); 35 36 if( ret ) 37 { 38 m_array[index] = value; 39 } 40 41 return ret; 42 } 43 44 template 45 < typename T, int N > 46 bool Array<T, N>::get(int index, T& value) 47 { 48 bool ret = (0 <= index) && (index < N); 49 50 if( ret ) 51 { 52 value = m_array[index]; 53 } 54 55 return ret; 56 } 57 58 template 59 < typename T, int N > 60 T& Array<T, N>::operator[] (int index) 61 { 62 if( (0 <= index) && (index < N) ) 63 { 64 return m_array[index]; // 這裏以前沒有驗證 index 是否合法,由於驗證了也沒辦法處理; 65 } 66 else 67 { 68 throw out_of_range("T& Array<T, N>::operator[] (int index)"); 69 } 70 } 71 72 template 73 < typename T, int N > 74 T Array<T, N>::operator[] (int index) const 75 { 76 if( (0 <= index) && (index < N) ) 77 { 78 return m_array[index]; // 這裏以前沒有驗證 index 是否合法,由於驗證了也沒辦法處理; 79 } 80 else 81 { 82 throw out_of_range("T Array<T, N>::operator[] (int index) const"); 83 } 84 } 85 86 template 87 < typename T, int N > 88 Array<T, N>::~Array() 89 { 90 91 } 92 93 #endif 94 95 //------------------------------------------------------------------ 96 97 // 模板文件 HeapArray.hpp 的優化 98 99 #ifndef HEAPARRAY_H 100 #define HEAPARRAY_H 101 102 #include <stdexcept> // 添加標準頭文件; 103 104 using namespace std; 105 106 template 107 < typename T > 108 class HeapArray 109 { 110 private: 111 int m_length; 112 T* m_pointer; 113 114 HeapArray(int len); 115 HeapArray(const HeapArray<T>& obj); 116 bool construct(); 117 public: 118 static HeapArray<T>* NewInstance(int length); 119 int length() const; 120 bool get(int index, T& value); 121 bool set(int index ,T value); 122 T& operator [] (int index); 123 T operator [] (int index) const; 124 HeapArray<T>& self(); 125 const HeapArray<T>& self() const; // 要考慮成員函數有沒有必要成爲 const 函數,const 函數主要是給 cosnt 對象調用; 126 ~HeapArray(); 127 }; 128 129 template 130 < typename T > 131 HeapArray<T>::HeapArray(int len) 132 { 133 m_length = len; 134 } 135 136 template 137 < typename T > 138 bool HeapArray<T>::construct() 139 { 140 m_pointer = new T[m_length]; 141 142 return m_pointer != NULL; 143 } 144 145 template 146 < typename T > 147 HeapArray<T>* HeapArray<T>::NewInstance(int length) 148 { 149 HeapArray<T>* ret = new HeapArray<T>(length); 150 151 if( !(ret && ret->construct()) ) 152 { 153 delete ret; 154 ret = 0; 155 } 156 157 return ret; 158 } 159 160 template 161 < typename T > 162 int HeapArray<T>::length() const 163 { 164 return m_length; 165 } 166 167 template 168 < typename T > 169 bool HeapArray<T>::get(int index, T& value) 170 { 171 bool ret = (0 <= index) && (index < length()); 172 173 if( ret ) 174 { 175 value = m_pointer[index]; 176 } 177 178 return ret; 179 } 180 181 template 182 < typename T > 183 bool HeapArray<T>::set(int index, T value) 184 { 185 bool ret = (0 <= index) && (index < length()); 186 187 if( ret ) 188 { 189 m_pointer[index] = value; 190 } 191 192 return ret; 193 } 194 195 template 196 < typename T > 197 T& HeapArray<T>::operator [] (int index) 198 { 199 if( (0 <= index) && (index < length()) ) 200 { 201 return m_pointer[index]; // 優化這裏,越界拋異常; 202 } 203 else 204 { 205 throw out_of_range("T& HeapArray<T>::operator [] (int index)"); 206 } 207 } 208 209 template 210 < typename T > 211 T HeapArray<T>::operator [] (int index) const 212 { 213 if( (0 <= index) && (index < length()) ) 214 { 215 return m_pointer[index]; // 優化這裏,越界拋異常; 216 } 217 else 218 { 219 throw out_of_range("T HeapArray<T>::operator [] (int index) const"); 220 } 221 } 222 223 template 224 < typename T > 225 HeapArray<T>& HeapArray<T>::self() 226 { 227 return *this; 228 } 229 230 template 231 < typename T > 232 const HeapArray<T>& HeapArray<T>::self() const 233 { 234 return *this; 235 } 236 237 template 238 < typename T > 239 HeapArray<T>::~HeapArray() 240 { 241 delete[]m_pointer; 242 } 243 244 #endif 245 246 //------------------------------------------------------------------ 247 248 // 測試文件 main.cpp 249 250 #include <iostream> 251 #include <string> 252 #include <memory> //for auto_ptr 253 #include "Array.hpp" 254 #include "HeapArray.hpp" 255 256 using namespace std; 257 258 void TestArray() 259 { 260 Array<int, 5> a; 261 262 for(int i=0; i<a.length(); i++) 263 { 264 a[i] = i; 265 } 266 267 for (int i=0; i<a.length(); i++) 268 { 269 cout << a[i] << ", "; 270 } 271 cout << endl; 272 } 273 274 void TestHeapArray() 275 { 276 //使用智能指針,目的是自動釋放堆空間 277 auto_ptr< HeapArray<double> > pa(HeapArray<double>::NewInstance(5)); 278 279 if(pa.get() != NULL) 280 { 281 HeapArray<double>& array = pa->self(); 282 283 for(int i=0; i<array.length(); i++) 284 { 285 array[i] = i; 286 } 287 288 for (int i=0; i<array.length(); i++) 289 { 290 cout << array[i] << ", "; 291 } 292 cout << endl; 293 } 294 } 295 296 int main(int argc, char *argv[]) 297 { 298 try 299 { 300 TestArray(); 301 302 cout << endl; 303 304 TestHeapArray(); 305 } 306 catch(const out_of_range& e) 307 { 308 cout << "exception: " << e.what() << endl; 309 } 310 catch(...) 311 { 312 cout << "other exception ... " << endl; 313 } 314 315 return 0; 316 } 317 318 /** 319 * 運新結果: 320 * 0, 1, 2, 3, 4, 321 * --------------------- 322 * 0, 1, 2, 3, 4, 323 * Run End... 324 */ 325 326 /** 327 * 棧空間數組下標越界異常運行結果: 328 * exception: T& Array<T, N>::operator[] (int index) 329 * Run End... 330 */ 331 332 /** 333 * 堆空間數組下標越界異常運行結果: 334 * 0, 1, 2, 3, 4, 335 * --------------------- 336 * exception: T& HeapArray<T>::operator [] (int index) 337 * Run End... 338 */
try..catch 另類寫法(不建議使用)的特色:
(1)try-catch用於分隔正常功能代碼與異常處理代碼
(2)try-catch能夠直接將函數實現分隔爲2部分
函數異常聲明/定義的特色:
(1)函數聲明和定義時能夠直接指定可能拋出的異常類型;
(2)異常聲明成爲函數的一部分,能夠提升代碼可讀性;
(3)函數異常聲明是一種與編譯器之間的契約;
(4)函數聲明異常後就只能拋出聲明的異常;
A、拋出其它異常將致使程序運行終止;
B、能夠直接經過異常聲明定義無異常函數;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 // int func(int i, int j) throw() // 無異常函數聲明 7 8 int func(int i, int j) throw(int, char) //異常聲明,表示該函數可能拋出int和char兩種類型的異常 9 { 10 if ((0 < j) && (j < 10)) 11 { 12 return (i + j); 13 } 14 else 15 { 16 throw 'c'; // 只能拋出指定的異常類型(int、char),不然程序運行失敗 17 } 18 } 19 20 //如下的寫法已經不被推薦 !!! 21 void test(int i) try //正常代碼 22 { 23 cout << "func(i, i) = " << func(i, i) << endl; 24 } 25 catch (int j) //異常代碼 26 { 27 cout << "Exception: " << j << endl; 28 } 29 catch (char j) //異常代碼 30 { 31 cout << "Exception: " << j << endl; 32 } 33 34 int main(int argc, char *argv[]) 35 { 36 test(5); //正常 37 38 test(10); //拋異常 39 40 return 0; 41 } 42 /** 43 * 運行結果: 44 * func(i, i) = 10 45 * Exception: c 46 */