75

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

在单个 .cpp 或 .h 文件中,这不是问题:实例将按照它们声明的顺序创建。但是,当您想用另一个编译单元中的实例初始化静态实例时,似乎无法指定顺序。结果是,根据天气情况,可能会构建依赖于另一个实例的实例,然后才构建另一个实例。结果是第一个实例初始化不正确。

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

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

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

确实,这确实有效。遗憾的是,您必须编写 globalObject().MemberFunction(),而不是 globalObject.MemberFunction(),导致客户端代码有些混乱和不雅。

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

4

6 回答 6

71

你已经回答了你自己的问题。静态初始化顺序是未定义的,最优雅的解决方法(同时仍然进行静态初始化,即不完全重构它)是将初始化包装在一个函数中。

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

于 2009-06-17T08:09:05.517 回答
7

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

但是你是对的,没有办法确保初始化的特定顺序,所以如果你已经下定决心,像你提到的那样,将初始化保持在一个函数中,可能是最简单的方法。

于 2009-06-17T08:12:04.153 回答
5

确实,这确实有效。遗憾的是,您必须编写 globalObject().MemberFunction(),而不是 globalObject.MemberFunction(),导致客户端代码有些混乱和不雅。

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

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

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

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

为了清理程序中的所有全局对象,只有一次调用 ~Globals()。为了访问全局,你仍然有类似的东西:

getGlobals().configuration.memberFunction ();

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

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

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

于 2009-06-17T10:12:07.953 回答
5

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

于 2009-06-26T14:42:37.417 回答
3

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

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

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

静态关键字C++标准

于 2013-08-12T10:29:29.923 回答
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;
}
于 2014-01-29T16:45:07.177 回答