我注意到,如果您在代码中初始化 C++ 中的静态变量,则初始化仅在您第一次运行该函数时运行。
这很酷,但它是如何实现的?它会转化为某种扭曲的 if 语句吗?(如果给定一个值,那么..)
void go( int x )
{
static int j = x ;
cout << ++j << endl ; // see 6, 7, 8
}
int main()
{
go( 5 ) ;
go( 5 ) ;
go( 5 ) ;
}
我注意到,如果您在代码中初始化 C++ 中的静态变量,则初始化仅在您第一次运行该函数时运行。
这很酷,但它是如何实现的?它会转化为某种扭曲的 if 语句吗?(如果给定一个值,那么..)
void go( int x )
{
static int j = x ;
cout << ++j << endl ; // see 6, 7, 8
}
int main()
{
go( 5 ) ;
go( 5 ) ;
go( 5 ) ;
}
是的,它通常会转换为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
。
是的,编译器通常会生成一个隐藏的布尔值“这已经初始化了吗?” flag 和 anif
每次执行函数时都会运行。
这里有更多阅读材料:编译器如何实现静态变量初始化?
虽然它确实是“某种扭曲的如果”,但扭曲可能比你想象的要多……
ZoogieZork 对 AndreyT 的回答的评论触及了一个重要方面:静态局部变量的初始化- 在包括 GCC 在内的一些编译器上 -默认情况下是线程安全的(编译器命令行选项可以禁用它)。因此,它使用了一些线程间同步机制(某种互斥锁或原子操作),这可能相对较慢. 如果您对在函数中显式使用此类操作感到不舒服 - 性能方面的 - 那么您应该考虑是否有一个影响较小的替代变量的惰性初始化(即自己以线程安全的方式显式构建它某处只有一次)。很少有函数对性能如此敏感,但这一点很重要——不要让它破坏你的一天,或者让你的代码更复杂,除非你的程序太慢并且你的分析器在那个区域指法。
它们只初始化一次,因为这是 C++ 标准要求的。这种情况如何发生完全取决于编译器供应商。根据我的经验,编译器会生成并使用本地隐藏标志。