我们无法确定静态对象的初始化顺序。
但这在以下示例中是否有问题?
代码:
class Factory
{
public:
static bool Register(name, func);
private:
static map<string, func> s_map;
};
// in cpp file
map<string, func> Factory::s_map;
bool Factory::Register(name, func)
{
s_map[name] = func;
}
Run Code Online (Sandbox Code Playgroud)
并在另一个cpp文件中
static bool registered = Factory::Register("myType", MyTypeCreate);
Run Code Online (Sandbox Code Playgroud)
当我注册更多类型时,我并不依赖于容器中的顺序。但是,第一次添加到容器中呢?我可以确定它已初始化为“足够”以容纳第一个元素吗?
还是“静态初始化顺序惨败”的另一个问题?
懒惰,这是来自http://en.cppreference.com/的副本:
在主函数开始执行之前,所有具有静态存储持续时间的非局部变量都作为程序启动的一部分进行初始化(除非延迟,请参见下文)。
...
动态初始化
在所有静态初始化完成后,非局部变量的动态初始化发生在以下几种情况:
...
延迟动态初始化
动态初始化是发生在主函数的第一条语句(对于静态函数)还是线程的初始函数(对于线程局部变量)之前,或者推迟到之后发生,这是实现定义的。
如果非内联变量的初始化被推迟到主/线程函数的第一条语句之后发生,则它发生在第一次odr 使用任何具有静态/线程存储持续时间的变量之前,该变量定义在与变量相同的翻译单元中被初始化。
重要的部分是odr-use:
ODR 使用
非正式地,如果一个对象的值被读取(除非它是编译时常量)或写入,它的地址被获取,或者引用被绑定到它,那么它就是 odr 使用的;
由于s_map是通过 填充的Factory::Register,因此我在这里看不到问题。
如果地图实现非常简单,它甚至可以作为静态初始化/在编译时的一部分进行初始化。
但即使延迟初始化,也会在使用 in 之前进行初始化Factory::Register,只要两者在同一个翻译单元中即可。
但是,如果地图在一个翻译单元Factory::Register中定义,而在另一个翻译单元中定义,则任何事情都可能发生。
您的方案不能保证按预期工作。成功取决于链接顺序。
一种确定的方法是通过(静态)函数访问映射,该函数将对象创建为静态变量,如下所示:
class Factory
{
public:
static bool Register(name, func);
private:
static map<string, func>& TheMap();
};
Run Code Online (Sandbox Code Playgroud)
map<string, func>& Factory::TheMap()
{
static map<string, func> g_;
return g_;
}
bool Factory::Register(name, func)
{
TheMap()[name] = func;
}
Run Code Online (Sandbox Code Playgroud)
这样做的缺点是作为开发人员的您很难控制静态变量的销毁顺序。在地图的情况下,这没有问题。但是如果静态变量相互引用,“静态链接失败”会变得更糟:根据我的经验,与程序开始时相比,在程序结束时防止/调试崩溃要困难得多。