2011-04-06 124 views
40

我注意到如果你用C++在代码中初始化一个静态变量,初始化只在你第一次运行该函数时运行。是什么让一个静态变量只初始化一次?

这很酷,但是如何实施?它是否会转化为某种扭曲的陈述? (如果给定的值,然后..)

void go(int x) 
{ 
    static int j = x ; 
    cout << ++j << endl ; // see 6, 7, 8 
} 

int main() 
{ 
    go(5) ; 
    go(5) ; 
    go(5) ; 
} 
+6

它是如何实现_什么编译器_? – 2011-04-06 13:59:32

回答

48

是,它并通常翻译成隐if语句内部布尔标志。因此,在最基本的实现你的宣言通常转化为类似

void go(int x) { 
    static int j; 
    static bool j_initialized; 

    if (!j_initialized) { 
    j = x; 
    j_initialized = true; 
    } 

    ... 
} 

最重要的是,如果你的静态对象有一个不平凡的析构函数,语言必须遵循另一条规则:这样的静态对象必须按照其建设的相反顺序进行破坏。由于构建顺序仅在运行时才知道,因此销毁顺序也在运行时定义。所以,每次用非平凡的析构函数构造一个局部静态对象时,程序都必须将它注册到某种线性容器中,稍后它将使用这个容器以适当的顺序破坏这些对象。

不用说,实际的细节取决于实施。


值得一补充说,当涉及到“原始”类型(如在你的榜样int)与编译时间常数初始化的静态对象,编译器是免费的,在启动时初始化对象。你永远不会注意到这种差异。但是,如果你把一个“非基本”对象

void go(int x) { 
    static std::string s = "Hello World!"; 
    ... 

一个更复杂的例子,然后用if上述做法是,你应该期望在生成的代码中查找,即使对象与编译初始化什么时间常数。

在你的情况下,初始化器在编译时不知道,这意味着编译器必须延迟初始化并使用隐含的if

+2

我认为静态是真正的全球性,这就是为什么它们在线程之间共享......而且作为全局的,它们只初始化一次...... – dicroce 2011-04-06 14:00:22

+3

@dicroce,函数内部的静态变量与真实全局变量不同:它们被懒惰地初始化,在封闭函数的第一次调用时。因此你需要在函数中进行某种形式的检查。 – 2011-04-06 14:02:02

+1

@dicroce:本地静态数据初始化*最多*一次。但事先并不知道*他们会被初始化。并且它们可能根本不会被初始化(如果控件从未运行过声明)。 – AnT 2011-04-06 14:04:19

2

虽然这的确是“某种扭曲,如果”扭曲可能会超过你想象的......

ZoogieZork对AndreyT的回答评论倒是一个重要的方面:初始化静态局部变量 - 在一些编译器包括GCC - 默认线程安全(编译器命令行选项可以禁用它)。因此,它使用一些线程间同步机制(一种互斥体或某种原子操作),它可能是相对较慢的。如果你不习惯 - 性能明智 - 在你的函数中明确使用这样的操作,那么你应该考虑是否有一个对变量的惰性初始化影响较小的替代方案(即,自己以线程安全的方式明确构建它)某处只有一次)。很少有功能对性能非常敏感,所以尽管这很重要 - 不要让它破坏你的一天,或者让你的代码更加复杂,除非你的程序太慢了,你的分析器会指责这个区域。

+0

'在某些编译器上是错误的:**是强制**,静态初始化是线程安全的。请参阅:http://stackoverflow.com/q/8102125/2757035 – 2017-01-24 20:53:09

+1

@underscore_d:这个答案是为C++ 03编写的,如果你想说改变了C++ 11以后的事情。 – 2017-02-16 06:45:38

1

它们仅初始化一次,因为这是C++标准的要求。这如何发生完全取决于编译器供应商。根据我的经验,编译器会生成并使用本地隐藏标志。