2009-09-14 136 views
53

似乎沒有簡單的答案,但是有什麼假設可以安全地做出什麼時候可以訪問靜態類字段?何時初始化靜態C++類成員?

編輯:唯一安全的假設似乎是所有的靜態在程序開始前被初始化(致電main)。所以,只要我不從其他靜態初始化代碼引用靜態,我應該沒有什麼可擔心的?

+2

讓我很高興我現在在做.net開發,答案是「就在你使用它之前」。 – Massif 2009-09-14 14:09:13

+0

我同意你在編輯中的結論。與靜態初始化並行的唯一處理是其他靜態初始化。 (當然,這可以與實現所執行的其他內容同時進行,但不是您編寫的任何內容。) – 2009-09-14 14:10:10

+5

如果您想在C++中播放該卡,除非我們只在需要時付費。 :P – GManNickG 2009-09-14 14:10:39

回答

47

該標準保證了兩件事 - 即在同一翻譯單元中定義的對象(通常意思是。CPP文件)在其定義的順序初始化(不聲明):

3.6.2

與靜態存儲時間(basic.stc.static對象存儲)應是零初始化(dcl.init)在任何其他初始化發生之前。使用常量表達式進行零初始化和初始化統稱爲靜態初始化;其他所有初始化都是動態初始化在任何動態初始化發生之前,初始化具有用常量表達式(expr.const)初始化的靜態存儲持續時間的POD類型(basic.types)的對象。在命名空間範圍內在同一個翻譯單元中定義並動態初始化的具有靜態存儲持續時間的對象應按其定義出現在翻譯單元中的順序進行初始化。

其他保證的是從翻譯單元靜態對象的初始化使用從該轉換單元中的任何對象或函數之前會做:

這是實現定義與否命名空間範圍對象的動態初始化(dcl.init,class.static,class.ctor,class.expl.init)在main的第一個語句之前完成。如果初始化被推遲到main的第一個語句後的某個時間點,它應該在第一次使用在同一個翻譯單元中定義的任何函數或對象之前出現,而不是被初始化的對象。

我沒有保證(特別是在不同翻譯單元中定義的對象的初始化順序是實現定義的)。

編輯 正如Suma的評論所指出的那樣,它也保證它們在輸入main之前被初始化。

+8

我認爲還有一件事情可以保證,「它們在程序啓動之前(即在輸入main之前)被初始化」 - 這應該包含在我認爲的答案中。 – Suma 2010-12-29 18:51:11

+0

如果我有一個具有「main()」函數的二進制文件,並且靜態是與二進制文件動態鏈接的庫的一部分,該怎麼辦?靜態在調用main之前初始化? – Dilawar 2015-04-04 13:27:09

+2

由於EDIT的原因,現在上面的答案有矛盾。第二個黃色框(引用標準)說:「如果初始化是在main()...」的第一個語句之後的某個時間點推遲的,並且您添加了「保證在main()之前發生」,與之相矛盾,並且不引用標準。 – 2015-05-06 11:45:15

1

我相信它可以在執行過程中隨時訪問。未定義的是靜態變量的初始化順序。

0

這個問題並沒有一個完全微不足道的答案,但基本上它們是在控制傳遞到程序的入口點(main)之前初始化的。 (就我所知),它們被初始化的順序是未定義的,並且可能是編譯器特定的。

編輯:澄清,你添加的假設是正確的。只要你只是在主入口後訪問它,你並不需要擔心它初始化的時間/方式。它將在那個時候初始化。

1

它們可以在實現文件(.c/cpp/cc)文件中初始化。不要在.h中初始化它們,因爲編譯器會抱怨多個定義。

它們通常在main之前被初始化,但是order是未知的,因此避免了依賴性。它們當然可以在成員函數中訪問。請記住,靜態成員的初始化順序未知。我建議將靜態成員封裝到靜態函數中,以檢查成員是否已初始化。

17

它們在程序啓動之前被初始化(即在輸入main之前)。

當單個CPP文件中存在兩個或更多個(靜態數據)定義時,它們將按照它們在文件中定義的順序進行初始化(文件中早先定義的定義是/在下一個之前初始化)。

當多個CPP文件中存在兩個或多個(靜態數據的)定義時,處理CPP文件的順序是未定義的/實現特定的。如果全局變量(在程序啓動之前調用)的構造函數引用在另一個CPP文件中定義的另一個全局變量(可能尚未構建),則會出現此問題。然而,邁爾斯C++有效(這是名爲確保他們使用之前全局對象初始化)的項目47並描述了一個變通......

  • 定義一個靜態變量中頭文件(它是靜態的,所以你可以有多個實例沒有連接器抱怨)

  • 讓變量的構造函數調用你需要的任何東西(特別是構造在頭文件中聲明的全局單例)

......它說的是一種可用於某些系統頭文件的技術,例如以確保cin全局變量在甚至靜態變量的構造函數使用它之前被初始化。

+2

在Effective C++ 3rd版本中它是項目4:確保對象在使用之前被初始化。 (並在那裏討論非局部靜態變量,例如靜態類成員) – 2015-10-05 13:39:46

+1

並不是所有的靜態對象都保證在'main'輸入前被初始化。請參閱Tadeusz Kopec對引用標準的這個問題的回答: 「它是實現定義的,是否命名空間對象的動態初始化(dcl.init,class.static,class.ctor,class.expl.init)範圍是在主要的第一個陳述之前完成的。「 – 2017-10-05 13:52:37

-1

我認爲下面的一個進程的主線程將執行,以便

  1. CRT庫

  2. 靜態初始化

  3. 執行的主要的初始化()函數以下五個步驟

  4. static unitialization

    CRT庫

你想從其它靜態初始化代碼引用靜態

  • unitialization? 也許以下代碼工作:

    class A; 
    static auto_ptr<A> a(auto_ptr<A>(&GetStaticA())); 
    A &GetStaticA(void) 
    { 
        static A *a = NULL; //the static basic type variables initialized with constant experession will be initialized earlier than the other static ones 
        if (a == NULL) 
        { 
         a = new A(); 
         return *a; 
        } 
    } 
    
  • 3

    您在編輯中的最終結論是正確的。但問題在於類本身是靜態的。更容易的說,我的代碼將有類靜態成員不引用其他全局數據/類靜態成員,但一旦你採取這種方式,事情很快就會出錯。我發現在實踐中有一種方法沒有類靜態數據成員,而是類靜態包裝器方法。這些方法可以將靜態對象保存在其自身內部。對於例如

    TypeX* Class2::getClass1Instance() 
    { 
        static TypeX obj1; 
        return &obj1; 
    } 
    

    注:較早的回答說:

    其他保證的是從翻譯單元靜態對象 的初始化使用任何物體或從該轉換單元中 功能之前,將完成

    這不完全正確,標準在這裏被錯誤地推斷出來。如果在輸入main之前調用翻譯單元的功能,則這可能不成立。