简体   繁体   English

什么使静态变量只初始化一次?

[英]What makes a static variable initialize only once?

I noticed that if you initialize a static variable in C++ in code, the initialization only runs the first time you run the function. 我注意到如果在代码中初始化C ++中的静态变量,初始化仅在您第一次运行该函数时运行。

That is cool, but how is that implemented? 这很酷,但是如何实现? Does it translate to some kind of twisted if statement? 它是否转化为某种扭曲的if语句? (if given a value, then ..) (如果给出一个值,那么..)

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

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

Yes, it does normally translate into an implicit if statement with an internal boolean flag. 是的,它通常会转换为带有内部布尔标志的隐式if语句。 So, in the most basic implementation your declaration normally translates into something like 因此,在最基本的实现中,您的声明通常会转换为类似的内容

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

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

  ...
} 

On top of that, if your static object has a non-trivial destructor, the language has to obey another rule: such static objects have to be destructed in the reverse order of their construction. 最重要的是,如果您的静态对象具有非平凡的析构函数,则该语言必须遵循另一个规则:这些静态对象必须按其构造的相反顺序进行破坏。 Since the construction order is only known at run-time, the destruction order becomes defined at run-time as well. 由于构造顺序仅在运行时已知,因此销毁顺序也在运行时定义。 So, every time you construct a local static object with non-trivial destructor, the program has to register it in some kind of linear container, which it will later use to destruct these objects in proper order. 因此,每次使用非平凡的析构函数构造本地静态对象时,程序必须将其注册到某种线性容器中,以后它将用于按正确的顺序销毁这些对象。

Needless to say, the actual details depend on implementation. 不用说,实际细节取决于实施。


It is worth adding that when it comes to static objects of "primitive" types (like int in your example) initialized with compile-time constants, the compiler is free to initialize that object at startup. 值得补充的是,当涉及使用编译时常量初始化的“原始”类型的静态对象(如示例中的int )时,编译器可以在启动时自由初始化该对象。 You will never notice the difference. 你永远不会注意到差异。 However, if you take a more complicated example with a "non-primitive" object 但是,如果您使用“非原始”对象采用更复杂的示例

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

then the above approach with if is what you should expect to find in the generated code even when the object is initialized with a compile-time constant. 然后上面使用if方法是你应该期望在生成的代码中找到的,即使用编译时常量初始化对象。

In your case the initializer is not known at compile time, which means that the compiler has to delay the initialization and use that implicit if . 在您的情况下,初始化程序在编译时是未知的,这意味着编译器必须延迟初始化并使用隐式if

Yes, the compiler usually generates a hidden boolean "has this been initialized?" 是的,编译器通常会生成一个隐藏的布尔值“这已被初始化了吗?” flag and an if that runs every time the function is executed. 标志和每次执行该函数时运行的if

There is more reading material here: How is static variable initialization implemented by the compiler? 这里有更多的阅读材料: 编译器如何实现静态变量初始化?

While it is indeed "some kind of twisted if", the twist may be more than you imagined... 虽然它确实是“某种扭曲的”,但扭曲可能比你想象的还要多......

ZoogieZork's comment on AndreyT's answer touches on an important aspect: the initialisation of static local variables - on some compilers including GCC - is by default thread safe (a compiler command-line option can disable it). ZoogieZork对AndreyT答案的评论涉及一个重要方面:静态局部变量的初始化 - 在某些编译器上包括GCC - 默认是线程安全的 (编译器命令行选项可以禁用它)。 Consequently, it's using some inter-thread synchronisation mechanism (a mutex or atomic operation of some kind) which can be relatively slow . 因此,它使用一些线程间同步机制(某种互斥或某种原子操作),这种机制可能相对较慢 If you wouldn't be comfortable - performance wise - with explicit use of such an operation in your function, then you should consider whether there's a lower-impact alternative to the lazy initialisation of the variable (ie explicitly construct it in a threadsafe way yourself somewhere just once). 如果你不熟悉 - 性能明智 - 在你的函数中明确使用这样的操作,那么你应该考虑是否有一个影响较小的替代变量的懒惰初始化(即你自己以线程安全的方式明确地构造它)某处只有一次)。 Very few functions are so performance sensitive that this matters though - don't let it spoil your day, or make your code more complicated, unless your programs too slow and your profiler's fingering that area. 很少有功能对性能非常敏感,但这很重要 - 不要让它破坏你的一天,或者让你的代码更复杂,除非你的程序太慢而你的探测器指的是那个区域。

They are initialized only once because that's what the C++ standard mandates. 它们只被初始化一次,因为这是C ++标准所要求的。 How this happens is entirely up to compiler vendors. 如何发生这完全取决于编译器供应商。 In my experience, a local hidden flag is generated and used by the compiler. 根据我的经验,编译器会生成并使用本地隐藏标志。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM