静态初始化顺序惨败
Eckel 在他的“Thinking in C++”(第 10 章)中描述了一种由 Jerry Schwarz 首创的解决这一惨败的技术。 他说,如果我们想要将 x 初始化为 100,将 y 初始化为 200 并在所有翻译单元之间共享它们,我们将创建一个如下所示的 Initializer.h
extern int x;
extern int y;
class Initializer {
static int initCount;
// if (initCount++ == 0) x = 100 & y = 200
/* ... */
};
static Initializer init;
:我们有实现文件
#include "Initializer.h"
int x;
int y;
int Initializer::initCount;
,Eckel 说“静态初始化(在实现文件中)将强制所有这些值为零”。
让我考虑以下情况:编译器在包含该标头的其他一些文件之后处理实现文件(这意味着 x 和 y 已在该其他文件中设置为 100 和 200)。编译器看到int x
,那么它会做什么呢?它将 x 和 y 设置为零以消除初始化和先前文件中所有可能的更改吗?但如果确实如此,那么 initCount 也将被设置为零,从而破坏整个技术。
In his "Thinking in C++" (Chapter 10) Eckel describes a technique that was pioneered by Jerry Schwarz to solve the fiasco.
He says that if we want to initialize x to 100 and y to 200 and share them among all translation units, we create an Initializer.h that looks like this:
extern int x;
extern int y;
class Initializer {
static int initCount;
// if (initCount++ == 0) x = 100 & y = 200
/* ... */
};
static Initializer init;
And in implementation file we have
#include "Initializer.h"
int x;
int y;
int Initializer::initCount;
and Eckel says that "static initialization (in implementation file) will force all these values to zero".
Let me consider the following case: the compiler processes the implementation file after some other file with that header included (it means that x and y have been already set to 100 and 200 in that other file). The compiler sees int x
, so what will it do? Will it set x and y to zero eliminating initialization and all possible changes in previous files? But if it does, then initCount
will also be set to zero, breaking down the whole technique.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我不确定你的意思是什么。如果
x
和y
在其他文件中定义,则会出现链接器冲突,并且程序根本无法编译。如果
x
、y
以及最重要的Initializer::initCount
以这种方式实现,那么程序中将会有它们的唯一实例;它们实际上是全局的,并且将在程序启动时、在构造任何Initializer
之前初始化为0
(由于包含声明<该类的 code>static 实例)。每个静态初始化器
的构造都会首先检查是否由于if (initCount++ == 0)
等而构造了任何其他初始化器
。因此,第一个运行的
Initializer
构造函数(仍在进入main
之前)将设置所有三个值。I'm not sure what you mean by this. If
x
andy
are defined in other files, then you have a linker clash and the program simply won't compile.If
x
,y
and most importantlyInitializer::initCount
are implemented in this way, there will be unique instances of them in the program; they are effectively global and will be initialized to0
at program start, before anyInitializer
is constructed (due to inclusion of the header declaring astatic
instance of that class). Each construction of astatic Initializer
will first check whether any otherInitializer
s have been constructed due to theif (initCount++ == 0)
etc.The first
Initializer
ctor to run (still before enteringmain
) will thus set all three values.“初始化器”中所做的是赋值,而不是初始化(假设语法有效)。
因此,它“解决”了您的特殊情况的静态初始化顺序失败,因为首先没有失败。 x 和 y 是整数,它们不会在不可预测的时间互相调用,而且最重要的是它们也位于同一个翻译单元中。编译器只会正确初始化它们。如果您之后按照定义的顺序分配值也很好,但这只是更复杂,而不是更好。
为了出现静态初始化顺序惨败,您需要这样的情况:x 的构造函数需要 y 的值(或相反),并且它们位于不同的翻译单元中。因此,这是否有效的可能性是 50:50。
现在,“初始化器”结构将按照定义的顺序正确分配值,但那时,x和y的构造函数已经运行,因为你不能分配给尚未构造的内容...因此,如果问题存在的话,它根本无法避免问题。
首次使用时构造是处理此问题的常用方法。该技术有不同的风格(每种都有自己的优点和缺点),例如:
What is done in "Initializer" is assignment, not initialization (assuming valid syntax).
As such, it "solves" the static initialization order fiasco for your special case, because there is no fiasco in the first place. x and y are integers, they don't call each other at unpredictable times, and on top of that they live in the same translation unit too. The compiler will just initialize them proper. It's fine if you assign values in a defined order afterwards, but it's only more complex, not any better.
For the static initialization order fiasco to appear, you would need a situation like: constructor of x needs the value of y (or the other way around) and they are in different translation units. Therefore, it's a 50:50 chance whether this works or not.
Now, the "Initializer" struct will correctly assign values in a defined order, but at that time, the constructors of x and y have already run, because you can't assign to what has not been constructed... so it would not avoid the problem at all, if it existed.
Construct on first use is the common way of dealing with this problem. There are different flavours (each with its own advantages and disadvantages) of that technique, such as for example:
假设您的意思是在其他源文件中的 static-init 范围内任何可能的使用和初始化,那么您是绝对正确的:如果编译器决定在其他文件中运行此文件的静态初始化之后,那么您将撤消其他工作。
在许多情况下,您可以通过根本不使用全局/静态来为自己省去很多麻烦。
Assuming you mean any possible use and initialization at static-init scope in other source files then you're absolutely correct: If the compiler decided to run this file's static initialization after that in other files then you'll undo that other work.
In many cases you can save yourself a tremendous amount of headaches by just not using globals/statics at all.
加载程序时,在执行任何代码之前,全局 x 和 y 将初始化为零。当创建任何初始化程序时,x 和 y 已初始化为零。事情按这个顺序发生:
The global x and y will be initialized to zero when the program is loaded, before any code was executed. When any Initializer is created, x and y are already initialized to zero. Things happen in that order:
为什么不声明(在文件范围内,在单个翻译单元中):
x 和 y 将存储在图像的读/写部分中,以便它们在进程中的任何代码执行之前初始化。您无需担心普通旧数据的初始化顺序。
Why not declare (at file scope, in a single translation unit):
x and y will be stored in the image's read/write section so they are initialised before any code in the process executes. You don't need to worry about initialisation order for plain-old-data.