什么时候初始化全局常量与外部链接安全从静态初始化顺序fiasco?

Lot*_*har 17 c++ static-initialization

请考虑以下示例:

  • tt.h声明了一个带有外部链接的全局常量 extern int g_TRAGIC;

  • tt.cpp定义g_TRAGIC如下 const int g_TRAGIC = 0xF001;

  • my.cpp希望用它来定义自己的全局常量 const int g_MAGIC = g_TRAGIC;

当我读到iso-FAQ时,我会认为这会导致静态初始化顺序失败.然而,iso-FAQ注意到

某些情况下,静态初始化顺序fiasco也可以应用于内置/内部类型.

这是什么某些情况下,是什么意思?对于内置/内在类型,我们在哪些条件下保存并从SIOF发出声音,特别是常量?或者必须将Construct On First Use Idiom用于所有具有外部链接的常量?

注意:在实际代码中,我无法更改g_TRAGIC的定义.

mks*_*eve 1

编译器可以生成不同类型的代码。

静态初始化数据段

编译器将名称及其初始值发送到数据节中。

.data
   dw myData   6
Run Code Online (Sandbox Code Playgroud)

它在编译时初始化,并在程序的整个生命周期中安全地定义

构建数据

另一种选择是编译器为变量保留一些空间,并为数据创建一个初始值设定项/构造函数,然后在 之前调用构造函数main。执行析构函数(如果需要)atexit

 class CriticalSection {
      CRITICAL_SECTION m_myCS;
      public:
         CriticalSection() {
              InitializeCriticalSection( &m_myCS );
         }
         ~CriticalSection() {
              DeleteCriticalSection( & m_myCS );
         }
 } cs;
Run Code Online (Sandbox Code Playgroud)

合并的

一些数据可以在两个阶段中执行。

 struct Data {
     bool initialized;
     void *(*pMalloc)( size_t size );
 }  FixMalloc = { true, MyMalloc };
Run Code Online (Sandbox Code Playgroud)

我见过编译器(VS2013)生成的代码initialized在静态数据中初始化为 true,但创建了一个在运行时分配的pMalloc函数MyMalloc。(这是因为 MyMalloc 没有已知常量。)

单例方法

 SomeClass * GetSomeClass()
 {
     static SomeClass cls;
     return &cls;
 }
Run Code Online (Sandbox Code Playgroud)

这是定义的顺序 - 当它被调用时,但需要一个完整的C++11编译器是线程安全的。

概括

保证是:-

  1. 同一编译单元中的静态变量是从上到下初始化的。
  2. 静态变量是单线程初始化的。
  3. 单例已经定义了构建顺序。但不一定是线程安全的。

保证不是:-

  1. 一个对象的所有部分都会同时初始化。
  2. 静态初始化有一个工作运行时间。

在调用 main 之前,静态和 C/C++ 运行时都会引导。构造顺序问题也会发生在代码和运行时之间,因此您可能会使用复杂的构造函数来构造一些项目,这些项目依赖于不可用的服务。