如果变量声明为static
在函数的作用域中,则仅初始化一次并在函数调用之间保留其值.它的生命到底是什么?它的构造函数和析构函数何时被调用?
void foo() { static string plonk = "When will I die?"; }
Motti.. 243
函数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:> sample.exe
在foo
中创建在foo 中销毁C:> sample.exe 1
如果
在foo中创建,则创建在foo
中
销毁在if 中销毁C:> sample.exe 1 2
在foo中
创建如果在foo 中被
销毁则在
Destroyed中 创建
[0]
由于C++ 98 [2]没有引用多线程如何在多线程环境中表现如何是未指定的,并且可能存在问题,正如Roddy所提到的.
[1]
C++ 98部分3.6.3.1
[basic.start.term]
[2]
在C++ 11中,静态以线程安全方式初始化,这也称为Magic Statics.
函数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:> sample.exe
在foo
中创建在foo 中销毁C:> sample.exe 1
如果
在foo中创建,则创建在foo
中
销毁在if 中销毁C:> sample.exe 1 2
在foo中
创建如果在foo 中被
销毁则在
Destroyed中 创建
[0]
由于C++ 98 [2]没有引用多线程如何在多线程环境中表现如何是未指定的,并且可能存在问题,正如Roddy所提到的.
[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
......这是不依赖销毁令的另一个原因!