全局常量的外部链接何时可以安全地避免静态初始化顺序问题?

17
考虑以下例子:
  • 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时,我会认为这会导致静态初始化顺序混乱(SIOF)。然而,iso-FAQ中指出:

在某些情况下,静态初始化顺序混乱也可能适用于内置/固有类型。

这里的“某些”情况是什么意思?在哪些条件下我们可以避免内置/固有类型的SIOF,特别是常量?或者对于所有具有外部链接的常量都必须使用COFU(Construct On First Use Idiom)?
注意:在真实代码中,我无法更改g_TRAGIC的定义。

1
你不能只在头文件中定义 g_TRAGIC 吗?C++是允许这样做的,这样可以避免初始化顺序混乱的问题。 - pcarter
好的观点。从理论上讲,是的,但实际上那段代码超出了我的控制范围。 - Lothar
当模块成为标准C++时,您将会更加安全。 - edmz
有没有任何理由保留 g_MAGIC 这个变量? - user207421
是的,g_MAGIC 是我们提供的接口的一部分。g_TRAGIC 是我无法控制的实现细节。 - Lothar
假定“某些情况”只是指存在问题的情况。您在帖子中描述了其中一种情况。不会出现问题的情况是两个常量都定义在同一个单位中。 - M.M
2个回答

1

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

静态初始化数据段

编译器将名称和初始值发射到数据部分中。

.data
   dw myData   6

这是在编译时初始化的,并且在程序的整个生命周期内都有安全定义。

构造数据

另一种选择是让编译器为变量保留一些空间,并创建一个数据的初始化程序/构造函数,然后在main之前调用构造函数。如果需要,则在atexit中执行析构函数。
 class CriticalSection {
      CRITICAL_SECTION m_myCS;
      public:
         CriticalSection() {
              InitializeCriticalSection( &m_myCS );
         }
         ~CriticalSection() {
              DeleteCriticalSection( & m_myCS );
         }
 } cs;

合并

有些数据可能会在两个阶段都执行。

 struct Data {
     bool initialized;
     void *(*pMalloc)( size_t size );
 }  FixMalloc = { true, MyMalloc };

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

单例方法

 SomeClass * GetSomeClass()
 {
     static SomeClass cls;
     return &cls;
 }

这是一个定义顺序的函数 - 当它被调用时,但需要完全支持C++11编译器才能保证线程安全。

概要

保证如下:

  1. 同一编译单元中的静态变量从上到下进行初始化。
  2. 静态变量单线程进行初始化。
  3. 单例模式具有定义的构造顺序。但不一定线程安全。

不保证如下:

  1. 对象的所有内容同时初始化。
  2. 静态初始化有工作运行时。

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


在重新阅读了您的回答和C++ FAQ之后,我得出以下结论:

有时/经常情况下,编译器可以正常初始化内置/固有类型,即使其他类型不能。

这取决于编译器,您不应该依赖于此。

在C ++中定义常量是一种习惯。

我会接受您的答案,指出“组合”情况给出了一个很好的线索。
- Lothar

0

进一步阅读ISO-FAQ,我们得到了答案。

如果您尝试使用常量函数的返回值来初始化内置/固有类型,则会发生SIOF。

const int g_MAGIC = f(g_TRAGIC);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接