类层次结构中的静态初始化顺序
Static initialization order in class heirarchy
我最近痛苦地意识到静态初始化顺序的惨败。我想知道"跨翻译单元未定义初始化顺序"的规则是否仍然适用于父类中的静态成员,而子类中的静态成员需要这些成员。
例如,假设我们有(为简洁起见,排除所有 # 守卫和包含(
// a.h
class A {
static int count;
static int register_subclass();
};
// a.cpp
int A::count = 0;
int A::register_subclass() {
return count ++;
}
然后是A
的一个子类,
// b.h
class B : public A {
static int id;
};
// b.cpp
int B::id = A::register_subclass();
这里有两个翻译单元,一个是静态对象,另一个是初始化时的静态对象......似乎这可能是静态初始化订单惨败的一个实例。
我的问题是:它真的安全吗?
也就是说,我是否可以保证B::id
在初始化A::count
之前不会包含从复制的垃圾?从我自己的测试来看,A
似乎总是首先初始化,但我不确定如何在初始化顺序中引入噪音以增加行为未定义时失败的可能性。
通常,依赖基类和派生类的静态初始化顺序是不安全的。不能保证A
的静态初始化将在B
之前发生。这就是静态初始化顺序惨败的定义。
您可以在首次使用惯用语时使用该构造:
// 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();
现场演示。
更新:然而,说到这里,波格丹在评论中指出
根据标准中的 [3.6.2],保证了此特定示例中的初始化顺序。它与继承无关,而是与
A::count
的初始化是常量初始化有关,保证在动态初始化之前完成,这是B::id
使用的。
但是,除非您完全掌握了此类内部功能,否则我建议您在首次使用惯用语时使用该构造。
在这种情况下没关系,但要小心多线程上下文中的A::register_subclass
等函数。如果多个线程同时调用它,则可能发生任何事情。
我想知道"跨翻译单元未定义初始化顺序"的规则是否仍然适用于父类中的静态成员,而子类中的静态成员需要这些成员。
是的,确实如此。
静态数据成员与继承层次结构(或者实际上,它们的封装类(相关的唯一方式是它们的完全限定名称;它们的定义和初始化完全不知道/不关心这一点。
- lambda 作为接受其他参数的参数的初始化顺序
- 大括号或等于初始值设定项初始化顺序
- C++ 模板中的静态常量初始化顺序
- 视觉C++:在 DLL 加载期间,全局变量初始化顺序是否具有确定性?
- 销毁 pthread 互斥体和 C++ 中的取消初始化顺序
- 线程局部变量的初始化顺序
- 类静态变量初始化顺序
- 具有静态存储持续时间的常量初始化变量的初始化顺序
- 解析 CRTP 初始化顺序
- 内联初始化的静态 const 类成员的初始化顺序保证
- 使用constexpr的全局初始化顺序
- 初始化与类类型相同的静态成员(静态初始化顺序问题)
- 为什么结构属性声明和初始化顺序的行为是这样的?
- 影响初始化顺序(以C++为单位)
- MSVC 2017 在单个翻译单元中违反静态初始化顺序
- C++11 成员类初始化顺序
- 同一函数中的静态函数变量初始化顺序
- 静态内联成员初始化顺序
- C++静态初始化顺序:添加到映射中
- 静态成员的静态阵列:初始化顺序惨败的可能性