静态成员在构造函数调用之前初始化吗?

4

我有一个类,其中包含一个std::map静态成员。我在同一翻译单元(即相同的cpp文件)中在构造函数体实现之前初始化它。但是我的程序没有输出结果。我发现了静态初始化顺序灾难的问题,但我不认为这可能是原因。

class Test {
public:
    static std::map<std::string, Test*> a;
    Test(std::string ID) {
        /* in my complete code  (where constructor
         * implementation and map initialization
         * are in a separate Test.cpp file), this fails, maybe
         * because the map is not initialized at the time
         * the constructor is being called by a sub class of Test */
        a.insert({ID, this});
    }
};

当我进行其他变量的静态初始化时,Test的构造函数被Subclass调用。是否存在在映射初始化之前调用Test构造函数的情况?

4个回答

3
“Test”的构造函数在地图初始化之前被调用的情况有吗?当然有。唯一需要的是除了定义地图的翻译单元之外,还存在Test的静态实例。(Test的构造函数定义在哪里并不重要。重要的是定义Test的静态实例的位置。)
还有其他可能的情况:例如,某些其他静态对象的构造函数使用了Test的局部实例。
解决这个问题的常见方法是为地图使用工厂方法:
std::map<std::string, Test*>& Test::registry()
{
    static std::map<std::string, Test*> theOneAndOnly;
    return theOneAndOnly;
}

这将导致地图在首次需要时被构建。

你使用的是哪个编译器?除非成员是const并且具有整数或枚举类型,否则无法在类定义中为静态成员指定初始化程序。


我使用gcc 4.9.2 Windows移植版(mingw-w64)。 我在类的cpp文件中使用默认构造函数初始化map,即mymap = {};。如果不这样做,就会出现链接器错误。另一个问题:如何在不执行构造函数代码的情况下构造对象,它怎么可能是无关的? - barsdeveloper
这就是你应该做的。在你的问题中,你在类定义中进行了初始化;这是不合法的。 - James Kanze
当对象被构造时,构造函数代码将被执行。但是构造函数代码位于何处(在哪个翻译单元中等)并不重要;重要的是正在初始化的对象的定义位于何处;除非它与地图的定义位于同一翻译单元中,否则初始化顺序未指定。 - James Kanze

1
在C++类中的static声明定义了一个“属于”该类本身而不是从该类创建的对象的对象。这意味着任何static成员的初始化都应该在Cpp文件中声明。
使用以下语法:
std::map<std::string, Test*> Test::a = {};

关于你的问题,答案是否定的。所有的static成员都会在任何类构造函数被调用之前被初始化。


2
除非它们不是。 - James Kanze
不是这样。如果你在同一个翻译单元中先定义了一个静态实例,那么在静态成员之前实例将被初始化(调用构造函数)。 - Mike Seymour
@MikeSeymour - 我理解你的意思,确实如果有人在映射初始化之前声明了一个静态Test对象,那么它将首先被调用。 - NirMH
如果它们在不同的翻译单元中定义,那么就没有指定哪一个会被首先调用。 - James Kanze

0

这是标准规定的内容:

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


我认为[basic.start.init]/4是更相关的段落。 - Kerrek SB

0

解决问题的简单方法是将您的地图设为某个函数的static局部变量,并返回对其的引用。此函数可以是翻译单元私有函数或static类方法。

一般来说,跨翻译单元之间的static全局变量依赖是不好的,因为初始化顺序在所有情况下都没有完全定义。

static局部变量在标准下被使用之前会被初始化一次。请注意,旧版本的Visual Studio存在多线程初始化问题,但新版本没有这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接