2011-11-29 93 views
34

我運行了一個示例程序,確實調用了堆棧分配對象的析構函數,但這是由標準保證的嗎?在C++中拋出後調用析構函數嗎?

+15

當然可以。 [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)是C++中最重要的成語之一,取決於此。 – Jon

+0

是的,這是異常處理的重點。 –

回答

50

是,它是保證(提供的異常被捕獲),下降到其中析構函數調用的順序

C++ 11 15.2構造和析構[except.ctor]

1控件從throw表達式傳遞到處理函數時,自從try塊被輸入後構造的所有自動對象都會調用析構函數。 自動對象按照其構造完成 的相反順序銷燬。

此外,如果異常對象施工過程中引發,所述部分構造的對象的子對象,保證被正確地破壞:

2的任何存儲持續時間,其初始化或 破壞的目的被一個異常終止,將會爲其所有完全構造的子對象(不包括 聯合類類的變體成員)執行析構函數 ,也就是說,對於主體構造函數(12.6.2)已完成執行的 子對象和 析構函數尚未開始執行。同樣,如果對象的非委託構造函數已完成執行,並且此對象的委託構造函數退出且出現異常,則將調用對象的析構函數。如果對象是以 新表達式分配的,則會調用匹配的釋放函數(3.7.4.2,5.3.4, 12.5)以釋放對象佔用的存儲空間(如果有)。

這整個過程被稱爲「堆棧展開」:

3要求自動對象析構函數的方法,構造從try塊到一拋表達的路徑上 被稱爲「堆 展開「。如果在堆棧展開過程中調用的析構函數以 作爲異常退出,則調用std :: terminate(15.5.1)。

堆疊展開形成了廣泛使用的技術Resource Acquisition Is Initialization (RAII)的基礎。

請注意,如果未捕獲到異常,堆棧展開不一定會完成。在這種情況下,堆棧展開是否完成取決於實現。但是,無論堆疊展開是否完成,在這種情況下,您都可以保證最終致電std::terminate

C++ 11 15.5.1的std ::終止()函數[except.terminate]

2…在沒有找到匹配處理程序的情況下, 它是實現定義的,無論在調用std::terminate()之前堆棧是否展開。

+4

注意:關於構造中斷的對象。對象本身並沒有被銷燬(它從來沒有真正存在過),保證的是到目前爲止完全構造的子部件(基類,屬性)將以相反的順序銷燬。 –

+0

爲未捕獲的異常添加了有關堆棧展開(或不展開)的信息。 –

6

是的,析構函數保證在堆棧展開時被調用,包括由於拋出異常而展開。只有很少的例外,你必須記住:

  • 如果在其構造函數中引發異常,則不調用該類的析構函數。
  • 如果在構建初始化列表捕獲塊中捕獲異常,則會自動重新引發異常。
+8

3)析構函數不應該拋出異常,因爲沒有任何方法可以充分處理它們。 – DevSolar

+2

@DevSolar確實存在一些反例。 –

+1

@ AlfP.Steinbach:在堆棧展開期間拋出的任何析構函數(由於拋出*另一個*異常)將'終止'你的進程。我有興趣看到反例... – DevSolar

相關問題