题
如果变量在函数的作用域中声明为 static
,则它只被初始化一次并在函数调用之间保留其值。它的生命到底是什么?什么时候调用它的构造函数和析构函数?
void foo()
{
static string plonk = "When will I die?";
}
解决方案
函数 static
变量的生命周期首次出现 [0] 程序流遇到声明,并在程序终止时结束。这意味着运行时必须执行一些簿记,以便仅在实际构造时才销毁它。
此外,由于标准规定静态对象的析构函数必须以完成构造 [1] 的相反顺序运行,并且构造的顺序可能取决于特定的程序运行,必须考虑施工顺序。
示例
struct emitter {
string str;
emitter(const string& s) : str(s) { cout << "Created " << str << endl; }
~emitter() { cout << "Destroyed " << str << endl; }
};
void foo(bool skip_first)
{
if (!skip_first)
static emitter a("in if");
static emitter b("in foo");
}
int main(int argc, char*[])
{
foo(argc != 2);
if (argc == 3)
foo(false);
}
<强>输出:强>
C:&GT; SAMPLE.EXE结果 创建于foo
在foo中被摧毁C:&gt; sample.exe 1
中被摧毁
如果创建的话 创建于foo
在foo中被摧毁
在C:&gt; sample.exe 1 2
创建于foo
如果创建的话 在if中被摧毁 在foo中被摧毁
[0]
因为 C ++ 98 [2] 没有引用多个线程如何在多线程中执行此操作线程环境未指定,并且可能有问题,因为罗迪提到。
[1]
C ++ 98 部分 3.6.3.1
[basic.start.term]
[2]
在C ++ 11中,静态以线程安全方式初始化,这也称为 Magic Statics 。
其他提示
Motti对订单是正确的,但还有其他一些事情需要考虑:
编译器通常使用隐藏标志变量来指示本地静态是否已经初始化,并且在该函数的每个条目上都检查此标志。显然这是一个很小的性能损失,但更令人担忧的是这个标志不能保证是线程安全的。
如果您有如上所述的本地静态,并且从多个线程调用 foo
,则可能会出现竞争条件导致 plonk
错误地或甚至多次初始化。此外,在这种情况下, plonk
可能会被与构造它的线程不同的线程破坏。
尽管标准说的是,我对局部静态破坏的实际顺序非常谨慎,因为你可能无意中依赖静态在被破坏后仍然有效,这很难跟踪下。
如果没有标准中的实际规则,现有的解释并不完全完整,见6.7:
在执行任何其他初始化之前,将执行具有静态存储持续时间或线程存储持续时间的所有块范围变量的零初始化。具有静态存储持续时间的块范围实体的常量初始化(如果适用)在首次输入其块之前执行。允许实现在允许实现静态初始化具有命名空间范围内的静态或线程存储持续时间的变量的相同条件下,使用静态或线程存储持续时间执行其他块范围变量的早期初始化。否则,在第一次控制通过其声明时初始化这样的变量;这样的变量在初始化完成后被认为是初始化的。如果初始化通过抛出异常退出,则初始化 未完成,因此下次控制进入声明时将再次尝试。如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成。如果控件在初始化变量时以递归方式重新输入声明,则行为未定义。
FWIW,Codegear C ++ Builder不会按照标准按预期顺序销毁。
C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if
......这是不依赖销毁令的另一个原因!
一旦程序执行开始,静态变量就会起作用,并且在程序执行结束之前它仍然可用。
静态变量在内存的数据段中创建。