【问题标题】:Static Initialization Order for Singletons单例的静态初始化顺序
【发布时间】:2019-05-21 09:34:44
【问题描述】:

所以我正在阅读 Zero Initialization 将初始化:

在任何其他初始化之前,每个具有静态或线程本地存储持续时间的命名变量不受常量初始化的影响

我使用 Singleton 和传统的私有构造函数和一个静态公共方法,其中有一个本地静态单例对象,该方法将返回。

我的问题是该类还有一个静态的vector,它是零初始化的,它似乎在单例之后被初始化,这意味着我无法与之交互。有什么东西可以控制这个初始化顺序,还是简单地定义了实现?


这是我的代码的简化:

class Foo {
    Foo(){ s_vec.push_back(13); }
public:
    static Foo& Get() {
        static Foo singleton;
        return singleton;
    }

    int Front() const { return s_vec.front(); }
    static vector<int> s_vec;
};
vector<int> Foo::s_vec;

我遇到了这个问题,因为在代码的其他地方我正在初始化一个像这样的静态全局变量并且没有得到 13:static const auto element = Foo.Get().Front()

【问题讨论】:

  • 如果意图只有一个实例,是否需要将向量设为静态?
  • 注意:“错误:不能调用没有对象的成员函数‘Foo& Foo::Get()’。”我认为你的意思是这个函数是静态的。
  • 您如何确定“它似乎在单例之后被初始化”?我的简单测试表明 Foo::s_vec 在 Foo::Get() 被调用之前存在。
  • "Foo& Get()" 应该是 "static Foo& Get()",我假设?

标签: c++ static singleton static-variables zero-initialization


【解决方案1】:

全局变量的构造函数在main开始之前执行,但没有指定跨编译单元的顺序。

您示例中的Foo 构造函数只应在您调用Foo::Get 时调用。如果第一次调用是在main,静态向量已经初始化好了。

当您在另一个全局对象的初始化代码中调用Foo::Get 时,您可能会遇到您所描述的竞争,尤其是当代码在另一个编译单元中时。

但是在像这样的简单测试中,向量应该总是首先被初始化并且不会有可能的竞争:

class Foo {
    Foo() = default;
public:
    static Foo& Get() {
        static Foo singleton;
        return singleton;
    }
    static vector<int> s_vec;
};
vector<int> Foo::s_vec; // will be initialized before main

int main() {
  Foo::Get(); // --> triggers constructor call Foo::Foo
  return 0;
}

(我假设Foo::Get 是单例中的静态成员,否则您无法实例化它。但在概念上并没有什么区别。)

有问题的场景可能如下所示:

// other file
struct Bar {
  Bar() { Foo::Get(); }
};
Bar bar; // global object

您无法控制Foo::s_vec(在第一个编译单元中)和bar(在第二个编译单元中)的初始化顺序。

【讨论】:

  • 我终于从圣诞假期回来了!我试图澄清我的问题。我意识到我的示例并没有清楚地说明我的问题:“该类还有一个静态向量,它是零初始化的,它似乎在单例之后被初始化,这意味着我无法与之交互。”
猜你喜欢
  • 2021-07-12
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多