C++静态初始化顺序

Dim*_* C. 62 c++ static-variables initialization-order static-order-fiasco

当我在C++中使用静态变量时,我常常想要初始化一个变量,将另一个变量传递给它的构造函数.换句话说,我想创建彼此依赖的静态实例.

在单个.cpp或.h文件中,这不是问题:将按照声明的顺序创建实例.但是,如果要使用另一个编译单元中的实例初始化静态实例,则无法指定顺序.结果是,根据天气,可能会发生构建依赖于另一个实例的实例,并且之后才构建另一个实例.结果是第一个实例初始化不正确.

有谁知道如何确保以正确的顺序创建静态对象?我已经搜索了很长时间寻找解决方案,尝试了所有这些解决方案(包括Schwarz Counter解决方案),但我开始怀疑有一个确实有效.

一种可能性是使用静态函数成员的技巧:

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}
Run Code Online (Sandbox Code Playgroud)

实际上,这确实有效.遗憾的是,您必须编写globalObject().MemberFunction()而不是globalObject.MemberFunction(),从而导致一些令人困惑和不雅的客户端代码.

更新:感谢您的反应.遗憾的是,我确实似乎回答了自己的问题.我想我必须学会忍受它......

laa*_*lto 61

你已经回答了自己的问题.静态初始化顺序是未定义的,并且围绕它的最优雅的方式(虽然仍然进行静态初始化,即不完全重构它)是将初始化包装在函数中.

https://isocpp.org/wiki/faq/ctors#static-init-order开始阅读C++ FAQ项目

  • 但是,唉,这个解决方案不是线程安全的. (6认同)
  • 线程安全在C++ 11中不是问题.见http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11 (5认同)
  • @enobayram:带回你2.1年的时间:),是的,这是一个问题.将初始化移动到函数中的重点是初始化现在发生在`main`之前_not_,但是当首次调用函数时,可能在程序执行期间的任何时刻......以及任何线程中. (3认同)
  • @Lightness,Jose,Charles:我认为你的意见应该纳入公认的答案中.由于多线程在今天很普遍,这可能会导致令人讨厌的意外,并且在静态初始化惨败的任何顶级搜索结果中都没有提到.即parashift根本没有提到它. (2认同)

Jes*_*der 7

也许您应该重新考虑是否需要这么多全局静态变量.虽然它们有时可能很有用,但通常将它们重构到较小的局部范围要简单得多,尤其是当您发现某些静态变量依赖于其他变量时.

但是你是对的,没有办法确保特定的初始化顺序,所以如果你的心被设置在它上面,那么像你提到的那样在函数中保持初始化可能是最简单的方法.

  • 你是对的,使用太多的全局静态变量是不明智的,但在某些情况下,它避免了必须过度传递相同的对象.想想logger对象,持久变量的容器,所有IPC连接的集合等等...... (4认同)
  • "避免过度传递一个对象"只是一种说法,"我的程序组件之间的依赖关系是如此广泛,以至于跟踪它们是过度的工作.所以最好停止跟踪它们".除了通常它并不是更好 - 如果依赖关系不能被简化,那么当它通过跟随对象传递的位置跟踪它们时最有用*. (2认同)

Ric*_*den 5

实际上,这确实有效.遗憾的是,您必须编写globalObject().MemberFunction()而不是globalObject.MemberFunction(),从而导致一些令人困惑和不雅的客户端代码.

但最重要的是它有效,而且它是失败证明,即.绕过正确的用法并不容易.

程序正确性应该是您的首要任务.另外,恕我直言,上面的()是纯粹的风格 - 即.完全不重要.

根据您的平台,请注意过多的动态初始化.动态初始化器可以进行相对少量的清理(参见此处).您可以使用包含不同全局对象成员的全局对象容器来解决此问题.你因此:

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}
Run Code Online (Sandbox Code Playgroud)

只有一次调用~Globals()来清理程序中的所有全局对象.要访问全局,您仍然可以使用以下内容:

getGlobals().configuration.memberFunction ();
Run Code Online (Sandbox Code Playgroud)

如果你真的想要你可以将它包装在宏中以使用宏保存一点点输入:

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();
Run Code Online (Sandbox Code Playgroud)

虽然,这只是初始解决方案的语法糖.


Dol*_*hin 5

大多数编译器(链接器)实际上支持一种(不可移植的)指定顺序的方式。例如,在 Visual Studio 中,您可以使用init_seg编译指示将初始化安排到几个不同的组中。AFAIK 无法保证每个组内的顺序。由于这是不可移植的,您可能需要考虑是否可以将您的设计修复为不需要它,但选项就在那里。