Answers:
函数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中
销毁C:> SAMPLE.EXE 1 2
创建在FOO
创建于如果
毁于如果
销毁在FOO
[0]
由于C ++ 98 [2]没有引用多线程,因此在多线程环境中如何表现尚无定论,如Roddy所述,这可能是有问题的。
[1]
C ++ 98部分3.6.3.1
[basic.start.term]
[2]
在C ++ 11中,静态方法是以线程安全的方式初始化的,这也称为Magic Statics。
[basic.start.term]
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
...这是不依赖销毁顺序的另一个原因!