所有的代碼都有可能不按照預定義的方式運行,成為程序異常。
C++中提供了try和catch語句塊對可能產生異常的代碼進行分開處理:
try語句塊處理正常邏輯catch語句塊處理異常C++語言中通過throw語句引發一個異常。
應用舉例:
在上面的例子中,throw扔出的異常值被catch抓到,catch的參數error就是throw扔出的異常值。
throw語句用于將異?!皩ο蟆睊伋?。 而且throw拋出的異常必須有catch來進行處理。
throw語句將異常拋出,如果在當前函數中沒有try…catch語句能夠處理該異常,則當前函數將立即返回。
異常被傳遞到上層調用函數,仍然需要try…catch語句 進行處理,如果上層函數也沒有能力處理該異常,則異 常繼續向更上層函數的函數傳遞。
如果在函數調用棧中的所有函數都無法處理拋出的異常,則程序異常中止。
同一個try語句塊可以跟上多個catch語句塊。 同一個try語句塊可以拋出多種不同類型的異常。 不同類型的異常由不同的catch語句塊負責處理。 異常被拋出后會自上而下逐一匹配catch語句塊。
異常匹配時,不會進行默認類型轉換。程序會嚴格匹配每個類型,不會類型轉換。
這些異常是通過異常的數據類型進行區分的。
程序代碼舉例:
#include <cstdlib>#include <iostream>using namespace std;int test(int i){ if( i == 1 ) { throw -1;//拋出 int 型異常 } if( i == 2 ) { throw "ERROR";//拋出 字符串 型異常 } if( i == 3 ) { throw 0.5;//拋出 double 型異常 }// if( i == 4 )// {// throw 'd';//拋出 字符char 型異常,但是沒有相應catch,程序退出 // } return i;}int main(int argc, char *argv[]){ for(int i=0; i<5; i++) { try { cout<<test(i)<<endl; } catch(int e) //處理int型異常 { cout<<"Int: "<<e<<endl; } catch(const char* e)//處理字符串型異常 { cout<<"const char*: "<<e<<endl; } catch(double e)//處理double型異常 { cout<<"double: "<<e<<endl; } //沒有處理字符類型的catch,因此不能拋出字符類型的異常 } cout << "PRess the enter key to continue ..."; cin.get(); return EXIT_SUCCESS;}有時在工程中只關心是否產生了異常,而不關心具體的異常類型。
C++中的catch語句可以使用 … 捕獲所有的異常。
如下圖所示:
catch(…)可以捕獲所有異常但卻無法得到異常信息。 catch(…)一般作為最后一個異常處理塊出現。在一些現代的編譯器中,如果catch(…)不是最后一個異常處理,程序會報錯。
看見代碼中的catch就要意識到這里在處理異常情況,而且異常是在對應的try中產生的。
在catch語句塊中仍然可以拋出異常,并且需要到外層 程序的catch中進行處理。
在catch(…)語句塊中,可以通過不帶參數的throw語 句拋出已經捕獲到的捕獲的異常。 如下圖所示:
不要在構造函數中拋出異常。 在構造函數可能申請系統資源,而在構造函數中拋出異常會導致對象構造不完全。 不完全對象的析構函數是不會被調用的,因此可能造成資源泄漏。
如下圖所示: 在上面的例子中,在為p分配了20字節的空間之后,拋出異常,無法析構,浪費了20字節的空間。
在工程中會定義一系列的異常類。 通過繼承,可以得到一個異常類族。 每個類代表工程中可能出現的一種異常類型。 為了避免對象構造與拷貝的開銷,在定義catch語句塊時使用引用作為參數。
在工程中可以使用標準庫中的異常類。 可將標準庫中的異常類作為基類派生新的異常類。 標準庫中的異常都是從exception類派生的。
exception類有兩個主要的分支: logic_error用于描述程序中出現的邏輯錯誤。 如:傳遞無效參數、除數為0 runtime_error用于描述無法預料的事件所造成的錯誤。 如:內存耗盡,硬件錯誤
以上兩個分支都是exception類派生的兩個類。 標準庫中的異常類: logic_ error和runtime_ error都提供了一個參數為字符串的構造函數,這樣就可以保持錯誤信息。 通過 what() 成員函數就可以得到錯誤的信息。
工程應用舉例:
#include <cstdlib>#include <iostream>#include <stdexcept>//異常類 頭文件 using namespace std;//自定義 divide_by_zero(除數為0)異常類//繼承自 logic_error 異常類, 邏輯錯誤 class divide_by_zero : public logic_error{public: divide_by_zero(const char* s) : logic_error(s) { }//自定義構造函數,參數列表為父類構造函數 };double Div(double a, double b){ if( (-0.00000001 < b) && ( b < 0.00000001) ) { throw divide_by_zero("Divide by zero..."); //拋出異常,異常參數為自定義的異常類 } return a / b;}int main(int argc, char *argv[]){ try { cout<<Div(1, 0)<<endl; } catch(exception& e)//賦值兼容性原則,接收所有的異常類 { cout<<e.what()<<endl;//打印出字符串 } cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS;}可以將函數體作為一個完整的try語句塊。
如下圖所示:
函數級try語法可以更好將正常邏輯代碼與異常處理代碼分開,提高代碼的可讀性與維護性。
異常處理是程序中隨處可見的情況。在工程實踐中,大多數的代碼都是用于處理異常的。異常處理的質量直接決定最終產品的質量。
C++提供了 throw語句 和 try…catch語句,用于將正常邏輯代碼與異常處理代碼進行分開處理。
catch(…)可以捕獲所有異常。 catch(…)經常作為最后一個catch語句出現。
不要在構造函數中拋出異常,這樣可能造成資源泄露。
工程中經常以標準庫中的異常類作為項目異常的基礎。
函數級try語句塊能夠更好的提高代碼的維護性。
新聞熱點
疑難解答
圖片精選