若是在函数范围内将变量声明为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中毁了 uiC:> sample.exe 1
建立于
在foo中建立
在foo中毁了
毁于 spaC:> sample.exe 1 2
在foo中建立
建立于
毁于
在foo中毁了 线程
[0]
因为C ++ 98 [2]没有引用多线程,所以在多线程环境中如何表现尚无定论,而且可能会引发Roddy的说起。 code
[1]
C ++ 98第3.6.3.1
节[basic.start.term] 对象
[2]
在C ++ 11中,静态是以线程安全的方式初始化的,这也称为Magic Statics 。
Motti关于顺序是正确的,但还须要考虑其余一些事项:
编译器一般使用一个隐藏的标志变量来指示是否已初始化本地静态变量,并在该函数的每一个条目上均检查该标志。 显然,这对性能影响不大,但更使人担心的是,不能保证此标志是线程安全的。
若是您具备上述的局部静态foo
,而且从多个线程调用了foo
,则您可能会遇到竞争情况,从而致使错误地初始化plonk
甚至屡次。 一样,在这种状况下, plonk
可能会被与构造它的线程不一样的线程破坏。
尽管有标准说明,但我会对本地静态销毁的实际顺序很是警戒,由于您有可能会无心中依赖销毁后仍然有效的静态,这确实很难追踪。
FWIW,Codegear C ++ Builder不会按照标准按预期顺序破坏。
C:\> sample.exe 1 2 Created in foo Created in if Destroyed in foo Destroyed in if
...这是不依赖销毁顺序的另外一个缘由!
若是没有6.7中的标准的实际规则,则现有的解释还不是很完整:
在进行任何其余初始化以前,将使用静态存储持续时间或线程存储持续时间对全部块范围变量进行零初始化。 若是适用,将在首次进入其块以前执行具备静态存储持续时间的块范围实体的恒定初始化。 在容许实现以静态或线程存储持续时间在命名空间范围内静态初始化变量的相同条件下,容许实现以静态或线程存储持续时间对其余块范围变量进行早期初始化。 不然,该变量将在控件第一次经过其声明时进行初始化; 此类变量在初始化完成后即被初始化。 若是初始化因为抛出异常而退出,则说明初始化未完成,所以下次控件进入声明时会再次尝试初始化。 若是在初始化变量时控件同时输入了声明,则并发执行应等待初始化完成。 若是控件在初始化变量时递归地从新输入声明,则该行为未定义。
静态变量在程序执行开始后即发挥做用,而且在程序执行结束以前一直可用。
静态变量在内存的数据段中建立。