如果一个变量在函数的作用域中声明为静态变量,它只初始化一次,并在函数调用之间保持其值。它的寿命到底有多长?什么时候调用它的构造函数和析构函数?
void foo()
{
static string plonk = "When will I die?";
}
如果一个变量在函数的作用域中声明为静态变量,它只初始化一次,并在函数调用之间保持其值。它的寿命到底有多长?什么时候调用它的构造函数和析构函数?
void foo()
{
static string plonk = "When will I die?";
}
当前回答
函数静态变量的生命周期在程序流[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中销毁 在if中销毁 C:>sample.exe 1 在foo中创建 在if中创建 在if中销毁 在foo中销毁
由于c++ 98[2]没有引用多线程,这将如何在多线程环境中表现是未指定的,并且可能是有问题的Roddy提到。
[1] cc+ +98第3.6.3.1节[基本启动]
在c++ 11中,静态数据是以线程安全的方式初始化的,这也被称为魔术静态数据。
其他回答
现有的解释是不完整的,没有来自标准的实际规则,在6.7中找到:
The zero-initialization of all block-scope variables with static storage duration or thread storage duration is performed before any other initialization takes place. Constant initialization of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered. An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope. Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.
总之,Codegear c++ Builder不会按照标准的预期顺序进行破坏。
C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if
... 这是另一个不依赖销毁令的原因!
Motti关于顺序的看法是正确的,但还有一些其他的事情需要考虑:
编译器通常使用一个隐藏标志变量来指示局部静态数据是否已经初始化,并且在函数的每个条目上都检查这个标志。显然,这对性能的影响很小,但更令人担忧的是,这个标志不能保证是线程安全的。
如果您有一个如上所述的本地静态,并且从多个线程调用foo,则可能存在竞态条件,导致plonk初始化不正确甚至多次。同样,在这种情况下,plonk可能会被不同于构造它的线程的线程破坏。
不管标准怎么说,我对局部静态破坏的实际顺序非常谨慎,因为你可能会在不知不觉中依赖于一个静态破坏后仍然有效,这真的很难追踪。
一旦程序执行开始,静态变量就开始发挥作用,直到程序执行结束它都是可用的。
静态变量在内存的数据段中创建。
函数静态变量的生命周期在程序流[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中销毁 在if中销毁 C:>sample.exe 1 在foo中创建 在if中创建 在if中销毁 在foo中销毁
由于c++ 98[2]没有引用多线程,这将如何在多线程环境中表现是未指定的,并且可能是有问题的Roddy提到。
[1] cc+ +98第3.6.3.1节[基本启动]
在c++ 11中,静态数据是以线程安全的方式初始化的,这也被称为魔术静态数据。