2016-10-18 151 views
7

鉴于其从静态成员变量另一类初始化的静态成员变量静态成员变量相关的初始化静态,非文字struct ii有时缺省初始化为0333。这取决于编译或链接顺序。 证明:C++:与INT VS结构

class StaticClass: // file 'ONE.cpp/.hpp' 
    static int i = 2 
    static struct ii { int a = 333 } 

class ABC: // file 'abc.cpp' 
    static int abc_i = StaticClass::i // always 2 
    static struct abc_ii = StaticClass::ii // sometimes 0, sometimes 333 

调用g++ -std=c++11 abc.cpp ONE.cpp && ./a.out导致i = 2/ii = 0(GCC 4.8.1,同样与铛++ 3.7; -Wall -Wextra从不抱怨)。

但是打电话g++ -std=c++11 ONE.cpp abc.cpp && ./a.out结果i = 2/ii = 333

cat ONE.cpp abc.cpp > X.cpp && g++ X.cpp && ./a.out VS cat abc.cpp ONE.cpp > Y.cpp && g++ Y.cpp && ./a.out

删除包含在单个文件走动码,默认初始化:

同样串联的文件时这样或那样的情况与ONE.o abc.o VS abc.o ONE.o,也0当此订单存在时发生:

const OneI ABC::def_ii = StaticClass::ii; const OneI StaticClass::ii = OneI{333};

和一个333这个顺序:

const OneI StaticClass::ii = OneI{333}; const OneI ABC::def_ii = StaticClass::ii;

为什么这甚至有两个单独的编译单元发生的呢?这可以通过不断地执行后者来避免吗?在ABCStaticClass::ii安全使用静态指针(虽然我不想,但)?

完整的C++代码:


/* File: abc.cpp */ 

#include <iostream> 
#include "ONE.hpp" 

struct ABC { 
    ABC(); 

    static const int def_i; 
    static const OneI def_ii; 
    void arg_i(const int &x) { std::cout << "i = " << x << " ";}; 
    void arg_ii(const OneI &x) { std::cout << "/ ii = " << x.a << " ";}; 

}; 

ABC::ABC() { 
    arg_i(def_i); 
    arg_ii(def_ii); 
} 

const int ABC::def_i = StaticClass::i; 
const OneI ABC::def_ii = StaticClass::ii; 

int main() { 
    ABC a; 
    std::cout << '\n'; 
} 
/* End: abc.cpp */ 

/* File: ONE.cpp */ 

#include <iostream> 

#include "ONE.hpp" 

const int StaticClass::i = 2; 
const OneI StaticClass::ii = OneI{333}; 

/* End: ONE.cpp */ 

/* File: ONE.hpp */ 

#include <iostream> 

#ifndef One 
#define One 

struct OneI { 
    OneI(int a_) : a(a_) { } 
    int a; 
}; 

struct StaticClass { 
    const static int i; 
    const static OneI ii; 
}; 

#endif // One header guard 

/* End: ONE.hpp */ 

回答

4

恭喜!您遇到了static initialization order fiasco

静态对象的初始化顺序未在多个翻译单元之间定义。

StaticClass::iiONE.cpp定义和ABC::def_iiabc.cpp定义。因此StaticClass::ii可能会或可能不会在ABC::def_ii之前被初始化。由于ABC::def_ii的初始化使用StaticClass::ii的值,因此该值将取决于StaticClass::ii是否已初始化而尚未

定义了一个翻译单元中的静态对象的初始化顺序。对象按其定义的顺序进行初始化。因此,当您连接源文件时,会定义初始化的顺序。然而,当你以错误的顺序串联的文件中,定义的初始化命令是错误的:

const OneI ABC::def_ii = StaticClass::ii; // StaticClass::ii wasn't initialized yet 
const OneI StaticClass::ii = OneI{333}; 

可以这样通过强制执行后,命令所有的时间以某种方式避免?

最微不足道的解决方案是以正确的顺序在同一翻译单元中定义两个对象。更通用的解决方案是使用Construct On First Use Idiom初始化静态对象。

正在使用一个静态指针ABCStaticClass::ii安全(我宁愿不要,虽然)?

只要指针的解除引用值的静态对象的另一个翻译单元,其将尖锐的物体被定义,其中,是的,用指针代替ABC::def_ii将是安全的初始化过程中不被使用。


StaticClass::ii将一直零初始化期间静态初始化阶段††。静态初始化顺序失败涉及动态初始化阶段††

†† C++标准草案[basic.start.static]

如果不执行恒定的初始化,具有静态存储的持续时间([basic.stc.static])或线程存储时限([basic.stc的变量。线程])是零初始化的([dcl.init])。一起,零初始化和常量初始化被称为静态初始化;其他所有初始化都是动态初始化静态初始化应在任何动态初始化发生之前执行。 [注意:非局部变量的动态初始化在[basic.start.dynamic]中描述;本地静态变量的描述在[stmt.dcl]中描述。 - 结束注释]

+0

您可能想详细说明静态和动态初始化阶段以及如何建立所需的动态初始化顺序。否则你的答案不到半个故事。 –

+0

@MaximEgorushkin我添加了关于静态和动态初始化的详细说明。虽然它确实有助于理解为什么未初始化的'StaticClass :: ii'的值必须为0,我不同意它对这个问题的重要性。 – user2079303

+0

是命令a)零初始化,b)静态初始化,c)动态初始化?而且由于'const int StaticClass :: i = 2'是b),什么是'const int ABC :: def_i = StaticClass :: i'?动态我会猜测。 – toting