简体   繁体   English

C++ static 初始化顺序:添加到 map

[英]C++ static initialization order: adding into a map

We cannot determine the order of the initialization of static objects.我们无法确定 static 对象的初始化顺序。

But is this a problem in the following example?但在下面的例子中这是一个问题吗?

  • one static variable is a map (or other container)一个 static 变量是 map (或其他容器)
  • from other static variable we populate that map从其他 static 变量我们填充 map

the code:编码:

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;
}

and in another cpp file在另一个 cpp 文件中

static bool registered = Factory::Register("myType", MyTypeCreate);

When I register more types I don't depend on the order in the container.当我注册更多类型时,我不依赖于容器中的顺序。 But what about the first addition to the container?但是第一次添加到容器中呢? Can I be sure it's initialized "enough" to take the first element?我可以确定它的初始化“足够”以获取第一个元素吗?

Or it's another problem of "static initialization order fiasco"?还是“静态初始化顺序惨败”的另一个问题?

Your scenario is not guaranteed to work as expected. 您的方案无法保证按预期工作。 The success depends on the link order. 成功取决于链接顺序。

One approach to be sure is to access the map through a (static) function that creates the object as a static variable like this: 一种方法是通过(静态)函数访问地图,该函数将对象创建为静态变量,如下所示:

h file: h文件:

class Factory
{
public:
    static bool Register(name, func);

private:
    static map<string, func>& TheMap();
};

cpp file: cpp文件:

map<string, func>& Factory::TheMap()
{
    static map<string, func> g_;
    return g_;
}

bool Factory::Register(name, func)
{
    TheMap()[name] = func;
}

The downside of this is that the order of destruction of static variables is hard to control by you as the developer. 这样做的缺点是,作为开发人员,您很难控制静态变量的破坏顺序。 In the case of the map this is no problem. 在地图的情况下,这是没有问题的。 But if static variables reference each other, the "static linking fiasco" gets even worse: In my experience it's much harder to prevent/debug a crash when a program ends compared to when it starts. 但是如果静态变量互相引用,那么“静态链接惨败”就会变得更糟:根据我的经验,当程序结束时,与启动时相比,防止/调试崩溃要困难得多。

Being lazy, here's a copy from http://en.cppreference.com/ : 懒惰,这是来自http://en.cppreference.com/的副本:

Non-local variables 非局部变量

All non-local variables with static storage duration are initialized as part of program startup, before the execution of the main function begins (unless deferred, see below). 具有静态存储持续时间的所有非局部变量在主函数执行开始之前初始化为程序启动的一部分(除非延迟,请参见下文)。

... ...

Dynamic initialization 动态初始化

After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations: 完成所有静态初始化后,在以下情况下会发生非局部变量的动态初始化:

... ...

Deferred dynamic initialization 延迟动态初始化

It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after. 它是实现定义的,是否在主函数的第一个语句(用于静态)或线程的初始函数(用于线程本地)之前发生动态初始化,或者延迟发生在之后。

If the initialization of a non-inline variable is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized. 如果非内联变量的初始化延迟发生在主/线程函数的第一个语句之后,则它发生在任何变量的第一次使用之前,其中静态/线程存储持续时间在与变量相同的转换单元中定义被初始化。

The important part is odr-use : 重要的部分是使用odr

ODR-use ODR使用

Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; 非正式地,如果一个对象的值被读取(除非它是一个编译时常量)或者被写入,它的地址被采用,或者一个引用被绑定到它上面,它就被使用了。

Since the s_map is populated through Factory::Register , I don't see a problem here. 由于s_map是通过Factory::Register填充的,所以我在这里看不到问题。


If the map implementation is very trivial , it may even be initialized as part of the static initialization/at compile time. 如果map实现非常简单 ,它甚至可以作为静态初始化/编译时的一部分进行初始化。

But even if the initialization is deferred, it will be initialized before the use in Factory::Register , as long as both are in the same translation unit . 但即使初始化是延迟的,它也会在Factory::Register使用之前进行初始化, 只要两者都在同一个翻译单元中

However, if the map is defined in one translation unit, and Factory::Register is defined in another, anything can happen. 但是,如果在一个转换单元中定义了映射,而在另一个转换单元中定义了Factory::Register则可能发生任何事情。

Statics are initialized prior to first use. 在首次使用之前初始化静态。 You should be fine. 你应该没事。

edit: As pointed out, doesn't apply to multiple translation units. 编辑:正如所指出的,不适用于多个翻译单元。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM