静态变量的动态初始化阶段

Dynamic initialization phase of static variables

本文关键字:初始化 动态 变量 静态      更新时间:2023-10-16

标准特别指出,在执行main之前,不必对静态持续时间变量(命名空间范围和类静态成员)进行动态初始化:"实现定义了命名空间范围内对象的动态初始化(8.5、9.4、12.1、12.6.1)是否在main的第一个语句之前完成。"is 3.6.2(3)动态初始化不是这样实现的吗?有什么更好/更简单的方法可以保证对象在使用前初始化?

您的问题的答案在您引用的问题之后的下一句话(ISO/IEC 14882-2003第3.6.2条)中。

实现定义了是否为动态初始化(8.5、9.4、12.1、12.6.1)在主语句的第一个语句如果初始化被推迟到第一个之后的时间点主声明,它将发生在首次使用任何函数之前或在作为待翻译对象的翻译单位已初始化

显然,为了确保某个变量X已初始化,您只需在函数main中(直接或间接)使用与变量X在同一翻译单元中定义的任何函数或变量(例如,如果您在函数main中直接或间接使用X,则您可以确定它已初始化)。

编辑:

如果除了保证您使用的变量已经初始化(正如上面引用的标准文本所保证的那样)之外,您还想知道为什么标准中包含main执行开始后初始化可能延迟的规定。

我的意思是,如果问题也是:为什么不要求在main开始之前执行所有初始化

好吧,这绝对不是关于动态链接的库——在启动main之前初始化它们的所有对象没有问题。此外,这与动态加载的库(LoadLibrary/dlopen)无关——它们显然不在C++标准的范围内(例如,它们不受一个定义规则的约束,通常它们甚至可能不是C++)。

理论上,这一规定允许延迟初始化,以避免不必要的运行时开销-例如,除非您实际使用特定翻译单元(C++源文件)中的某些函数或对象,否则您不必进行其运行时初始化。但是,任何实现都不太可能在运行时真正进行延迟初始化——多线程同步对于这种初始化来说是具有挑战性的,而且本身就是运行时开销。

但是每个实现实际所做的只是链接那些实际使用的模块(翻译单元)。因此,即使您链接到一些静态库,其中包含一些动态初始化的对象(可能有副作用,如文件创建或用户交互),但不使用来自同一翻译单元的任何内容,实现也不必运行该对象的初始化。因此,该条款允许避免在最终可执行文件中包含任何未使用的翻译单元,即使它们是程序的正式组成部分。

我认为这里的意图是允许动态加载库。

库中定义的静态变量不能保证在main之前初始化,但必须在使用特定库中的任何内容之前初始化。

这不是一个直接的答案,但为了进行比较,研究类似语言(Ada)如何处理这种相同的情况可能会很有趣。

在Ada包(大致相当于名称空间)中,可以有初始化的数据,这些数据实际上与程序具有相同的生存期,很像Cish statics(为了简单起见,我们将忽略通用包,它们与模板一样可以在较低的范围内创建)。进行这些初始化的过程被称为"精化"。

阿达保证,所有程序包将在主程序开始时详细说明。然而,如果另一个包还没有详细制定,那么创建"详细制定"代码就很有可能不起作用。一般来说,编译器可以选择精化顺序。因此,除非编译器碰巧选择了您需要的顺序,否则编写的代码将不起作用,这被视为"有界错误"。

显然,这并不理想,因为在其他包中使用非内置数据类型通常是可取的,有时这会包括一些需要详细说明的初始化。因此,Ada提供了一些pragma,允许您要求编译器在当前包之前详细说明部分或全部依赖项。

这并不完美。很有可能编写的代码实际上没有有效的细化顺序。如果您这样做(或者没有正确处理pragma,编译器也无法解决它),则会在精化过程中引发Program_Error异常。一些调试器在处理精化代码时遇到困难,因此这些错误可能是需要跟踪的主要PITA。尽管如此,我还是非常怀念C++中的这个功能。这就是如果小心的话,可以在"静态"声明中使用复杂对象和必须完全避免它们之间的区别。

静态持续时间变量(命名空间范围和类静态成员)的动态初始化ALWAYS发生在执行main之前
未定义这些静态持续时间变量的初始化顺序。