什么时候初始化静态C++类成员?

Ton*_*ony 60 c++ static initialization

似乎没有简单的答案,但是有没有可以安全地做出关于何时可以访问静态类字段的假设?

编辑:唯一安全的假设似乎是所有静态都在程序开始之前初始化(调用main).那么,只要我不参考其他静态初始化代码中的静态,我就没有什么可担心的了?

Tad*_*pec 55

该标准保证两件事 - 在同一翻译单元中定义的对象(通常表示.cpp文件)按其定义(而不是声明)的顺序初始化:

3.6.2

具有静态存储持续时间(basic.stc.static)的对象的存储应在任何其他初始化发生之前进行零初始化(dcl.init).使用常量表达式进行零初始化和初始化统称为静态初始化; 所有其他初始化是动态初始化.具有使用常量表达式(expr.const)初始化的静态存储持续时间的POD类型(basic.types)的对象应在任何动态初始化发生之前初始化.在同一翻译单元的命名空间范围内定义并动态初始化的静态存储持续时间的对象应按其定义出现在翻译单元中的顺序进行初始化.

另一个有保证的事情是,在使用来自此翻译单元的任何对象或函数之前,将从翻译单元初始化静态对象:

无论是否在main的第一个语句之前完成命名空间作用域对象的动态初始化(dcl.init,class.static,class.ctor,class.expl.init),它都是实现定义的.如果初始化延迟到main的第一个语句之后的某个时间点,则它应该在第一次使用与要初始化的对象相同的转换单元中定义的任何函数或对象之前发生.

我保证没有其他任何东西(特别是在不同翻译单元中定义的对象的初始化顺序是实现定义的).

编辑正如Suma的评论所指出的那样,它也保证在main输入之前进行初始化.

  • 由于EDIT,现在上面的答案中存在矛盾.第二个黄色框(引用标准)说"如果初始化被推迟到main()的第一个语句之后的某个时间点......",并且你添加了"保证在main()之前发生",这与它相矛盾,并没有引用标准. (3认同)

Chr*_*isW 19

它们在程序开始之前(即在main输入之前)初始化.

当单个CPP文件中存在两个或多个(静态数据的)定义时,它们将按照在文件中定义它们的顺序进行初始化(文件中先前/后面定义的那个在下一个之前初始化)一个是).

当在多个CPP文件中存在两个或多个(静态数据的)定义时,处理CPP文件的顺序是未定义/特定于实现的.如果全局变量的构造函数(在程序启动之前调用)引用另一个可能尚未构造的CPP文件中定义的全局变量,则会出现此问题.但是,Meyers的Effective C++(标题为确保全局对象在使用前已初始化)的 47项确实描述了一种解决方法......

  • 在头文件中定义一个静态变量(它是静态的,所以你可以有多个实例而不需要链接器抱怨)

  • 让该变量的构造函数调用您需要的任何东西(特别是构造在头文件中声明的全局单例)

...它说的是一种可以在某些系统头文件中使用的技术,例如确保cin在静态变量的构造函数使用它之前初始化全局变量.

  • 在Effective C++第3版中,它是第4项:确保在使用对象之前对其进行初始化.(并在那里讨论非本地静态变量,例如静态类成员) (2认同)
  • 并非所有静态对象都保证在进入 `main` 之前被初始化。参见 Tadeusz Kopec 对这个问题的回答,它引用了标准:“命名空间范围的对象的动态初始化(dcl.init、class.static、class.ctor、class.expl.init)是否是实现定义的在 main 的第一条语句之前完成。” (2认同)

Paa*_*ani 5

您在编辑中的最终结论是正确的。但问题是类静态本身。更容易说我的代码将具有不引用其他全局数据/类静态成员的类静态成员,但是一旦您采取这条路线,事情很快就会出错。我发现在实践中有用的一种方法是不使用类静态数据成员,而使用类静态包装方法。然后,这些方法可以将静态对象保存在其内部。例如

TypeX* Class2::getClass1Instance()
{
    static TypeX obj1;
    return &obj1;
}
Run Code Online (Sandbox Code Playgroud)

注意:之前的回答说:

另一个有保证的事情是,在使用该翻译单元中的任何对象或函数之前,将完成翻译单元中静态对象的初始化

这并不完全正确,并且此处推断的标准是错误的。如果在输入 main 之前调用翻译单元的函数,则这可能不成立。