[英]What is the lifetime of a static variable in a C++ function?
如果一個變量在函數的作用域中被聲明為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 中銷毀C:>sample.exe 1
創建於 if
在 foo 中創建
在 foo 中銷毀
毀於 ifC:>sample.exe 1 2
在 foo 中創建
創建於 if
毀於 if
在 foo 中銷毀
[0]
由於C++98 [2]沒有提到多線程,所以在多線程環境中這將如何表現是未指定的,並且可能會像Roddy提到的那樣有問題。
[1]
C++98第3.6.3.1
節[basic.start.term]
[2]
在 C++11 中,靜態以線程安全的方式初始化,這也稱為Magic Statics 。
Motti 關於順序是正確的,但還有一些其他的事情需要考慮:
編譯器通常使用一個隱藏的標志變量來指示局部靜態是否已經初始化,並且在函數的每個條目上都會檢查這個標志。 顯然這是一個小的性能損失,但更令人擔憂的是這個標志不能保證是線程安全的。
如果您有上述的本地靜態,並且從多個線程調用foo
,您可能會遇到競爭條件,導致plonk
初始化不正確甚至多次。 此外,在這種情況下, plonk
可能會被與構造它的線程不同的線程破壞。
不管標准怎么說,我對局部靜態破壞的實際順序非常謹慎,因為你可能會在不知不覺中依賴靜態被破壞后仍然有效,這真的很難追蹤。
如果沒有標准中的實際規則,現有的解釋並不完整,見 6.7:
具有靜態存儲持續時間或線程存儲持續時間的所有塊范圍變量的零初始化在任何其他初始化發生之前執行。 具有靜態存儲持續時間的塊范圍實體的持續初始化(如果適用)在首次進入其塊之前執行。 在允許實現在命名空間范圍內靜態初始化具有靜態或線程存儲持續時間的變量的相同條件下,允許實現使用靜態或線程存儲持續時間執行其他塊范圍變量的早期初始化。 否則這樣的變量在第一次控制通過它的聲明時被初始化; 此類變量在其初始化完成后被視為已初始化。 如果初始化通過拋出異常退出,則初始化未完成,因此將在下次控件進入聲明時再次嘗試。 如果在初始化變量時控制同時進入聲明,則並發執行將等待初始化完成。 如果在初始化變量時控件以遞歸方式重新進入聲明,則行為未定義。
FWIW,Codegear C++Builder 不會按照標准按照預期的順序進行破壞。
C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if
……這又是一個不依賴破壞令的理由!
一旦程序執行開始,靜態變量就開始發揮作用,並且在程序執行結束之前一直可用。
靜態變量在內存的數據段中創建。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.