上文簡述了在C語言中異常的處理機制,本文主要講解C++中的異常處理.
一、異常的語法格式
在C++中,異常的拋出和處理主要使用了以下三個關鍵字:try、 throw 、 catch.其格式如下:
當我們在程序中想拋出一個異常時,可以這樣:
#include<iostream> #include<exception> using namespace std; int Div(int left,int right){ if(right==0){ throw exception("除數不能為0"); } return left/right; }
當我們想使用這個函數時,需要在函數外部進行異常的捕獲:
int main(){ try{ Div(10,20); //合法 Div(10,30); //合法 Div(10,0); //非法,會拋出異常 }catch(exception & e){ e.what(); //打印異常信息 } return 0; }
如果存在不同類型的異常,我們可以這樣:
try{ //包含可能拋出異常的語句; }catch(類型名 [形參名]){ //可能出現的異常1 }catch(類型名 [形參名]){ //可能出現的異常2 }catch(...){ //如果不確定異常類型,在這里可以捕獲所有類型異常! }
二、異常拋出與捕獲
1.異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪部分代碼.
就上述代碼來說,我們throw了一個exception對象,因此在捕獲異常時,最終會匹配到catch到exception的代碼塊.
2.被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出位置最近的那個.
當try內的代碼塊出現異常時,系統會根據catch的順序和參數的匹配程度來選擇執行哪個代碼塊,因此,系統會選擇最靠前且參數越匹配的代碼塊.
3. 拋出異常后會釋放局部存儲對象,所以被拋出的對象也就還給系統了,throw表達式會初始化一個拋出特殊的異常對象副本(匿名對象),異常對象由編譯管理,異常對象在傳給對應的catch處理之后撤銷。
也就是說,在上述的除法代碼中,我們throw出的對象在拋出異常后會還給操作系統,而throw表達式會自己初始化一個匿名的對象副本,在傳給catch相應的代碼塊后被回收.
三、棧展開
當我們的程序拋出異常時,會先暫停當前函數的執行,開始查找對應匹配的catch語句.
首先會檢查throw是否在代碼塊內部.
如果是的話,再去找匹配的catch子句.
如果有匹配的,則進行處理.
如果沒有,則退出當前函數棧,繼續在調用函數的棧中進行從查找.
不斷重復上述過程.
如果到達main函數棧,依舊沒有匹配,則直接終止程序.
上述沿着調用鏈查找匹配的catch子句的過程稱之為棧展開.
找到匹配的catch子句並處理以后,會繼續沿着catch子句后面繼續執行.
四、異常捕獲的匹配規則
異常對象類型與catch說明符的類型必須完全匹配,只有以下幾種情況例外:
1.允許從非const對象到const類型對象的轉換.
2.允許派生類型到基類類型的轉換.
3.將數組轉換為指向數組類型的指針,將函數轉換為指向函數的指針.
五、異常規范
在函數聲明之后,列出該函數可能拋出異常類型,並保證該函數不會拋出其他類型的異常.
1、成員函數在類內聲明和類外定義兩處必須有相同的異常規范.
2、函數拋出一個沒有被列在它異常規范中的異常時(且函數中拋出異常沒有在函數內部進行處理),系統調用C++標准庫中定義的函數unexpected().
3、如果異常規范為throw(),則表示不得拋出任何異常,該函數不用放在try塊中.
4、派生類的虛函數的異常規范必須與基類虛函數的異常規范一樣或更嚴格(是基類虛函數的異常的子集).
(因為派生類的虛函數被指向基類類型的指針調用時,保證不會違背基類成員函數的異常規范.)
六、異常與構造&析構函數
1. 構造函數完成對象的構造和初始化,需要保證不要在構造函數中拋出異常,否則可能導致對象不完整或沒有完全初始化.
2. 析構函數主要完成資源的清理,需要保證不要在析構函數內拋出異常,否則可能導致資源泄漏(內存泄漏、句柄未關閉等).
七、自定義異常類型
自定義異常類型代碼:
class Exception : public exception{ public: Exception(int errId = 0,const char* errMsg = "") :_errId(errId) ,_errMsg(errMsg){} public: virtual const char* what()const{ cout<<"errId:"<<_errId<<endl; cout<<"errMsg:"<<_errMsg.c_str()<<endl; return _errMsg.c_str(); } private: int _errId; string _errMsg; }
測試代碼:
void TestException(){ throw Exception(1,"錯誤!"); } int main(){ try{ TestException(); }catch(exception &e){ e.what(); } }
關於C++異常處理這一塊的知識暫時先整理到這里,感謝各位讀者支持!