如果一个变量被声明为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:>sample.exe在 foo 中
创建 在 foo
中销毁C:>sample.exe 1
创建于 if
创建于 foo
销毁于 foo
销毁于 ifC:>sample.exe 1 2
Created in foo
Created in if
Destroyed in if
Destroyed in foo
[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
……这也是不依赖破坏令的另一个原因!
The Static variables are come into play once the program execution starts and it remain available till the program execution ends.
The Static variables are created in the Data Segment of the Memory.