2017-04-25 36 views
3

如何處理從本地靜態對象的構造函數中拋出的異常?例如我已經以下代碼:本地靜態對象和異常

class A 
{ 
public: 
    A() {throw runtime_error("Ooops");} 
}; 

void foo() 
{ 
    static A a = A(); 
    cout << "Continue" << endl; 
} 

int main(void) 
{ 
    try 
    { 
     foo(); 
    } 
    catch(...) 
    { 
    } 
    foo(); // Prints continue 
    return 0; 
} 

據我所知,在第二呼叫foo方法的情況下,對象a被視爲完全構造對象,構造不被調用。 (更多結束,它似乎是由於第一次異常拋出不被稱爲a的析構函數)

+3

不,您的理解不正確。你可以嘗試一下[trivially](https://wandbox.org/permlink/0zqV3BglWpVZSgRM)。 –

+0

如果拋出異常並終止構造函數的執行(未完成構造對象),您如何期望'a'完全構造? – Rogus

+0

'靜態A a = A();'你爲什麼這樣做?爲什麼要複製初始化? –

回答

4

如果這是真的,那麼這是一個編譯器錯誤。

(然而,VTT claims that this code produces the expected result with Visual Studio 2015,所以我建議您仔細檢查你的結果。)

下面是標準規定的行爲:

[C++14: 6.7/4]:所有塊的零初始化(8.5)在進行任何其他初始化之前執行具有靜態存儲持續時間(3.7.1)或線程存儲持續時間(3.7.2)的範圍變量。在靜態存儲持續時間(如果適用)下,塊範圍實體的常量初始化(3.6.2)在第一次輸入塊之前執行。允許實現在靜態或線程存儲持續時間在命名空間範圍(3.6.2)中允許實現靜態初始化變量的相同條件下,使用靜態或線程存儲持續時間對其他塊範圍變量進行早期初始化。否則,這種變量會在控件第一次通過聲明時被初始化;這種變量在其初始化完成時被認爲是初始化的。 如果通過拋出異常退出初始化,則初始化未完成,因此下次控制進入聲明時將再次嘗試初始化。如果控制器在變量初始化時同時進入聲明,則併發執行應等待初始化完成。如果控制在初始化變量時遞歸地重新輸入聲明,則行爲是未定義的。 [..]

GCC 6.3.0 correctly attempts to reconstruct the A(從而再次引發)。


更多了,這似乎是一個由於第一異常拋出的析構函數不叫

沒有,也不會。你不能銷燬一開始就沒有成功創建的對象。

[C++14: 15.2/2]:任何存儲持續時間,其初始化或破壞是由一個異常終止將已析構函數對於所有其完全構造的子對象(不包括聯合狀類的變體成員)的執行的一個目的,即,用於主構造函數(12.6.2)已經完成並且析構函數尚未開始執行的子對象。同樣,如果對象的非委託構造函數已完成執行,並且該對象的委託構造函數以異常退出,則會調用該對象的析構函數。如果在新表達式中分配了對象,則會調用匹配釋放函數(3.7.4.2,5.3.4,12.5)(如果有),以釋放對象佔用的存儲空間。


順便說一句,這並不解決問題,但你應該這樣寫:

static A a; 

的拷貝初始化從臨時是沒有意義的。

+1

6.7/4「如果通過拋出異常退出初始化,則初始化不完整,因此下一次控制進入聲明時將再次嘗試初始化。」 – aschepler

+0

@aschepler:謝謝 –

+0

@BoundaryImposition,附加/分離調試器中程序的行爲不同(我的意思是msvc 2015)。在分離的調試程序失敗的情況下 – LmTinyToon

2

靜態局部變量的每個實例還隱式地創建了一個全局布爾變量,在構造靜態變量後,該變量將被設置爲true。如果構造函數拋出的時間比下次調用方法時還多,則會有另一個構造靜態變量的嘗試。

+0

是的,但OP顯示它不是。 –

+0

嗯,它看起來像他甚至沒有運行他的代碼(它實際上有一個未捕獲的異常),請參見[工作示例](http://ideone.com/4HZSTO),它也可以在VS2015中正常工作 – VTT

+0

有趣!我沒有VS2015確認。 –