考虑以下代码,分为三个编译单元:
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:
延迟动态初始化
动态初始化是发生在主函数的第一条语句(对于静态)还是线程的初始函数(对于线程局部变量)之前,或者推迟到之后发生,这是实现定义的。
如果非内联变量的初始化延迟发生在主/线程函数的第一条语句之后,它发生在任何变量的第一次 odr 使用之前,该变量在与该变量相同的翻译单元中定义为静态/线程存储持续时间被初始化。如果给定的翻译单元没有使用任何变量或函数,则该翻译单元中定义的非局部变量可能永远不会被初始化(这模拟了按需动态库的行为)。但是,只要 TU 中的任何东西被 odr-used,所有初始化或销毁有副作用的非局部变量都将被初始化,即使它们没有在程序中使用。
请注意,a.cpp
不知道 and 的存在,B
并且& withC
的唯一交互是在它们各自实例的构造过程中的调用和。B
C
A
GetA()
A::Register()
据我所知,B
&C
实例不是 ODR 使用的,当然也不是来自main()
的翻译单元。他们的初始化显然有副作用,但在我看来,不能保证这种初始化会在进入之前发生main()
,或者在main()
打印注册的字符串之前发生 - 或者根本不发生。
所以 - 最后 -我的问题是:B
andC
实例在main()
打印注册字符串之前被初始化不是因为标准,而是因为 gcc 的实现定义的行为吗?
如果有标准保证,怎么保证?