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

时间:2009-09-14 13:51:15

标签: c++ static initialization

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

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

7 个答案:

答案 0 :(得分:53)

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

3.6.2

  

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

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

  

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

我无法保证(特别是在不同翻译单元中定义的对象的初始化顺序是实现定义的。)

EDIT 正如Suma的评论所指出的,它也保证在输入main之前初始化它们。

答案 1 :(得分:17)

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

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

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

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

  • 让该变量的构造函数调用您需要的任何内容(特别是构造标题中声明的全局单例)

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

答案 2 :(得分:3)

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

TypeX* Class2::getClass1Instance()
{
    static TypeX obj1;
    return &obj1;
}

TypeX* Class2::getClass1Instance() { static TypeX obj1; return &obj1; } 注意:早先的回答是:

  

另一个有保障的是静态对象的初始化   来自翻译单位将在使用任何对象之前完成或   这个翻译单位的功能

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

答案 3 :(得分:1)

我相信它可以在执行期间随时访问。尚未定义的是静态变量的初始化顺序。

答案 4 :(得分:1)

可以在实现文件(.c / cpp / cc)文件中初始化它们。不要在.h中初始化它们,因为编译器会抱怨多个定义。

它们通常在main之前初始化,但是顺序是未知的,因此避免依赖。它们当然可以在成员函数中访问。请记住,静态成员的初始化顺序是未知的。我建议将静态成员封装到静态函数中,该函数将检查成员是否已初始化。

答案 5 :(得分:0)

这个问题没有一个完全无关紧要的答案,但基本上它们是在控制传递到程序的入口点(主要)之前初始化的。初始化它们的顺序(据我所知)未定义,可能是特定于编译器的。

编辑:澄清一下,您添加的假设是正确的。只要您只在主要条目后访问它,您就不必担心它何时/如何初始化。它将在那时初始化。

答案 6 :(得分:-1)

我认为进程的主线程将按顺序执行以下五个步骤

  1. 初始化CRT库

  2. 静态初始化

  3. 执行main()函数

  4. 静态酉化

  5. CRT库的单元化

  6. 你想从其他静态初始化代码中获取参考静态吗? 也许以下代码有效:

    class A;
    static auto_ptr<A> a(auto_ptr<A>(&GetStaticA()));
    A &GetStaticA(void)
    {
        static A *a = NULL; //the static basic type variables initialized with constant experession will be initialized earlier than the other static ones
        if (a == NULL)
        {
             a = new A();
             return *a;
        }
    }