静态类的未初始化静态数据成员

4
我今天遇到了一个奇怪的问题,我并没有完全理解。希望这里有人能够提供帮助。
情况比较简单。我有一个类,其中有一个静态成员是std::set类型。该类有两个模板构造函数,它们只在参数数量上有所不同。它们的行为相同,因此请注意构造函数是模板化的,并且构造函数正在搜索和插入std::set。
我遇到了以下行为: 对于该类的静态实例,在调用静态std::set的第一个方法(find())时,构造函数会崩溃。看起来set未被初始化。我认为构造函数在静态成员变量初始化之前被调用。
这里是一个简化的示例:
////////// 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;

崩溃发生在find方法的ErrorIf中。如果我注释掉那行,它会在插入行崩溃。

构造函数在main之前被调用(类的静态实例)。 有人知道这里可能出了什么问题吗?


我们需要一个完整运行的示例来展示问题,否则我们只是在猜测。但听起来你遇到了全局初始化顺序问题。你是否在全局作用域中创建了一个类型为ConVar<T>的对象? - Martin York
2个回答

4

构造函数中相互访问的全局对象在实例化顺序上可能会出现问题。

有几种方法可以解决这个问题:

尝试使用

//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 之前不要访问静态/全局变量,这被称为 "静态初始化顺序混乱。" 在那个时候,你的 mRegisteredVars 简单地不存在。按照你的做法进行操作是未定义的行为。

谢谢您指出这个问题。我并不知道这个麻烦事,我几乎肯定编译器会在访问构造函数之前识别初始化静态成员 - 即使类本身是静态的。 - Millianz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接