如果变量在函数的作用域中声明为 static ,则它仅初始化一次并在函数调用之间保留其值 . 它的生命到底是什么?什么时候调用它的构造函数和析构函数?
static
void foo() { static string plonk = "When will I die?"; }
如果没有6.7中标准的实际规则,现有的解释并不完全完整:
在执行任何其他初始化之前,将执行具有静态存储持续时间或线程存储持续时间的所有块范围变量的零初始化 . 具有静态存储持续时间的块范围实体的常量初始化(如果适用)在首次输入其块之前执行 . 允许实现在允许实现静态初始化具有命名空间范围内的静态或线程存储持续时间的变量的相同条件下,使用静态或线程存储持续时间执行其他块范围变量的早期初始化 . 否则,在第一次控制通过其声明时初始化这样的变量;这样的变量在初始化完成时被认为是初始化的 . 如果通过抛出异常退出初始化,则初始化未完成,因此下次控制进入声明时将再次尝试初始化 . 如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成 . 如果控件在初始化变量时以递归方式重新输入声明,则行为未定义 .
Motti对订单是对的,但还有其他一些事情需要考虑:
编译器通常使用隐藏标志变量来指示本地静态是否已经初始化,并且在该函数的每个条目上都检查此标志 . 显然这是一个很小的性能损失,但更令人担忧的是这个标志不能保证是线程安全的 .
如果你有一个如上所述的本地静态,并且从多个线程调用'foo',你可能会遇到竞争条件导致'plonk'被错误地初始化甚至多次 . 此外,在这种情况下,'plonk'可能会被构造它的不同线程破坏 .
尽管标准说的是,但我对局部静态破坏的实际顺序非常警惕,因为你可能无意中依赖静态在被破坏后仍然有效,并且这很难被追踪 .
函数 static 变量的生命周期在程序流第一次[0]遇到声明时开始,并在程序终止时结束 . 这意味着运行时必须执行一些簿记,以便仅在实际构建时才销毁它 .
另外,由于标准说静态对象的析构函数必须按照完成构造的相反顺序运行[1],并且构造顺序可能取决于具体的程序运行,所以必须考虑构造的顺序 .
Example
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); }
Output:
C:>在foo中创建的sample.exe在foo中销毁C:> sample.exe 1如果在foo中创建,则创建在foo中销毁如果在C中被破坏:> sample.exe 1 2在foo中创建如果在Destroyed中被销毁则创建在foo
[0] 由于 C++98 [2]没有引用多个线程如何在多线程环境中表现如何,这是未指定的,并且可能有问题,如Roddy提及 .
[0]
[1] C++98 section 3.6.3.1 [basic.start.term]
[1]
3.6.3.1
[2] 在C 11中,静态以线程安全方式初始化,这也称为Magic Statics .
[2]
FWIW,Codegear C Builder不会按照标准按预期顺序进行破坏 .
C:\> sample.exe 1 2 Created in foo Created in if Destroyed in foo Destroyed in if
......这是不依赖破坏秩序的另一个原因!
4 回答
如果没有6.7中标准的实际规则,现有的解释并不完全完整:
Motti对订单是对的,但还有其他一些事情需要考虑:
编译器通常使用隐藏标志变量来指示本地静态是否已经初始化,并且在该函数的每个条目上都检查此标志 . 显然这是一个很小的性能损失,但更令人担忧的是这个标志不能保证是线程安全的 .
如果你有一个如上所述的本地静态,并且从多个线程调用'foo',你可能会遇到竞争条件导致'plonk'被错误地初始化甚至多次 . 此外,在这种情况下,'plonk'可能会被构造它的不同线程破坏 .
尽管标准说的是,但我对局部静态破坏的实际顺序非常警惕,因为你可能无意中依赖静态在被破坏后仍然有效,并且这很难被追踪 .
函数
static
变量的生命周期在程序流第一次[0]遇到声明时开始,并在程序终止时结束 . 这意味着运行时必须执行一些簿记,以便仅在实际构建时才销毁它 .另外,由于标准说静态对象的析构函数必须按照完成构造的相反顺序运行[1],并且构造顺序可能取决于具体的程序运行,所以必须考虑构造的顺序 .
Example
Output:
[0]
由于 C++98 [2]没有引用多个线程如何在多线程环境中表现如何,这是未指定的,并且可能有问题,如Roddy提及 .[1]
C++98 section3.6.3.1
[basic.start.term][2]
在C 11中,静态以线程安全方式初始化,这也称为Magic Statics .FWIW,Codegear C Builder不会按照标准按预期顺序进行破坏 .
......这是不依赖破坏秩序的另一个原因!