您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關C++中怎么使用trythrowcatch進行異常處理,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
C++異常處理基本語法
C++ 通過 throw 語句和 try…catch 語句實現對異常的處理。throw 語句的語法如下:
throw 表達式;
該語句拋出一個異常。異常是一個表達式,其值的類型可以是基本類型,也可以是類。
try…catch 語句的語法如下:
try { 語句組}catch(異常類型) { 異常處理代碼}...catch(異常類型) { 異常處理代碼}
catch 可以有多個,但至少要有一個。
不妨把 try 和其后{}中的內容稱作“try塊”,把 catch 和其后{}中的內容稱作“catch塊”。
try…catch 語句的執行過程是:
執行 try 塊中的語句,如果執行的過程中沒有異常拋出,那么執行完后就執行最后一個 catch 塊后面的語句,所有 catch 塊中的語句都不會被執行; 如果 try 塊執行的過程中拋出了異常,那么拋出異常后立即跳轉到第一個“異常類型”和拋出的異常類型匹配的 catch 塊中執行(稱作異常被該 catch 塊“捕獲”),執行完后再跳轉到最后一個 catch 塊后面繼續執行。
例如下面的程序:
#include <iostream>using namespace std;int main(){ double m ,n; cin >> m >> n; try { cout << "before piding." << endl; if( n == 0) throw -1; //拋出int類型異常 else cout << m / n << endl; cout << "after piding." << endl; } catch(double d) { cout << "catch(double) " << d << endl; } catch(int e) { cout << "catch(int) " << e << endl; } cout << "finished" << endl; return 0;}
程序的運行結果如下:
9 6↙before piding.1.5after piding.finished
說明當 n 不為 0 時,try 塊中不會拋出異常。因此程序在 try 塊正常執行完后,越過所有的 catch 塊繼續執行,catch 塊一個也不會執行。
程序的運行結果也可能如下:
9 0↙before piding.catch\(int) -1finished
當 n 為 0 時,try 塊中會拋出一個整型異常。拋出異常后,try 塊立即停止執行。該整型異常會被類型匹配的第一個 catch 塊捕獲,即進入 catch(int e) 塊執行,該 catch 塊執行完畢后,程序繼續往后執行,直到正常結束。
如果拋出的異常沒有被 catch 塊捕獲,例如,將catch(int e),改為catch(char e),當輸入的 n 為 0 時,拋出的整型異常就沒有 catch 塊能捕獲,這個異常也就得不到處理,那么程序就會立即中止,try…catch 后面的內容都不會被執行。
能夠捕獲任何異常的 catch 語句
如果希望不論拋出哪種類型的異常都能捕獲,可以編寫如下 catch 塊:
catch(...) { ...}
這樣的 catch 塊能夠捕獲任何還沒有被捕獲的異常。例如下面的程序:
#include <iostream>using namespace std;int main(){ double m, n; cin >> m >> n; try { cout << "before piding." << endl; if (n == 0) throw - 1; //拋出整型異常 else if (m == 0) throw - 1.0; //拋出 double 型異常 else cout << m / n << endl; cout << "after piding." << endl; } catch (double d) { cout << "catch (double)" << d << endl; } catch (...) { cout << "catch (...)" << endl; } cout << "finished" << endl; return 0;}
程序的運行結果如下:
9 0↙before piding.catch (...)finished
當 n 為 0 時,拋出的整型異常被catchy(...)捕獲。
程序的運行結果也可能如下:
0 6↙before piding.catch (double) -1finished
當 m 為 0 時,拋出一個 double 類型的異常。雖然catch (double)和catch(...)都能匹配該異常,但是catch(double)是第一個能匹配的 catch 塊,因此會執行它,而不會執行catch(...)塊。
由于catch(...)能匹配任何類型的異常,它后面的 catch 塊實際上就不起作用,因此不要將它寫在其他 catch 塊前面。
異常的再拋出
如果一個函數在執行過程中拋出的異常在本函數內就被 catch 塊捕獲并處理,那么該異常就不會拋給這個函數的調用者(也稱為“上一層的函數”);如果異常在本函數中沒有被處理,則它就會被拋給上一層的函數。
例如下面的程序:
#include <iostream>#include <string>using namespace std;class CException{public: string msg; CException(string s) : msg(s) {}};double Devide(double x, double y){ if (y == 0) throw CException("devided by zero"); cout << "in Devide" << endl; return x / y;}int CountTax(int salary){ try { if (salary < 0) throw - 1; cout << "counting tax" << endl; } catch (int) { cout << "salary < 0" << endl; } cout << "tax counted" << endl; return salary * 0.15;}int main(){ double f = 1.2; try { CountTax(-1); f = Devide(3, 0); cout << "end of try block" << endl; } catch (CException e) { cout << e.msg << endl; } cout << "f = " << f << endl; cout << "finished" << endl; return 0;}
程序的輸出結果如下:
salary < 0tax counteddevided by zerof=1.2finished
CountTa 函數拋出異常后自行處理,這個異常就不會繼續被拋給調用者,即 main 函數。因此在 main 函數的 try 塊中,CountTax 之后的語句還能正常執行,即會執行f = Devide(3, 0);。
第 35 行,Devide 函數拋出了異常卻不處理,該異常就會被拋給 Devide 函數的調用者,即 main 函數。拋出此異常后,Devide 函數立即結束,第 14 行不會被執行,函數也不會返回一個值,這從第 35 行 f 的值不會被修改可以看出。
Devide 函數中拋出的異常被 main 函數中類型匹配的 catch 塊捕獲。第 38 行中的 e 對象是用復制構造函數初始化的。
如果拋出的異常是派生類的對象,而 catch 塊的異常類型是基類,那么這兩者也能夠匹配,因為派生類對象也是基類對象。
雖然函數也可以通過返回值或者傳引用的參數通知調用者發生了異常,但采用這種方式的話,每次調用函數時都要判斷是否發生了異常,這在函數被多處調用時比較麻煩。有了異常處理機制,可以將多處函數調用都寫在一個 try 塊中,任何一處調用發生異常都會被匹配的 catch 塊捕獲并處理,也就不需要每次調用后都判斷是否發生了異常。
有時,雖然在函數中對異常進行了處理,但是還是希望能夠通知調用者,以便讓調用者知道發生了異常,從而可以作進一步的處理。在 catch 塊中拋出異常可以滿足這種需要。例如:
#include <iostream>#include <string>using namespace std;int CountTax(int salary){ try { if( salary < 0 ) throw string("zero salary"); cout << "counting tax" << endl; } catch (string s ) { cout << "CountTax error : " << s << endl; throw; //繼續拋出捕獲的異常 } cout << "tax counted" << endl; return salary * 0.15;}int main(){ double f = 1.2; try { CountTax(-1); cout << "end of try block" << endl; } catch(string s) { cout << s << endl; } cout << "finished" << endl; return 0;}
程序的輸出結果如下:
CountTax error:zero salaryzero salaryfinished
第 14 行的throw;沒有指明拋出什么樣的異常,因此拋出的就是 catch 塊捕獲到的異常,即 string("zero salary")。這個異常會被 main 函數中的 catch 塊捕獲。
函數的異常聲明列表
為了增強程序的可讀性和可維護性,使程序員在使用一個函數時就能看出這個函數可能會拋出哪些異常,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++標準異常類
C++ 標準庫中有一些類代表異常,這些類都是從 exception 類派生而來的。常用的幾個異常類如圖 1 所示。
圖1:常用的異常類
bad_typeid、bad_cast、bad_alloc、ios_base::failure、out_of_range 都是 exception 類的派生類。C++ 程序在碰到某些異常時,即使程序中沒有寫 throw 語句,也會自動拋出上述異常類的對象。這些異常類還都有名為 what 的成員函數,返回字符串形式的異常描述信息。使用這些異常類需要包含頭文件 stdexcept。
下面分別介紹以上幾個異常類。本節程序的輸出以 Visual Studio 2010為準,Dev C++ 編譯的程序輸出有所不同。
1) bad_typeid
使用 typeid 運算符時,如果其操作數是一個多態類的指針,而該指針的值為 NULL,則會拋出此異常。
2) bad_cast
在用 dynamic_cast 進行從多態基類對象(或引用)到派生類的引用的強制類型轉換時,如果轉換是不安全的,則會拋出此異常。程序示例如下:
#include <iostream>#include <stdexcept>using namespace std;class Base{ virtual void func() {}};class Derived : public Base{public: void Print() {}};void PrintObj(Base & b){ try { Derived & rd = dynamic_cast <Derived &>(b); //此轉換若不安全,會拋出 bad_cast 異常 rd.Print(); } catch (bad_cast & e) { cerr << e.what() << endl; }}int main(){ Base b; PrintObj(b); 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 allocationios_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> subscriptinvalid string position
如果將v.at(100)換成v[100],將s.at(100)換成s[100],程序就不會引發異常(但可能導致程序崩潰)。因為 at 成員函數會檢測下標越界并拋出異常,而 operator[] 則不會。operator [] 相比 at 的好處就是不用判斷下標是否越界,因此執行速度更快。
以上就是C++中怎么使用trythrowcatch進行異常處理,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。