再现静态初始化顺序的悲剧在C ++(Reproducing static initializatio

2019-10-18 13:31发布

我读到静态初始化顺序的悲剧在C ++中有关应用程序的崩溃。 我想我明白,但仍然有几个问题:
1)如果我想重现这个问题,我该怎么做(让我的程序应该崩溃)? 我想编写一个测试程序,以重现崩溃。 能否请您提供如果可能的源代码?
2)我读这个C ++ FAQ精简版文章,它说,它有两个静态对象x和y,在两个不同的文件和y调用X的方法。 它怎么可能为全球的静态成员的文件级别范围是什么?
3)这个问题是非常危险的,有没有试图修复它的编译器的水平?
4)有多少次,你的C ++专家在面对现实生活的生产这个问题?

Answer 1:

编辑:调整以使其在评论光线更准确。

一个很好的例子是这样的:

// A.cpp
#include "A.h"
std::map<int, int> my_map;

// A.h
#include <map>
extern std::map<int, int> my_map;

// B.cpp
#include "A.h"
class T {
public:
    T() { my_map.insert(std::make_pair(0, 0)); }
};

T t;

int main() {
}

问题是,该实例t可以在之前被构造my_map对象。 因此,可以在尚未将构建对象上发生的插入。 导致飞机坠毁。

一个简单的解决办法是做这样的事情,而不是:

// A.h
#include <map>
std::map<int, int> &my_map()

// A.cpp
#include "A.h"
std::map<int, int> &my_map() {
    // initialized on first use
    static std::map<int, int> x;
    return x;
}

// B.cpp
#include "A.h"

class T {
public:
    T() { my_map().insert(std::make_pair(0, 0)); }
};

T t;

int main() {
}

通过通过函数访问静态对象,我们可以保证初始化的顺序,因为功能范围静是第一次使用时初始化。 因此, t对象首先构造,它调用my_map()这产生在其第一运行一个静态地图对象,然后返回对它的引用。



Answer 2:

1)你会要么必须检查运行时启动代码,看看它是如何选择初始化的顺序,或试验了一下。 您可以通过两个以上的对象,也许是3或4之间创建初始化相依关系改善具有错误的几率。

2)只是实例在文件级的对象:

OBJECT_TYPE x;

3)没有编译器解决了这个,我知道的。 这需要在或链接后检测。

4)在实践中,很容易避免:让所有的初始化自成体系。



Answer 3:

“1)如果我想重现这个问题,我该怎么做(让我的程序应该崩溃)?我想编写一个测试程序,以重现崩溃,你能请尽可能提供源代码?”

你不能写一个便携式测试案例。 静态初始化顺序的悲剧是为了没有定义。 当有人写代码,如果他们在一个法律秩序正在初始化,将工作,但如果他们在其他一些法律秩序正在初始化失败,会出现问题。 所以,你不能保证它会工作,同样的原因,你不能保证它会失败。 这是整点。

也许你可以采取猜测链接器将初始化所有从一个转换单元中,全局从另一个所有全局之前。 所以设置了两个源文件A和B,具有全局A1和A2在A和B1和B2 B.然后使用B1(我的意思是,“做的东西,如果B1尚未初始化从而未能”)在构造A1,并在B2的构造使用A2。 此外,在A2的构造函数中使用A1(和在所述的该顺序声明它们)。 然后将不会失败的唯一顺序是B1,A1,A2,B2,你可以想象是实施做出漂亮不太可能的选择。 在一个特定的实现,如果它不以某种方式成功,转的东西让A2使用B2而是采用A2 B2的,只是希望不改变初始化顺序。

当然,你也可以在B1的构造函数中使用B2(并在B中命令宣布它们),以保证故障不管初始化顺序。 但是,那将不是静态初始化顺序的悲剧,这将只是一个从根本上打破循环依赖。

“2)我读这个C ++ FAQ精简版文章,它说,它有两个静态对象x和y,在两个不同的文件和y调用X的方法。它怎么可能为全球的静态成员的文件级别范围是什么?”

例如声明它们extern在两个翻译单元(可能使用一个共同的报头)。 适用范围,联动和储存时间都是不同的事情。

“3)这个问题是非常危险的,有没有试图修复它的编译器的水平?”

从来没听说过。 我敢肯定它的制定是否不反对X“用途”(在我上面定义的意义上)的对象Ÿ在其构造停机问题,在链接时和T-排序它会在如此构建依赖图最好的局部量度。

“4)你ç多少次++专家在面对现实生活的生产这个问题?”

从来没有,因为(a)我不离开全局躺在身边,和(b)当我用他们,我避免做任何事情在他们的初始化看中。 基本上,没有设计一个类,然后再决定有它的一个全局实例 - 如果你打算使用一个全局对象,设计它作为一个全球性的。 并尽可能使用本地范围的静态的而非全局的静态。 如果您需要提供的东西,看起来像一个全球性的,发布它作为返回对象的引用的功能,或作为对象,它们可以在栈上创建,并呼吁他们的功能,然后充当代理(或手柄,如果你喜欢)的全局状态。 你还担心线程安全的,但线程环境提供的方式来管理,而没有办法来管理比让你的来电者在他们的翻译单位来定义你的全局其他的惨败。

它只有当你正在实施它定义全局变量,像一个API获取困难std::out 。 还有,你可以使用,你在其中宣布全球相同的标题定义一个虚拟文件范围的变量一招。 我不记得名字,虽然。



文章来源: Reproducing static initialization order fiasco in C++