C ++静态初始化顺序

时间:2009-06-17 08:00:36

标签: c++ static-variables static-order-fiasco initialization-order

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

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

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

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

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}

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

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

6 个答案:

答案 0 :(得分:57)

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

https://isocpp.org/wiki/faq/ctors#static-init-order

开始阅读C ++ FAQ项目

答案 1 :(得分:8)

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

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

答案 2 :(得分:6)

  

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

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

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

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

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}

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

getGlobals().configuration.memberFunction ();

如果你真的想要将它包装在一个宏中,可以使用宏来节省一点点打字:

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();

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

答案 3 :(得分:3)

大多数编译器(链接器)实际上都支持指定顺序的(非可移植)方式。例如,使用visual studio,您可以使用init_seg编译指示将初始化安排到几个不同的组中。 AFAIK没有办法保证每组内的订单。由于这是非便携式的,您可能需要考虑是否可以修改您的设计而不需要它,但选项就在那里。

答案 4 :(得分:3)

指出这个帖子的年龄,我想提出我找到的解决方案。 正如许多人在我之前所指出的那样,C ++没有为静态初始化排序提供任何机制。我建议将每个静态成员封装在类的静态方法中,然后初始化成员并以面向对象的方式提供访问。 让我举个例子,假设我们要定义名为“Math”的类,在其他成员中包含“PI”:

class Math {
public:
   static const float Pi() {
       static const float s_PI = 3.14f;
       return s_PI;
   }
}

将在第一次调用Pi()方法时初始化s_PI(在GCC中)。请注意:具有静态存储的本地对象具有依赖于实现的生命周期,有关详细信息,请参阅2中的6.7.4。

Static keywordC++ Standard

答案 5 :(得分:1)

在方法中包装静态将修复顺序问题,但它不像其他人指出的那样是线程安全的,但如果这是一个问题,你可以这样做也使它成为线程。

// File scope static pointer is thread safe and is initialized first.
static Type * theOneAndOnlyInstance = 0;

Type& globalObject()
{
    if(theOneAndOnlyInstance == 0)
    {
         // Put mutex lock here for thread safety
         theOneAndOnlyInstance = new Type();
    }

    return *theOneAndOnlyInstance;
}