静态类初始化阶段

问题描述:

我最近变得痛苦地意识到Static Initialization Order Fiasco。我想知道,如果“初始化顺序在翻译单元中未定义”的规则仍然适用于子类中的静态成员所需的父类中的静态成员。静态类初始化阶段

例如,假设我们(不包括,为简便起见,所有的#警卫和包括)

// 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似乎总是首先被初始化,但我不确定如何在初始化顺序中引入噪声,以在行为未定义时增加失败的可能性。

+3

否。将a.cpp和b.cpp链接到可执行文件的顺序*不*保证。 *表示“初始化顺序失败”。 (您的链接器可能使用字母顺序,或者它可能不会)。 –

+0

@BoPersson我害怕这一点。谢谢。让这个答案(也许提供一个参考?),我会将它标记为接受 – stett

+0

@BoPersson:答案去那里好友↓↓↓↓↓↓↓↓↓↓ –

一般来说,它是不能完全依靠一个基类和派生类的静态初始化顺序。不能保证A的静态初始化将在B之前发生。这是static initialization order fiasco的定义。根据然而,他说,波格丹在评论中指出

您可以使用结构在第一次使用成语:

// 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.

更新标准中的[3.6.2]中,本具体示例中的初始化顺序是有保证的。它与继承无关,但与A::count的初始化是不断初始化,这是保证在动态初始化之前完成的事实,这是B::id使用什么。

但是,除非你有这样intracaccies的完整把握,我建议你使用结构在第一次使用成语。

而正是在这种情况下确定,但在多线程方面要小心像A::register_subclass功能。如果多个线程同时调用它,则会发生任何事情

+0

谢谢 - 这实际上是我最终使用的解决方案。这太糟糕了,有必要在我可怜的简单的小变量周围添加那些令人讨厌的静态函数。 – stett

+0

实际上,根据标准中的[3.6.2],OP的具体示例中的初始化顺序是有保证的。它与继承没有任何关系,但事实上'A :: count'的初始化是*常量初始化*,这是保证在动态初始化之前完成的,这是'B :: id'使用的。然而,'count ++'可能会在线程中出现问题 - 'B :: id'和其他一些'C :: id'的初始化可能会同时运行。 cc @stett – bogdan

+0

使用本地'静态'的建议通常很合理,但为了准确性,我认为可能需要在答案中澄清上面的细节。 – bogdan

我在想,如果“初始化顺序在转换单元中未定义”的规则仍然适用于子类中的静态成员所需的父类中的静态成员。

是的,它的作用。

静态数据成员与继承层次结构(或者真的,它们的封装类完全相关)的唯一方法是完全限定名称;他们的定义和初始化完全没有意识到/不理解这一点。