2011-10-15 153 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