【问题标题】:Dynamic initialization phase of static variables静态变量的动态初始化阶段
【发布时间】:2011-06-16 12:45:34
【问题描述】:

该标准明确指出,静态持续时间变量(命名空间范围和类静态成员)的动态初始化不必在 main 执行之前发生: “命名空间范围对象的动态初始化(8.5、9.4、12.1、12.6.1)是否在main的第一条语句之前完成是实现定义的。”是 3.6.2(3) 这不是[总是?] 实现动态初始化的方式吗?有什么更好/更简单的方法来保证对象在使用前被初始化?

【问题讨论】:

  • 你在哪里读到的?我不确定这是真的。你可能会想到“静态初始化顺序惨败”(parashift.com/c++-faq-lite/ctors.html#faq-10.14),但这与main()无关。
  • 查看我的编辑:IS 3.6.2(3)
  • 我不知道 C++ 标准是怎么说的,但至少在 C 中,静态初始化的“方式和时间”是未指定的行为。唯一的要求是在程序启动之前设置它们。在托管环境中,这意味着必须在调用 main() 之前初始化它们。
  • C 没有静态持续时间变量和常量的动态初始化阶段。初始化程序必须是编译时常量。

标签: c++


【解决方案1】:

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

是否由实现定义 或不动态初始化 (8.5, 9.4, 12.1, 12.6.1) 的对象 命名空间范围在 main 的第一个语句。 如果 初始化被推迟到一些 第一个之后的时间点 主要声明,它会发生 在第一次使用任何功能之前 或在同一个中定义的对象 翻译单元作为对象 已初始化。

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

编辑:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    我相信这里的目的是允许动态加载库。

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

    【讨论】:

    • 现在是有道理的。谢谢。
    • @John 实际上这并没有多大意义。动态链接库在执行开始之前被加载和初始化(所以在main之前)。除非库被显式加载——比如LoadLibrary(winapi)或dlopen(POSIX)——但是这超出了C++标准的范围(从显式加载的库不受ODR等事实可以看出) - 因为它们并没有真正成为进程中全局可用名称空间的一部分 - 它们的符号也必须显式查询)。查看我的答案最新修订。
    【解决方案3】:

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

    在 Ada 包中(大致相当于命名空间)可以包含初始化数据,这些数据实际上与程序具有相同的生命周期,很像 Cish 静态(为简单起见,我们将忽略通用包,类似于模板可以在较低范围内创建)。进行这些初始化的过程称为“精化”。

    Ada 保证所有包都将在主程序开始时详细说明。但是,如果另一个包尚未详细说明,则很有可能创建将无法工作的“详细说明”代码。通常,编译器可以选择详细说明顺序。因此,除非编译器碰巧选择了您需要的顺序,否则编写无法工作的代码被视为“有界错误”。

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

    它并不完美。编写确实没有有效的详细说明顺序的代码是很可能的。如果你这样做(或者没有得到正确的编译指示并且编译器也无法弄清楚),你会得到一个Program_Error 在细化过程中引发的异常。一些调试器在单步执行细化代码时遇到问题,因此这些错误可能是要追踪的主要 PITA。尽管如此,我真的很想念 C++ 中的这个功能。如果您小心的话,能够在“静态”声明中使用复杂对象与必须完全避免它们之间存在区别。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 好了,订单的某些部分已经定义好了。特别是,一个特定翻译单元中的静态变量按声明顺序初始化。
      • 请注意我对问题的编辑。 3.6.2(3) 说它不必在 main 执行之前发生。
      • “静态持续时间变量(命名空间范围和类静态成员)的动态初始化总是发生在 main 执行之前。”在大多数实现中。但标准保证。
      • @Oli: "一个特定翻译单元中的静态变量按声明顺序初始化" 按照定义的顺序(可能有多个声明)。
      猜你喜欢
      • 1970-01-01
      • 2011-08-22
      • 2010-12-22
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多