在他的“思想在C++”(第10章)中,艾克尔描述了曾傑瑞施瓦茨开创的解决这场惨败的技术。他说,如果我们想要初始化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也将被设置为零,从而破坏了整个技术。
发布于 2011-03-14 13:28:08
如果是
,那么编译器会在另一个文件之后处理实现文件,而不是将x和y设置为零,从而消除初始化和以前文件中所有可能的更改?
我不知道你这么说是什么意思。如果x和y是在其他文件中定义的,那么您就会遇到链接器冲突,程序根本就不会编译。
如果x、y和最重要的Initializer::initCount是以这种方式实现的,那么程序中就会有它们的唯一实例;它们实际上是全局的,在构建任何Initializer之前,它们将被初始化为0 (由于包含声明该类的static实例的标题)。static Initializer的每个构造都将首先检查是否由于if (initCount++ == 0)等原因而构建了其他Initializer。
运行的第一个Initializer ctor (仍在输入main之前)将设置所有三个值。
发布于 2011-03-14 13:48:23
在“初始化程序”中所做的是赋值,而不是初始化(假设有效的语法)。
因此,它“解决”了特殊情况下的静态初始化顺序失败,因为从一开始就没有失败。X和y是整数,它们在不可预测的时间不相互调用,而且它们也生活在同一个翻译单元中。编译器将正确地初始化它们。如果事后按定义的顺序赋值,这很好,但它只是更复杂,而不是更好。
如果要出现静态初始化顺序失败,您需要这样的情况:x的构造函数需要y的值(或者相反的情况),并且它们位于不同的翻译单元中。因此,无论这是否有效,都有50:50的机会。
现在,“初始化器”结构将正确地按定义的顺序赋值,但此时,x和y的构造函数已经运行,因为您不能为未构造的值赋值.因此,如果这个问题存在的话,它根本就不会避免。
第一次使用结构是解决这一问题的常用方法。这种技术有不同的味道(各有其优缺点),例如:
x& get_x() { static x *xxx = new x(); return *xxx; }发布于 2011-03-14 13:44:15
假设您的意思是在其他源文件中在静态init范围内进行任何可能的使用和初始化,那么您就完全正确了:如果编译器决定在其他文件中运行该文件的静态初始化,那么您将撤销其他工作。
在很多情况下,只要完全不使用全局/静力学,你就可以节省大量的头痛。
https://stackoverflow.com/questions/5299095
复制相似问题