2011-10-15 158 views
3

今天我遇到了一個奇怪的問題,我並不完全理解。希望這裏有人能幫忙。靜態類的未初始化靜態數據成員

設置相當簡單。我有一個具有std :: set類型的靜態成員的類。該類有2個模板構造函數,它們的參數個數不同。這兩個構造函數的行爲都是相同的,所以只要注意構造函數是模板化的,構造函數正在搜索並插入到std :: set中。

我遇到以下行爲: 對於類的靜態實例,構造函數在靜態std :: set(find())上調用的第一個方法上崩潰。它看起來像集沒有被初始化。在我看來,構造函數在靜態成員變量初始化之前被調用。

下面是一個簡化的例子:ErrorIf的內部發生在find方法

////////// Header File 

class ConVar : public IListener 
{ 
    friend EventHandler; // Event Handler auto registers all instances of convar to commands 

public: // Auto 

    template< typename T > 
    ConVar(string const& name, string const& description, T const& default_value); 

private: 
    static std::set<u32> mRegisteredVars; 
}; 


//////// INL file (included from header) 

template< typename T > 
ConVar::ConVar(string const& name, string const& description, T const& default_value) 
    : mName(name), 
    mhName(name), 
    mDescription(description), 
    mClamp(false) 
{ 
    u32 hname = CONSTHASH(name.c_str()); 
    ErrorIf(mRegisteredVars.find(hname) != mRegisteredVars.end(), "Attempt to create same ConVar multiple times. Note the ConVars are static singletons!"); 

    *this = default_value; 

    mRegisteredVars.insert(hname); 

    gCore.Events.Subscribe(mhName, this); 
    } 

    ///////////// .cpp file 

    std::set<u32> ConVar::mRegisteredVars; 

的碰撞。如果我評論該行,它會在插入的行上崩潰。

構造函數在main(該類的靜態實例)之前調用 有沒有人知道這裏可能會發生什麼?

+0

我們需要一個充分運行的例子,展示行爲,否則我們只是猜測。但是這聽起來像是在觸及初始化問題的全局順序。您是否在全球範圍內創建了一個'ConVar '類型的對象? –

回答

3

從構造函數中訪問對象的全局對象在實例化順序上會有問題。

周圍有這樣的幾種方法:

嘗試

//Change 
static std::set<u32> mRegisteredVars; 

//Into 
static std::set<u32>& getRegisteredVarsSet() 
{ 
    static std::set<u32>& mRegisteredVars; 
    return mRegisteredVars; 
} 
// Obviously remove the `std::set<u32> ConVar::mRegisteredVars;` 
// From the cpp file. 

那麼無論你使用:mRegisteredVars變化getRegisteredVarsSet()

現在,即使你從一個靜態的構造函數訪問mRegisteredVars存儲持續時間對象調用getRegisteredVarsSet()(以檢索它)將保證mRegisteredVars在被返回之前將被完全初始化並因此可供使用。

因爲它是函數的靜態成員,所以它的使用壽命是程序的長度,因此它將在調用之間保持其狀態。

3

在調用main之前,請勿訪問靜態/全局變量。這被稱爲「Static Initialization Order Fiasco」。

您的mRegisteredVars根本不會在此時退出。做你所做的事是未定義的行爲。

+0

謝謝你指出這一點。我沒有意識到這種慘敗,我幾乎肯定編譯器會認識到在訪問構造函數之前初始化靜態成員 - 即使這個類本身是靜態的。 – Millianz