c 異常處理的實現

2021-06-16 09:18:07 字數 2917 閱讀 5126

我們編寫的程式一般要滿足正確性、健壯性、易讀性和可復用性、可擴充套件性。健壯性指程式既能處理正確流程的情況,也能處理非法的錯誤的異常情況,提示使用者出現了什麼問題,一般由異常處理實現。在編寫小型的程式時,可以很容易發現程式的錯誤,異常提示顯得不那麼重要,但在大型的由多人共同完成的程式中往往難以發現出現了什麼問題,因此異常提示和處理顯得尤為重要。下面我們了解c++的異常處理的實現。

異常機制提供程式中錯誤檢測和錯誤處理部分之間的通訊。c++的異常處理中包括:

throw表示式:錯誤檢測部分丟擲乙個異常,說明程式遇到了無法處理的問題

try塊:錯誤處理部分使用它處理異常。try語句塊以try關鍵字開始,並以乙個或多個catch子句結束。在try塊中執行的**所丟擲的異常,通常會被其中乙個catch子句處理。

由標準庫定義的一組異常類,用來在throw和catch之間傳遞有關的錯誤資訊。exception標頭檔案定義了最常見的異常類,它的類名是exception,stdexcept標頭檔案定義了幾種常見的異常類,new標頭檔案定義了bad_alloc異常類,提供因無法分配記憶體而由new丟擲的異常,type_info標頭檔案定義了bad_cast異常類。exception,bad_allo,bad_cast只定義了預設建構函式,其他的異常型別則只定義了乙個使用string初始化的建構函式,用於提示錯誤資訊,異常型別只定義了乙個what操作,這個函式沒有引數,返回const char*型別,為該一場提供文字資訊。異常類的繼承層次如下圖所示:

我們來看乙個例子

int ********(int a, int b, int c)

int main()

catch(const runtime_error& e)

cout<

return 0; }

函式int ********(int a, int b, int c)求三角形三邊之和,函式首先判斷這三條邊能否構成乙個三角形,不能就丟擲乙個異常轉入異常處理塊中。

異常是通過丟擲(throw)物件而引發的,該物件的型別決定應該啟用哪個catch子句,被選中的**是呼叫鏈中與該物件型別匹配且離丟擲異常位置最近的的那個。異常以類似於將實參傳遞給函式的方式丟擲和捕獲。

執行throw的時候,不會執行跟在throw後面的語句,而是將控制從throw轉到匹配的catch,該catch可以是同一函式中區域性的catch,也可以在直接或間接呼叫發生異常的函式的另乙個函式中。控制從乙個地方轉移到另乙個地方,有兩個重要含義:

(1)

沿著呼叫鏈的函式提早退出

(2)

一般而言,在處理異常的時候,丟擲異常的塊中的區域性儲存不存在了。

丟擲異常時物件為指標解引用形式時,異常物件的型別與指標的靜態型別相同,不論指標是否指向其派生型別。通常丟擲指標是個壞主意,發生異常時丟擲異常塊的區域性儲存被釋放了。

丟擲異常的的時候,暫停當前函式的執行,開始查詢匹配的catch子句,首先檢查throw本身是否在try塊內,如果是,檢查與該try相關的catch子句,看是否其中之一與被丟擲的物件匹配。如果找到匹配的catch,就處理異常,如果找不到,就退出當前函式,並且繼續在呼叫函式中查詢。這個過程稱為「棧展開」

棧展開期間,提早退出包含throw的函式和相關的呼叫它的函式,每個函式中的區域性物件都會撤銷,如果是類型別的,就呼叫其析構函式。考慮這個問題,如果在呼叫析構函式時跑出了新的異常,編譯器怎麼辦?是取代舊的異常還是忽略新的異常?當然,析構函式應保證不會丟擲異常。

建構函式有可能會丟擲異常,甚至發生在初始化表中。為了處理來自建構函式初始化表的異常,必須將建構函式初始化為函式測試塊。可以使用函式測試塊將一組catch子句與函式連成乙個整體。

templatehandle::t(t* p)

try:ptr(p),use(new size_t(1))

catch(const bad_alloc& e)

如果最終沒有找到匹配的catch子句,則呼叫庫函式terminate.

catch子句的匹配:比實參和形參的匹配規則更嚴格,大部分轉換都不允許,出下面幾個可能的區別之外,允許從非const到const的轉換,允許從派生型別到基類型別的轉換,將陣列轉化為指向陣列型別的指標,將函式轉化為指向函式型別的適當指標。

異常說明符:異常物件本身是丟擲物件的副本,是否再次複製取決於異常說名符(其實就是catch形參)。基類的異常說明符可以捕獲派生類的異常物件,而且,異常說明符的靜態型別決定catch子句可以執行的動作。如果被丟擲的物件型別是派生型別的,但由接受基型別的catch子句處理,那麼catch不能使用派生類的特有的成員函式。

catch子句的順序必須反映型別層次。

有可能單個catch不能完全處理乙個異常,在進行了一些矯正行動後,catch可能確定該異常必須有函式呼叫鏈中更上層的函式處理,catch可以通過重新丟擲將異常傳遞給函式呼叫鏈中更上層的函式。重新丟擲是後面不跟型別或表示式的乙個throw,

throw;

空throw語句將重新丟擲異常,他只能出現在catch或從catch呼叫的函式中。被丟擲的異常是原來的異常物件,而不是catch形參。

捕獲所有異常的catch子句為:

catch(…)

異常說明:跟在函式形參表之後,在關鍵字throw後跟著乙個(或為空)由圓括號括住的異常型別列表。

void recoup(int) throw(runtime_error);

空說明列表指出函式不丟擲任何異常。

void no_problem() throw();

如果乙個函式沒有指出異常說明,則該函式可以丟擲任意型別的異常。

異常說明有用的一種情況是,如果函式可以保證不丟擲任何異常。

派生類虛函式的異常說明符必須比對應基類虛函式的異常說明符同樣嚴格,或者更受限。

可以在函式指標的定義中提供異常說明符,在用另一指標初始化帶異常說明的函式指標,或者將後者賦值給函式位址的時候,兩個指標的異常說明符不必相同,但是,源指標的異常說明符必須至少與目標指標的一樣嚴格,甚至更嚴格。

void (*pf)(int) throw() = recoup; //error

的異常處理 C 異常處理總結

做開發不僅僅要考慮到業務邏輯更要在寫 時將各種可能考慮周全,但是這又是很難的事情,畢竟開發就是個人的事,而使用者可能上萬甚至百萬級別。這時,程式的穩定性就極為重要,我們不能讓程式因為某一處執行出問題而就直接導致程式或者產生其他更嚴重的後果,比如 做除法時當除數為零時,陣列訪問越界時,容器capaci...

C語言實現異常處理

1.setjmp j 設定 jump 點,用正確的程式上下文填充jmp buf物件j。這個上下文包括程式存放位置 棧和框架指標,其它重要的暫存器和記憶體資料。當初始化完jump的上下文,setjmp 返回0值。2.以後呼叫longjmp j,r 的效果就是乙個非區域性的goto或 長跳轉 到由j描述...

異常處理(二 C語言的異常處理)

1 異常終止 標準c庫提供了abort 和exit 兩個函式,它們可以強行終止程式的執行,其宣告處於 標頭檔案中。這兩個函式本身不能檢測異常,但在c程式發生異常後經常使用這兩個函式進行程式終止。下面的這個例子描述了exit 的行為 code include include int main void...