C C 異常機制

2021-07-14 02:49:40 字數 2852 閱讀 6120

此文為網上眾多博文整理結果,自己的思考較少,主要用於記錄以提醒自己。

通俗易懂地介紹了異常的處理。

c++的丟擲異常實際是作為另一種返回值來使用的。 丟擲異常的好處一是可以不干擾正常的返回值,另乙個是呼叫者必須處理異常,而不像以前c語言返回乙個整數型的錯誤碼,呼叫者往往將它忽略了(malloc 就是個典型的例子,當申請空間失敗時,只會返回乙個空指標,可能還會設定錯誤碼,但粗心的呼叫者如果不檢查返回指標而直接使用,就會 dump ;若在 c++裡使用 new開闢空間失敗,就會丟擲乙個 bad_alloc的異常,必須處理,否則就會終結程式)。

c++中的例外處理使用三個關鍵字來進行:try、catch、throw,其語法架構如下:

try  catch(type 1)  catch(type 2)

try 總是和 catch 配套使用的。

在程式中,當檢查到錯誤發生時,使用throw丟出乙個數值,這個數值可能是乙個整數、浮點數、字串或是物件,丟出數值之後,程式邏輯會離開丟出點,然後 開始比對catch中設定的資料型態,如果找到對應的型態,就執行該區塊中的程式碼,執行完後就離開整個try...catch,這有些類似switch 語法,執行完對應的case之後設定break來離開switch區塊。 

簡單舉個例子:

#include

using

namespace

std;

intmain

()catch

(int

err)

return0;

}

執行結果:

請輸入被除數: 10 

請輸入除數: 0 

除數為: 0 

結果無限大

當使用者輸入除數為0時,程式會檢查出來,此時丟出乙個整數錯誤程式碼,程式中catch有乙個捕捉整數的區塊,當捕捉到錯誤程式碼時,就會執行catch區 塊中的程式碼。 

發生錯誤時,執行哪一段catch區塊是由您所丟出的資料型態而定,丟出整數時就由設定catch整數的區塊捕捉,丟出浮點數時就用設定catch浮點數 的區塊捕捉,您也可以直接丟出乙個代表錯誤訊息的字串,以說明錯誤的原因,例如: 

#include

using

namespace

std;

intmain

()catch

(int

err)

catch

(const

char

*str

)return0;

}

執行結果:

請輸入被除數: 9 

請輸入除數: 0 

錯誤: 發生除零的錯誤

如果打算在catch中處理完例外之後,再度將例外丟出,則再使用throw即可,例如:

catch(...)

簡單介紹了異常丟擲與棧展開(stack unwinding)。

丟擲異常時,將暫停當前函式的執行,開始查詢匹配的catch子句。首先檢查throw本身是否在try塊內部,如果是,檢查與該try相關的catch子句,看是否可以處理該異常。如果不能處理,就退出當前函式,並且釋放當前函式的記憶體並銷毀區域性物件(對於物件,會自行呼叫析構函式釋放資源,在這個意義上,析構函式已然變成了異常處理很重要的一部分,故析構函式絕對不能再丟擲異常),繼續到上層的呼叫函式中查詢,直到找到乙個可以處理該異常的catch。這個過程稱為棧展開(stack unwinding)。當處理該異常的catch結束之後,緊接著該catch之後的點繼續執行。

1. 為區域性物件呼叫析構函式

如上所述,在棧展開的過程中,會釋放區域性物件所占用的記憶體並執行類型別區域性物件的析構函式。但需要注意的是,如果乙個塊通過new動態分配記憶體,並且在釋放該資源之前發生異常,該塊因異常而退出,那麼在棧展開期間不會釋放該資源,這樣就會造成記憶體洩露。

2. 析構函式應該從不丟擲異常

在為某個異常進行棧展開的時候,析構函式如果又丟擲自己的未經處理的另乙個異常,將會導致呼叫標準庫terminate函式。通常terminate函式將呼叫abort函式,導致程式的非正常退出。所以析構函式應該從不丟擲異常。

3. 異常與建構函式

如果在建構函式物件時發生異常,此時該物件可能只是被部分構造,要保證能夠適當的撤銷這些已構造的成員。

網上一種流行的說法是不會造成記憶體洩露:

1)如果物件是在棧上,那麼函式退棧自然會釋放其占用的空間,無需多慮。

2)如果物件是在堆上,我們還得兩種情況討論:

1、如果是new運算子丟擲的異常,那麼堆空間還沒有分配成功,也就無需釋放

2、如果是建構函式丟擲的異常,堆空間已經分配成功,那麼編譯器會負責釋放堆空間(inside the c++ object model, p301)

可見,物件本身的記憶體,是不會洩露的。

需要注意的是:如果乙個物件的建構函式丟擲異常,那麼該物件的析構函式不會被呼叫。

原因很簡單:如果物件沒有被構造完整,析構函式中的某些**可能會有風險。為了避免這類意外問題,編譯器拒絕生成呼叫析構函式的**。

更細緻的分析,讀者可以自行google。

這篇可作參考:

(異常與建構函式、析構函式)

4. 未捕獲的異常將會終止程式

不能不處理異常。如果找不到匹配的catch,程式就會呼叫庫函式terminate

C C 異常處理機制 I

引言 visual c 提供了對c語言 c 語言及mfc的支援,因而其涉及到的異常 exception 處理也包含了這三種型別,即c語言 c 語言和mfc的異常處理。除此之外,微軟對c和c 的異常處理進行了擴充套件,提出了結構化異常處理 seh 的概念,它支援c和c 與之相比,mfc異常處理僅支援c...

C C 異常處理機制 II

二.c 語言異常處理 2.1 c 異常處理語法 感謝c 語言的後期改造者們,他們在標準c 語言中專門整合了異常處理的相關語法 與之不同的是,所有的c 標準庫異常體系都需要執行庫的支援,它不是語言核心支援的 當然,異常處理被加到程式語言中,也是程式語言發展和逐步完善的必然結果。我們看到,c 不是唯一整...

C C 異常處理機制 I

c c 異常處理機制 i 2010 12 07 17 23 44 分類 c c 引言 visual c 提供了對c語言 c 語言及mfc的支援,因而其涉及到的異常 exception 處理也包含了這三種型別,即c語言 c 語言和mfc的異常處理。除此之外,微軟對c和c 的異常處理進行了擴充套件,提出...