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