class 层次结构中的静态初始化顺序

Static initialization order in class heirarchy

我最近痛苦地意识到 Static Initialization Order Fiasco。我想知道 "initialization order is undefined across translation units" 的规则是否仍然适用于父 class 中的静态成员,而子 class.

中的静态成员需要这些成员

例如,假设我们有(为简洁起见,不包括所有# guards 和 includes)

// a.h
class A {
    static int count;
    static int register_subclass();
};

// a.cpp
int A::count = 0;
int A::register_subclass() {
    return count ++;
}

然后是 A

的子 class
// b.h
class B : public A {
    static int id;
};

// b.cpp
int B::id = A::register_subclass();

这里有两个翻译单元,其中一个在初始化时依赖于另一个静态对象的静态对象...看起来它可能是静态初始化顺序失败的一个实例。

我的问题是:它真的安全吗

也就是说,我能保证 B::idA::count 初始化之前不会包含从 A::count 复制的垃圾吗?从我自己的测试来看,A 似乎总是先初始化,但我不确定如何在初始化顺序中引入噪声以增加行为未定义时失败的可能性。

通常,依赖基 class 和派生 class 的静态初始化顺序是不安全的。无法保证 A 的静态初始化会在 B 之前发生。即static initialization order fiasco.

的定义

您可以使用 construct on first use 成语:

// a.h
class A {
private:
    static int& count();
protected:
    static int register_subclass();
};

// a.cpp
int& A::count() {
    static int count = 0;
    return count;
}

int A::register_subclass() {
    return count()++;
}

// b.h
class B : public A {
public:
    static int id;
};

// b.cpp
int B::id = A::register_subclass();

Live demo.

更新:然而,博格丹在评论中指出

according to [3.6.2] in the Standard, the order of initialization in this specific example is guaranteed. It has nothing to do with inheritance, but with the fact that the initialization of A::count is constant initialization, which is guaranteed to be done before dynamic initialization, which is what B::id uses.

但是除非你完全掌握了这些内部结构,否则我建议你使用construct on first use 习语。

在这种情况下没问题,但要注意多线程上下文中的 A::register_subclass 等函数。如果多个线程同时调用它,任何事情都可能发生。

I am wondering though if the rule that "initialization order is undefined across translation units" still holds for static members in a parent class which are needed by static members in a child class.

是的,确实如此。

静态数据成员与继承层次结构(或者,实际上,它们的封装 类 相关)的唯一方式是它们的完全限定名称;它们的定义和初始化完全是unaware/uncaring this.