您好,登錄后才能下訂單哦!
我們在上節博客中講了 C 語言中的異常處理,今天我們就來講下 C++ 中的異常處理。在 C++ 中內置異常處理的語法元素 try ... catch ...,try 語句處理正常代碼邏輯,catch 語句處理異常情況,try 語句中的異常由相對應的 catch 語句處理。C++ 通過 throw 語句拋出異常信息,throw 拋出的異常必須被 catch 處理,當前函數能夠處理異常,程序繼續往下執行;當前函數無法處理異常,則函數停止執行并返回。未被處理的異常會順著函數調用棧向上傳播,直到被處理為止,否則程序將停止執行。如下
下來我們就以代碼為例來進行分析
#include <iostream> #include <string> using namespace std; double divide(double a, double b) { const double delta = 0.000000000000001; double ret = 0; if( !((-delta < b) && (b < delta)) ) { ret = a / b; } else { throw 0; } return ret; } int main() { try { double r = divide(1, 0); cout << "r = " << r << endl; } catch(...) { cout << "Divided by zero..." << endl; } return 0; }
我們來看看編譯結果
我們再來試試 1/1 呢
已經正確實現了哈。C++ 的這個異常處理是不是很方便呢。同一個 try 語句是可以跟上多個 catch 語句的。catch 語句可以定義具體處理的異常類型,不同類型的異常由不同的 catch 語句負責處理;try 語句中可以拋出任何類型的異常,catch(...) 用于處理所有類型的異常,任何異常都只能被捕獲(catch)一次。下來我們來看看異常處理的匹配規則,如下
下來我們還是以代碼為例來進行說明
#include <iostream> #include <string> using namespace std; void Demo1() { try { throw 0; } catch(char c) { cout << "catch(char c)" << endl; } catch(short c) { cout << "catch(short c)" << endl; } catch(double c) { cout << "catch(double c)" << endl; } catch(int c) { cout << "catch(int c)" << endl; } } void Demo2() { throw "D.T.Software"; } int main() { try { Demo1(); //Demo2(); } catch(char* s) { cout << "catch(char* s)" << endl; } catch(const char* cs) { cout << "catch(const char* cs)" << endl; } catch(string ss) { cout << "catch(string ss)" << endl; } return 0; }
我們來看看會打印出什么
我們看到直接在最后匹配到了 int,因為拋出的 0 默認類型為 int,它不會進行默認類型的轉換。我們再來看看 Demo2 會打印出什么
因為字符串是字面量,所以它會匹配到 const char* cs 上,如果我們在 Demo2 函數中拋出的是 string("D.T.Software");看看會打印出什么
便會打印出字符串了。那么在 catch語句中我們還可以拋出異常,如下
那么我們為什么要在 catch 語句中重新拋出異常呢?catch 中捕獲的異常可以被重新解釋后拋出,在工程開發中使用這樣的方式統一異常類型,如下
那么我們還是以代碼為例來進行講解
#include <iostream> #include <string> using namespace std; void Demo() { try { try { throw 'c'; } catch(int i) { cout << "Inner: catch(int i)" << endl; throw i; } catch(...) { cout << "Inner: catch(...)" << endl; throw; } } catch(...) { cout << "Outer: catch(...)" << endl; } } /* 假設: 當前的函數式第三方庫中的函數,因此,我們無法修改源代碼 函數名: void func(int i) 拋出異常的類型: int -1 ==> 參數異常 -2 ==> 運行異常 -3 ==> 超時異常 */ void func(int i) { if( i < 0 ) { throw -1; } if( i > 100 ) { throw -2; } if( i == 11 ) { throw -3; } cout << "Run func..." << endl; } void MyFunc(int i) { try { func(i); } catch(int i) { switch(i) { case -1: throw "Invalid Parameter"; break; case -2: throw "Runtime Exception"; break; case -3: throw "Timeout Exception"; break; } } } int main() { Demo(); /* try { MyFunc(11); } catch(const char* cs) { cout << "Exception Info: " << cs << endl; } */ return 0; }
我們先以 Demo 函數為例來進行分析,在 try 語句中的 try 語句里拋出 c,匹配到 catch(...) 語句中,先打印出 Inner: catch(...),再次拋出。匹配到外面的 catch(...) 語句中,先打印出 Outer: catch(...)。我們來看看結果
我們看到和我們的分析是完全一致的。接下來的 func 函數就比如是第三方的源碼,我們得根據這個功能寫個一個屬于我們自己的 func 函數。我們改寫完之后是不是就一目了然呢?比如沒改寫之前,拋出個 11 的異常,對應的便會打印出 -3,我們還得去查這個 -3 代表啥意思。我們來注釋掉 Demo 函數,看看下面的編譯結果
輸出結果一目了然,直接看到是超時異常。那么我們便直接定位到了問題,這樣效率便會提高。在 C++ 中,異常的類型可以是自定義類類型,對于類類型異常的匹配依舊是至上而下嚴格匹配,賦值兼容性原則在異常匹配中依然適用。一般而言,將匹配子類異常的 catch 放在上部,匹配父類異常的 catch 放在下部。在工程中會定義一系列的異常類,每個類代表工程中可能出現的一種異常類型。代碼復用時可能需要解釋不同的異常類,在定義 catch 語句時需要推薦使用引用作為參數。
接下來我們還是以代碼為例來進行分析
#include <iostream> #include <string> using namespace std; class Base { }; class Exception : public Base { int m_id; string m_desc; public: Exception(int id, string desc) { m_id = id; m_desc = desc; } int id() const { return m_id; } string description() const { return m_desc; } }; /* 假設: 當前的函數式第三方庫中的函數,因此,我們無法修改源代碼 函數名: void func(int i) 拋出異常的類型: int -1 ==> 參數異常 -2 ==> 運行異常 -3 ==> 超時異常 */ void func(int i) { if( i < 0 ) { throw -1; } if( i > 100 ) { throw -2; } if( i == 11 ) { throw -3; } cout << "Run func..." << endl; } void MyFunc(int i) { try { func(i); } catch(int i) { switch(i) { case -1: throw Exception(-1, "Invalid Parameter"); break; case -2: throw Exception(-2, "Runtime Exception"); break; case -3: throw Exception(-3, "Timeout Exception"); break; } } } int main() { try { MyFunc(11); } catch(const Exception& e) { cout << "Exception Info: " << endl; cout << " ID: " << e.id() << endl; cout << " Description: " << e.description() << endl; } catch(const Base& e) { cout << "catch(const Base& e)" << endl; } return 0; }
我們看到定義了兩個類,在類 Exception 中定義了 id 和 description 用來描述他們的信息,再在 MyFunc 函數中生成臨時對象 Exception 用來獲取他們的信息,我們來看看編譯結果
這樣的信息是不是更加直觀呢。如果我們將上面的 catch 語句中的父類放在子類前面呢,看看結果
我們看到編譯已經警告了,運行后它打印的是父類的信息,因為它同樣遵循賦值兼容性原則。我們在之前說的,將匹配子類異常的 catch 放在上部,匹配父類異常的 catch 放在下部。一定要遵循這個規則。在 C++ 標準庫中提供了實用異常類族,標準庫中的異常都是從 exception 類派生的,exception 類有兩個主要的分支:a> logic_error 常用于程序中可避免邏輯錯誤;b> runtime_error 常用于程序中無法避免的惡性錯誤。下圖是標準庫中的異常類關系
通過對異常的學習,總結如下:1、C++ 中直接支持異常處理的概念;2、try...catch..是 C++ 中異常處理的專用語句;3、try 語句處理正常代碼邏輯,catch 語句處理異常情況,同一個 try 語句可以跟上多個 catch 語句;4、異常處理必須嚴格匹配,不進行任何的轉換;5、catch 語句塊中可以拋出異常,異常的類型可以是自定義類類型;6、賦值兼容性原則在異常匹配中依然適用;7、標準庫中的異常都是從 exception 類派生的。
歡迎大家一起來學習 C++ 語言,可以加我QQ:243343083。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。