静态初始化顺序惨败

发布于 2024-10-21 20:38:47 字数 698 浏览 10 评论 0原文

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

锦欢 2024-10-28 20:38:47

但是如果这是真的,并且编译器在另一个文件之后处理实现文件,那么它会将 x 和 y 设置为零,从而消除初始化和先前文件中所有可能的更改?

我不确定你的意思是什么。如果 xy 在其他文件中定义,则会出现链接器冲突,并且程序根本无法编译。

如果xy以及最重要的Initializer::initCount以这种方式实现,那么程序中将会有它们的唯一实例;它们实际上是全局的,并且将在程序启动时、在构造任何Initializer之前初始化为0(由于包含声明<该类的 code>static 实例)。每个静态初始化器的构造都会首先检查是否由于if (initCount++ == 0)等而构造了任何其他初始化器

因此,第一个运行的 Initializer 构造函数(仍在进入 main 之前)将设置所有三个值。

But if it is true, and the compiler handles the implementation file after some another file, than it will set x and y to zero eliminating initialization and all possible changes in previous files?

I'm not sure what you mean by this. If x and y are defined in other files, then you have a linker clash and the program simply won't compile.

If x, y and most importantly Initializer::initCount are implemented in this way, there will be unique instances of them in the program; they are effectively global and will be initialized to 0 at program start, before any Initializer is constructed (due to inclusion of the header declaring a static instance of that class). Each construction of a static Initializer will first check whether any other Initializers have been constructed due to the if (initCount++ == 0) etc.

The first Initializer ctor to run (still before entering main) will thus set all three values.

反话 2024-10-28 20:38:47

“初始化器”中所做的是赋值,而不是初始化(假设语法有效)。

因此,它“解决”了您的特殊情况的静态初始化顺序失败,因为首先没有失败。 x 和 y 是整数,它们不会在不可预测的时间互相调用,而且最重要的是它们也位于同一个翻译单元中。编译器只会正确初始化它们。如果您之后按照定义的顺序分配值也很好,但这只是更复杂,而不是更好。

为了出现静态初始化顺序惨败,您需要这样的情况:x 的构造函数需要 y 的值(或相反),并且它们位于不同的翻译单元中。因此,这是否有效的可能性是 50:50。

现在,“初始化器”结构将按照定义的顺序正确分配值,但那时,x和y的构造函数已经运行,因为你不能分配给尚未构造的内容...因此,如果问题存在的话,它根本无法避免问题。

首次使用时构造是处理此问题的常用方法。该技术有不同的风格(每种都有自己的优点和缺点),例如:

x& get_x() { static x *xxx = new x(); return *xxx; }

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:

x& get_x() { static x *xxx = new x(); return *xxx; }
靑春怀旧 2024-10-28 20:38:47

假设您的意思是在其他源文件中的 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.

赠意 2024-10-28 20:38:47

加载程序时,在执行任何代码之前,全局 x 和 y 将初始化为零。当创建任何初始化程序时,x 和 y 已初始化为零。事情按这个顺序发生:

  1. 程序被加载
  2. 全局变量和静态变量被初始化为零(x 和 y 得到它们的 0 值)
  3. 构造全局对象(初始化程序将 x 和 y 设置为 100 和 200)

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:

  1. Program is loaded
  2. Global and static variables are zero initialized (x and y get their 0 values)
  3. Global objects are constructed (the Initializer sets x and y to 100 and 200)
離殇 2024-10-28 20:38:47

为什么不声明(在文件范围内,在单个翻译单元中):

int x = 100;
int y = 200;

x 和 y 将存储在图像的读/写部分中,以便它们在进程中的任何代码执行之前初始化。您无需担心普通旧数据的初始化顺序。

Why not declare (at file scope, in a single translation unit):

int x = 100;
int y = 200;

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文