若是在函數範圍內將變量聲明爲static
變量,則該變量僅初始化一次,並在函數調用之間保留其值。 它的壽命究竟是什麼? 什麼時候調用其構造函數和析構函數? 安全
void foo() { static string plonk = "When will I die?"; }
函數static
變量的生命週期從程序流第一次遇到該聲明開始[0]開始,並在程序終止時結束。 這意味着運行時必須執行一些記賬以僅在實際構建時對其進行銷燬。 多線程
另外,因爲該標準規定靜態對象的析構函數必須以其構造完成的相反順序運行[1] ,而且構造的順序可能取決於特定的程序運行,所以必須考慮構造的順序。 併發
例 函數
struct emitter { string str; emitter(const string& s) : str(s) { cout << "Created " << str << endl; } ~emitter() { cout << "Destroyed " << str << endl; } }; void foo(bool skip_first) { if (!skip_first) static emitter a("in if"); static emitter b("in foo"); } int main(int argc, char*[]) { foo(argc != 2); if (argc == 3) foo(false); }
輸出: 性能
C:> sample.exe
在foo中建立
在foo中毀了 uiC:> sample.exe 1
建立於
在foo中建立
在foo中毀了
毀於 spaC:> sample.exe 1 2
在foo中建立
建立於
毀於
在foo中毀了 線程
[0]
因爲C ++ 98 [2]沒有引用多線程,所以在多線程環境中如何表現尚無定論,而且可能會引發Roddy的說起。 code
[1]
C ++ 98第3.6.3.1
節[basic.start.term] 對象
[2]
在C ++ 11中,靜態是以線程安全的方式初始化的,這也稱爲Magic Statics 。
Motti關於順序是正確的,但還須要考慮其餘一些事項:
編譯器一般使用一個隱藏的標誌變量來指示是否已初始化本地靜態變量,並在該函數的每一個條目上均檢查該標誌。 顯然,這對性能影響不大,但更使人擔心的是,不能保證此標誌是線程安全的。
若是您具備上述的局部靜態foo
,而且從多個線程調用了foo
,則您可能會遇到競爭情況,從而致使錯誤地初始化plonk
甚至屢次。 一樣,在這種狀況下, plonk
可能會被與構造它的線程不一樣的線程破壞。
儘管有標準說明,但我會對本地靜態銷燬的實際順序很是警戒,由於您有可能會無心中依賴銷燬後仍然有效的靜態,這確實很難追蹤。
FWIW,Codegear C ++ Builder不會按照標準按預期順序破壞。
C:\> sample.exe 1 2 Created in foo Created in if Destroyed in foo Destroyed in if
...這是不依賴銷燬順序的另外一個緣由!
若是沒有6.7中的標準的實際規則,則現有的解釋還不是很完整:
在進行任何其餘初始化以前,將使用靜態存儲持續時間或線程存儲持續時間對全部塊範圍變量進行零初始化。 若是適用,將在首次進入其塊以前執行具備靜態存儲持續時間的塊範圍實體的恆定初始化。 在容許實現以靜態或線程存儲持續時間在命名空間範圍內靜態初始化變量的相同條件下,容許實現以靜態或線程存儲持續時間對其餘塊範圍變量進行早期初始化。 不然,該變量將在控件第一次經過其聲明時進行初始化; 此類變量在初始化完成後即被初始化。 若是初始化因爲拋出異常而退出,則說明初始化未完成,所以下次控件進入聲明時會再次嘗試初始化。 若是在初始化變量時控件同時輸入了聲明,則併發執行應等待初始化完成。 若是控件在初始化變量時遞歸地從新輸入聲明,則該行爲未定義。
靜態變量在程序執行開始後即發揮做用,而且在程序執行結束以前一直可用。
靜態變量在內存的數據段中建立。