區域性靜態變數的初始化與異常
1、問題
程式設計的過程中,思考了一個問題。當一個區域性的靜態變數使用一個函式的返回值初始化時,如果該函式丟擲異常,那麼,區域性靜態變數是否被定義成功,即,如果再次呼叫包含區域性靜態變數的函式,丟擲異常的函式會不會再次被呼叫。
2、測試
就此問題,我寫了如下的測試程式:
3、結果
1、在VC9中:第二次呼叫TestStatic函式不再丟擲異常,即,區域性靜態變數已經定義成功。
2、在gcc中:第二次呼叫TestStatic函式依舊丟擲異常,即,區域性靜態變數沒有定義成功,需要再次進入定義,實現真正的記憶體分配空間。
對於結果,我更欣賞GNU的方式!
不管喜歡那種方式,測試結果都告訴我們,這段程式碼並部具有可移植性。VC的方式,讓我比較失望,因為,因此而失去了一個通用的程式設計技巧。然而,知道什麼是錯的,比知道什麼是對的還要重要,難道不是嗎?
4、背景與應用
有朋友會問,這樣的問題,什麼情況下會遇到?
我只用一個比較經典的例子來說明。略微熟悉設計模式的朋友都會知道Singleton模式。一般返回的都是一個具體類的例項。雖說一般情況下建構函式中不要丟擲異常,但免不了在某些情況下,可能會丟擲異常,尤其是在這種單例項模式的情形下,沒有必要一定保持建構函式中一定不丟擲異常(stl要求容器中的類的建構函式不要丟擲異常),所以,在這種情況下,我們似乎會希望,如果構造不成功,客戶函式捕獲異常,並修復錯誤後,再次獲得單例項時,希望能夠再次進行初始化,而得到一個正確的單例項的引用。然而,在這種情況下,面對兩個編譯器的測試結果,這段程式碼並不具有可移植性。
如果,只是如果,GNU編譯器的方式是一個規範的話,那麼,我們在Singleton模式下,就無需判斷返回的物件是否有效,如果發生異常,也無須在實現類中編寫彌補的程式碼,只需要再次重新獲得Singleton即可,可以簡化程式碼。但,這只是如果。
5、補充
回覆 scwinter:
多謝兄弟關注!剛才我測試了一下,啟用和未啟用異常展開語義,編譯的結果是不一樣的。又掌握了一個知識點!結果如下:
E:/temp/C++>cl test.cpp
用於 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 15.00.30729.01 版
版權所有(C) Microsoft Corporation。保留所有權利。
test.cpp
C:/Program Files/Microsoft Visual Studio 9.0/VC/INCLUDE/xlocale(342) : warning C4530: 使用了 C++ 異常處理程式,但未啟用展開語義。請指定 /EHsc
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
E:/temp/C++>test
exc1:100
TestStatic:0
E:/temp/C++>cl test.cpp /EHsc
用於 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 15.00.30729.01 版
版權所有(C) Microsoft Corporation。保留所有權利。
test.cpp
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
E:/temp/C++>test
exc1:100
exc2:100