C 中禁止異常資訊傳遞到析構函式外

2021-05-18 01:02:25 字數 1903 閱讀 5938

在有兩種情況下會呼叫析構函式。第一種是在正常情況下刪除乙個物件,例如物件超出了作用域或被顯式地delete。第二種是異常傳遞的堆疊輾轉開解(stack-unwinding)過程中,由異常處理系統刪除乙個物件。

在上述兩種情況下,呼叫析構函式時異常可能處於啟用狀態也可能沒有處於啟用狀態。遺憾的是沒有辦法在析構函式內部區分出這兩種情況。因此在寫析構函式時你必須保守地假設有異常被啟用,因為如果在乙個異常被啟用的同時,析構函式也丟擲異常,並導致程式控制權轉移到析構函式外,c++將呼叫terminate函式。這個函式的作用正如其名字所表示的:它終止你程式的執行,而且是立即終止,甚至連區域性物件都沒有被釋放。

class session ;

函式logcreation 和 logdestruction被分別用於記錄物件的建立與釋放。我們因此可以這樣編寫session的析構函式:

session::~session()

一切看上去很好,但是如果logdestruction丟擲乙個異常,會發生什麼事呢?異常沒有被session的析構函式捕獲住,所以它被傳遞到析構函式的呼叫者那裡。但是如果析構函式本身的呼叫就是源自於某些其它異常的丟擲,那麼terminate函式將被自動呼叫,徹底終止你的程式。這不是你所希望發生的事情。程式沒有記錄下釋放物件的資訊,這是不幸的,甚至是乙個**煩。那麼事態果真嚴重到了必須終止程式執行的地步了麼?如果沒有,你必須防止在logdestruction內丟擲的異常傳遞到session析構函式的外面。唯一的方法是用try和catch blocks。一種很自然的做法會這樣編寫函式:

session::~session()

catch (...)

}

但是這樣做並不比你原來的**安全。如果在catch中呼叫operator《時導致乙個異常被丟擲,我們就又遇到了老問題,乙個異常被轉遞到session析構函式的外面。

我們可以在catch中放入try,但是這總得有乙個限度,否則會陷入迴圈。因此我們在釋放session時必須忽略掉所有它丟擲的異常:

session::~session()

catch (...)

}

catch表面上好像沒有做任何事情,這是乙個假象,實際上它阻止了任何從logdestruction丟擲的異常被傳遞到session析構函式的外面。我們現在能高枕無憂了,無論session物件是不是在堆疊輾轉開解(stack unwinding)中被釋放,terminate函式都不會被呼叫。

不允許異常傳遞到析構函式外面還有第二個原因。如果乙個異常被析構函式丟擲而沒有在函式內部捕獲住,那麼析構函式就不會完全執行(它會停在丟擲異常的那個地方上)。如果析構函式不完全執行,它就無法完成希望它做的所有事情。例如,我們對session類做乙個修改,在建立session時啟動乙個資料庫事務(database transaction),終止session時結束這個事務:

session::session() // 為了簡單起見,,

session::~session()

如果在這裡logdestruction丟擲乙個異常,在session建構函式內啟動的transaction就沒有被終止。我們也許能夠通過重新調整session析構函式內的函式呼叫順序來消除問題,但是如果endtransaction也丟擲乙個異常,我們除了回到使用try和catch外,別無選擇。

綜上所述,我們知道禁止異常傳遞到析構函式外有兩個原因,第一能夠在異常轉遞的堆疊輾轉開解(stack-unwinding)的過程中,防止terminate被呼叫。第二它能幫助確保析構函式總能完成我們希望它做的所有事情。(如果你仍舊不很信服我所說的理由,可以去看herb sutter的文章exception-safe generic containers ,特別是「destructors that throw and why they』re evil」這段)。

析構函式中丟擲的異常

析構函式在什麼時候被呼叫執行?對於c 程式設計師來說,這個問題比較簡單,但是比較愛嘮叨的阿愚還是建議應該在此再提一提,也算回顧一下c 的知識,而且這將對後面的討論和理解由一定幫助。先看乙個簡單的示例吧!如下 class mytest base void main catch 編譯執行上面的程式,從程...

c 建構函式和析構函式中的異常

情況一 建構函式中有異常丟擲 建構函式中有異常丟擲,需要注意如果該建構函式中已在堆中生成了物件,要在建構函式中進行異常處理去釋放堆中資源或者使用智慧型指標,因為這時建構函式未執行成功,物件還未建立成功,在棧回退時不會呼叫析構函式,進而造成記憶體洩漏 若在堆中生成的物件則無需擔心,因為在棧回退時會釋放...

C 異常以及異常與析構函式

1.丟擲異常 1.1 丟擲異常 也稱為拋棄異常 即檢測是否產生異常,在c 中,其採用throw語句來實現,如果檢測到產生異常,則丟擲異常。該語句的格式為 throw 表示式 如果在try語句塊的程式段中 包括在其中呼叫的函式 發現了異常,且拋棄了該異常,則這個異常就可以被try語句塊後的某個catc...