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

时间:2023-01-15 10:36:44

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 given a value, then ..)

这很酷,但这是如何实现的呢?它会转化成某种扭曲的if语句吗?(如果给定一个值,那么…)

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

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

4 个解决方案

#1


49  

Yes, it does normally translate into an implicit if statement with an internal boolean flag. So, in the most basic implementation your declaration normally translates into something like

是的,它通常会转换为带有内部布尔标志的隐式if语句。因此,在最基本的实现中,您的声明通常可以转换为类似的内容

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. You will never notice the difference. However, if you take a more complicated example with a "non-primitive" object

值得补充的是,当涉及到使用编译时常量初始化的“原始”类型的静态对象(如示例中的int)时,编译器可以在启动时*初始化该对象。你永远也不会注意到其中的差别。但是,如果您使用一个“非原始”对象的更复杂的示例

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。

#2


7  

Yes, the compiler usually generates a hidden boolean "has this been initialized?" flag and an if that runs every time the function is executed.

是的,编译器通常会生成一个隐藏的布尔值“它初始化了吗?”

There is more reading material here: How is static variable initialization implemented by the compiler?

这里有更多的阅读材料:编译器如何实现静态变量初始化?

#3


2  

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). 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 (i.e. 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.

ZoogieZork对AndreyT的回答的评论涉及到一个重要的方面:静态本地变量的初始化(包括GCC)是默认的线程安全(编译器命令行选项可以禁用它)。因此,它使用一些线程间同步机制(互斥体或某种原子操作),这种机制可能比较慢。如果您对在函数中显式地使用这样的操作感到不舒服(在性能上),那么您应该考虑是否有一种影响较小的替代方法可以替代变量的惰性初始化(例如,您可以自己在某个地方以线程安全的方式显式地构造它,只需一次)。很少有函数的性能如此敏感,这很重要——不要让它破坏您的一天,或者使您的代码更复杂,除非您的程序太慢,而您的分析器正在处理该区域。

#4


1  

They are initialized only once because that's what the C++ standard mandates. How this happens is entirely up to compiler vendors. In my experience, a local hidden flag is generated and used by the compiler.

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

#1


49  

Yes, it does normally translate into an implicit if statement with an internal boolean flag. So, in the most basic implementation your declaration normally translates into something like

是的,它通常会转换为带有内部布尔标志的隐式if语句。因此,在最基本的实现中,您的声明通常可以转换为类似的内容

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. You will never notice the difference. However, if you take a more complicated example with a "non-primitive" object

值得补充的是,当涉及到使用编译时常量初始化的“原始”类型的静态对象(如示例中的int)时,编译器可以在启动时*初始化该对象。你永远也不会注意到其中的差别。但是,如果您使用一个“非原始”对象的更复杂的示例

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。

#2


7  

Yes, the compiler usually generates a hidden boolean "has this been initialized?" flag and an if that runs every time the function is executed.

是的,编译器通常会生成一个隐藏的布尔值“它初始化了吗?”

There is more reading material here: How is static variable initialization implemented by the compiler?

这里有更多的阅读材料:编译器如何实现静态变量初始化?

#3


2  

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). 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 (i.e. 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.

ZoogieZork对AndreyT的回答的评论涉及到一个重要的方面:静态本地变量的初始化(包括GCC)是默认的线程安全(编译器命令行选项可以禁用它)。因此,它使用一些线程间同步机制(互斥体或某种原子操作),这种机制可能比较慢。如果您对在函数中显式地使用这样的操作感到不舒服(在性能上),那么您应该考虑是否有一种影响较小的替代方法可以替代变量的惰性初始化(例如,您可以自己在某个地方以线程安全的方式显式地构造它,只需一次)。很少有函数的性能如此敏感,这很重要——不要让它破坏您的一天,或者使您的代码更复杂,除非您的程序太慢,而您的分析器正在处理该区域。

#4


1  

They are initialized only once because that's what the C++ standard mandates. How this happens is entirely up to compiler vendors. In my experience, a local hidden flag is generated and used by the compiler.

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