C ++ 11中的延迟初始化顺序 [英] Deferred initialisation order in C++11

查看:124
本文介绍了C ++ 11中的延迟初始化顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码,将其分为三个编译单元:

a.h:

struct A
{
    void Register(const char* s);

    const char* m_s[10];
    int m_i = 0;
};

A& GetA();

a.cpp:

#include "a.h"
#include <stdio.h>

void A::Register(const char* s)
{
    m_s[m_i++] = s;
}

A& GetA()
{
    static A instance;
    return instance;
}

int main(int argc, char* argv[])
{
    A& a = GetA();
    int n = a.m_i;
    for (int i = 0; i < n ; ++i)
        printf("%s\n", a.m_s[i]);
    return 0;
}

b.cpp:

#include "a.h"
struct B
{
    B() { GetA().Register("b"); }

    static B* instance;
};
B* B::instance = new B;

c.cpp:

#include "a.h"
struct C
{
    C() { GetA().Register("c"); }

    static C* instance;
};
C* C::instance = new C;

代码使用gcc(-std = c ++ 11)构建并正常运行,产生输出:

c
b

现在,引用 cppreference.com :

延迟的动态初始化

是否动态初始化是实现定义的 发生-在主函数的第一条语句之前(对于静态函数) 或线程的初始函数(对于线程局部变量),或推迟 以后发生.

如果非内联变量的初始化为 推迟在main/thread函数的第一条语句之后发生, 它发生在第一次使用静态/线程的任何变量的odr之前 在与变量相同的转换单位中定义的存储期限 进行初始化.如果没有给定的变量或函数 翻译单元,该翻译中定义的非局部变量 单元可能永远不会初始化(这会模拟 按需动态库).但是,只要来自TU的任何内容 odr使用的所有非局部变量,其初始化或销毁 具有副作用的将被初始化,即使它们未在 程序.

请注意,a.cpp不知道BC的存在,并且B& CAGetA()A::Register()在其各自实例的构造期间的调用.

据我所知,B& C实例未使用ODR,并且当然不是来自main()的翻译单元.它们的初始化显然有副作用,但是在我看来,不能保证这种初始化将在输入main()之前或在main()打印已注册的字符串之前发生-或根本不会发生.

所以-终于-我的问题是这个:BC实例是在main()打印由于 not 符合标准,而是符合gcc的实现定义的行为?

如果标准保证,怎么办?

解决方案

BC实例是否在main()打印注册的字符串之前初始化,而不是由于标准而是gcc的实现定义的行为而导致的事实?

由标准保证.报价中最相关的部分:

如果未从给定的翻译单元中使用任何变量或函数,则该翻译单元中定义的非局部变量可能永远不会初始化

由于未从b.cppc.cpp使用过任何函数或变量,因此它们的静态变量可能未初始化(关于动态初始化),因此初始化的副作用可能不可见. /p>


在实践中,我期望显示单元在静态链接转换单元时的初始化行为,以及在动态加载转换单元(共享库)时可能出现的非初始化行为.但标准并不能保证两者都没有,因为它没有指定共享库的行为.

Consider the following code, split across three compilation units:

a.h:

struct A
{
    void Register(const char* s);

    const char* m_s[10];
    int m_i = 0;
};

A& GetA();

a.cpp:

#include "a.h"
#include <stdio.h>

void A::Register(const char* s)
{
    m_s[m_i++] = s;
}

A& GetA()
{
    static A instance;
    return instance;
}

int main(int argc, char* argv[])
{
    A& a = GetA();
    int n = a.m_i;
    for (int i = 0; i < n ; ++i)
        printf("%s\n", a.m_s[i]);
    return 0;
}

b.cpp:

#include "a.h"
struct B
{
    B() { GetA().Register("b"); }

    static B* instance;
};
B* B::instance = new B;

c.cpp:

#include "a.h"
struct C
{
    C() { GetA().Register("c"); }

    static C* instance;
};
C* C::instance = new C;

The code builds and runs fine using gcc (-std=c++11), producing the output:

c
b

Now, referring to cppreference.com:

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. If no variable or function is odr-used from a given translation unit, the non-local variables defined in that translation unit may never be initialized (this models the behavior of an on-demand dynamic library). However, as long as anything from a TU is odr-used, all non-local variables whose initialization or destruction has side effects will be initialized even if they are not used in the program.

Note that a.cpp is unaware of the existence of B and C, and that the only interactions of B & C with A are the invocations of GetA() and A::Register() during construction of their respective instances.

As far as I can see, the B & C instances are not ODR-used, and certainly not from main()'s translation unit. Their initialisation clearly has side effects, but it seems to me that there's no guarantee that this initialisation will occur before entry to main(), or before main() prints the registered strings - or indeed at all.

So - finally - my question is this: Is the fact that the B and C instances are initialised before main() prints the registered strings due not to the standard, but instead to gcc's implementation-defined behaviour?

If it is guaranteed by the standard, how?

解决方案

Is the fact that the B and C instances are initialised before main() prints the registered strings due not to the standard, but instead to gcc's implementation-defined behaviour?

It is not guaranteed by standard. The most relevant part of the quote:

If no variable or function is odr-used from a given translation unit, the non-local variables defined in that translation unit may never be initialized

Since no function, nor variable has been odr used from b.cpp nor c.cpp, their static variables may be uninitialized (in regard to dynamic initialization) and therefore the side-effects of their initialization might not be visible.


In practice, I would expect the shown, initializing behaviour when the translation units are statically linked, and the possible non-initializing behaviour when they are dynamically loaded (shared library). But neither is guaranteed by the standard, as it doesn't specify how shared libraries behave.

这篇关于C ++ 11中的延迟初始化顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆