c++中全局变量初始化是在进入main函数之前完成的,也包括类的静态成员。存在多个全局变量时,它们的初始化顺序是未知的,如果某些全局变量初始化依赖了其他的全局变量,可能会诱发程序未定义行为,在进入main函数之前就崩溃。尤其是在c++类的静态成员之间,这种依赖关系还是挺常见的,一旦发生这种bug,是很难定位的,一来没办法debug,因为还没进入main就崩了,在一个这种问题可能还是偶发的,更难排查了。
举一个例子,在实现原型模式时,原型列表通常被声明为父类的静态成员,派生类通过静态成员初始化将自己注册到原型列表。此时,派生类的静态成员就依赖了父类的静态成员,有可能派生类静态成员去执行初始化时,父类的静态成员尚未初始化,这样就直接导致程序异常退出。
class IFather
{
public:
IFather(){}
virtual IFather* Clone()=0;
static IFather* FindAndClone(std::string name)
{
if(m_prototypes.find(name)==m_prototypes.end())
return nullptr;
return m_prototypes[name]->clone();
}
protected:
static void AddProtoType(std::string name,IFather* proto)
{
m_prototypes[name] = proto;
}
private:
static std::map<string,IFather*> m_prototypes;
}
std::map<string,IFather*> IFather::m_prototypes;
class Children:public IFather
{
public:
Children(std::string name)
{
m_name=name;
}
IFather* Clone()
{
return new Children("Children");
}
private:
Children()
{
AddProtoType("Children",this);
}
std::string m_name="Children";
static Children proto;
}
Children Children::proto;
上面的代码简单地阐述了这一点,Chlidren::proto和IFather::m_prototypes初始化顺序是不确定的,如果Chlidren::proto在IFather::m_prototypes之前执行初始化,就会出错。为了解决这个问题,可以将m_prototypes变成静态函数内部的局部静态变量,以此保证m_prototypes在Chlidren::proto之前初始化。
class IFather
{
public:
IFather(){}
virtual IFather* Clone()=0;
static IFather* FindAndClone(std::string name)
{
std::map<string,IFather*>& protoRef = GetPrototypesMap();
if(protoRef.find(name)==m_prototypes.end())
return nullptr;
return protoRef[name]->clone();
}
protected:
static void AddProtoType(std::string name,IFather* proto)
{
std::map<string,IFather*>& protoRef = GetPrototypesMap();
protoRef[name] = proto;
}
private:
static std::map<string,IFather*>& GetPrototypesMap()
{
static std::map<string,IFather*> m_prototypes;
return m_prototypes;
}
}