1. 程式人生 > >8 More Effective C++—條款11(解構函式內阻止異常流出)

8 More Effective C++—條款11(解構函式內阻止異常流出)

1 提出問題

1 解構函式呼叫時機

解構函式會在下面兩種情況下被呼叫:

1, 離開物件所在作用域,物件生命週期終結,解構函式被呼叫,物件被銷燬。 2, 異常丟擲引起了棧展開(stack-unwinding),離開物件的所在的作用域,物件生命週期中介,解構函式被呼叫。

第一種情況如下面程式碼所示,離開函式myfunction()後,物件myobject會自動被析構。

void myfunction() {
	MyClass myobject;
}

2 棧展開

異常被丟擲後,當前函式停止執行,並查詢對應的catch字句。首先檢查throw是否在try內,若在,則將該異常與try對應的catch子句進行匹配。若匹配失敗,則釋放當前函式記憶體,銷燬區域性物件,將異常拋向上層呼叫函式,直到找到可以匹配的catch子句。

上面的過程就是”棧展開“。當處理catch結束後,緊接著catch之後繼續執行。但是,需要注意如下幾點:

1,棧展開過程只能銷燬區域性物件,即呼叫區域性物件的解構函式。 若使用new在heap聲明瞭物件,且在delete呼叫前出現異常,由於提前退出,在heap中宣告的資源將不會被釋放,造成記憶體洩漏。 2,解構函式應該從不丟擲異常 在進行棧展開過程中,解構函式若再次拋另一個異常,則會導致標準庫函式terminate被呼叫。terminate函式呼叫abort函式,程式異常退出。 3,異常與建構函式 同上篇所述,建構函式呼叫過程中出現異常,應保證已經被構造的成員能被恰當銷燬。 4,未捕獲的異常將終止程式

同上面所述,庫函式terminate被呼叫,終止程式執行。

3 問題

正如上面所提到,若解構函式丟擲異常,有兩種危害:

1,異常點之後的語句無法完成,析構工作沒有完成。 2,有可能是棧展開呼叫解構函式,兩次異常丟擲導致程式終結。

2 解決問題

因此,最簡單粗暴的方式,就是直接攔截解構函式中丟擲的異常,保證不會有更多的異常向上傳遞。如下面程式碼所示。

~Destructor() {
	try {
		doSomething();
	} catch (...) {
		// doNothing, avoid more exception
	}
}