静态初始化命令惨败

时间:2011-03-14 13:19:35

标签: c++ static initialization static-order-fiasco

在他的“用C ++思考”(第10章)中,Eckel描述了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也将设置为零,从而打破整个技术。

5 个答案:

答案 0 :(得分:4)

  

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

我不确定你的意思。如果在其他文件中定义了xy,则会发生链接器冲突,程序将无法编译。

如果xy,最重要的是Initializer::initCount以这种方式实施,程序中会有唯一的实例;它们实际上是全局的,并且会在程序启动时初始化为0之前构建任何Initializer(由于包含声明static实例的标头那个班)。 static Initializer的每个结构将首先检查是否由于Initializer等而构建了其他if (initCount++ == 0)

要运行的第一个Initializer ctor(仍然在输入main之前)将设置所有三个值。

答案 1 :(得分:3)

“初始化程序”中的操作是赋值,而不是初始化(假设语法有效)。

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

对于要出现的静态初始化顺序fiasco,您需要一种情况,例如:x的构造函数需要y的值(或相反的方式),并且它们位于不同的转换单元中。因此,这是否有效为50:50。

现在,“Initializer”结构将按照定义的顺序正确分配值,但此时,x和y的构造函数已经运行,因为你无法分配到尚未构建的内容......因此,如果它存在,它将无法避免该问题。

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

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

答案 2 :(得分:2)

假设你的意思是在其他源文件中静态初始化范围内任何可能的使用和初始化,那么你是绝对正确的:如果编译器决定在其他文件中运行此文件的静态初始化,那么你将撤消其他工作

在许多情况下,你可以通过根本不使用全局/静态来节省大量的麻烦。

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

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

int x = 100;
int y = 200;

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