2016-10-10 127 views
18

關於堆棧展開,C++標準表示:是堆棧展開與C++標準保證的異常?

例外,被認爲是完成了異常對象的初始化後未捕獲的([except.throw]),直到完成處理程序的激活爲異常([ except.handle])。這包括堆棧展開。

par 15.5.3的當前標準。我試圖理解最新的句子(This includes stack unwindings)是指什麼:

  • 是否它假定編譯器必須關心展開堆棧?
  • 或者,它是說它是依賴於編譯器來解放還是不是堆棧?

問題源於下面的代碼片段:

#include <iostream> 
#include <exception> 

struct S{ 
    S() { std::cout << " S constructor" << std::endl; } 
    virtual ~S() { std::cout << " S destructor" << std::endl; } 
}; 

void f() { 
    try{throw(0);} 
    catch(...){} 
} 

void g() { 
    throw(10); 
} 

int main() { 
    S s; 
    f(); 
    //g(); 
} 

現在:

  1. ,如果你運行它,是(捕獲異常),你有堆棧展開的提示
  2. 如果您對f();發表評論並取消註釋g();(沒有捕捉到該例外情況),則表示您沒有解開堆棧的提示

因此,這兩個實驗似乎贊成上面的第一個項目符號; clang ++和g ++都同意結果(但它不是判別式)。

而且,在我看來很奇怪的標準,這是在指定和時間在這裏留下了陰影對象現場時非常小心。

有人可以澄清嗎?對於標準保證的未捕獲異常,堆棧展開?如果是,在哪裏?如果不是,爲什麼?

+1

在發佈的程序中,'s'在堆棧展開期間不會被破壞。它在'f()'返回後被銷燬。 – aschepler

回答

29

堆棧展開的標準保證未捕獲的異常?

堆棧開卷是保證發生僅捕獲異常([except.handle]/9):

如果沒有發現匹配的處理器,函數std::terminate()被調用;在調用std::terminate()之前,堆棧是否展開是實現定義的。

所以它是實現定義的,否則。

如果不是,爲什麼?

如果發生未捕獲的異常,則會調用標準std::terminate。這代表程序執行的結束。如果您當時有一些特定於平臺的記錄系統狀態信息的平臺特定方式,則可能不希望該狀態受堆棧解除打擾。

如果你不這樣做...那麼你不在乎任何方式。

如果您確實需要堆棧始終解除堆棧,那麼您可以將main代碼(以及任何線程函數)置於try {} catch(...) {throw;}塊中。

+0

_「如果你真的需要堆棧總是解開,那麼你可以把你的主代碼放在try {} catch(...){throw;}塊中。」_ - 線程呢?我的直覺認爲,在這種情況下單個線程入口函數也需要try-catch塊,否? –

+0

@JohannGerell:我認爲這是UB,如果線程函數退出並出現異常。 –

+1

@KerrekSB不,它是「終止」。 –