【问题标题】:C++ Static Initialization via Schwartz Counter通过 Schwartz 计数器进行 C++ 静态初始化
【发布时间】:2012-02-12 19:08:14
【问题描述】:

Schwartz counter 旨在确保在使用全局对象之前对其进行初始化。

请考虑使用如下所示的施瓦茨计数器。

文件 Foo.h:

class Foo
{
   Foo::Foo();
};

文件 Foo.cpp:

#include "Foo.h"

// Assume including Mystream.h provides access to myStream and that
// it causes creation of a file-static object that initializes
// myStream (aka a Schwartz counter).
#include "MyStream.h"

Foo::Foo()
{
   myStream << "Hello world\n";
}

如果 Foo::Foo() 在 main() 启动后运行,由于 cmets 中提到的文件静态初始化器对象,myStream 的使用可以保证是安全的(即 myStream 将在使用前被初始化)。

但是,假设 Foo 实例是在 main() 启动之前创建的,如果它是全局的就会发生这种情况。此处显示:

文件 Global.cpp:

#include "Foo.h"

Foo foo;

请注意,Global.cpp 不像 Foo.cpp 那样获得文件静态初始化器对象。在这种情况下,Schwartz 计数器如何确保 MyStream 初始化程序(以及 MyStream 对象本身)在 foo 之前初始化?或者在这种情况下施瓦茨计数器会失败吗?

【问题讨论】:

    标签: c++


    【解决方案1】:

    "Schwartz counters" 的使用(以 Jerry Schwartz 的名字命名,他设计了 IOStreams 库的基础,因为它现在在标准中;请注意,他不能因为许多奇怪的选择而受到指责,因为这些选择被标记在原始设计)可能会导致在构造对象之前对其进行访问。最明显的情况是在构造全局对象期间调用一个函数,该对象使用通过 Schwartz 计数器构造的自己的全局调用另一个翻译单元(我使用 std::cout 作为由 Schwartz 计数器保护的全局以保持示例短):

    // file a.h
    void a();
    
    // file a.cpp
    #include <iostream>
    void a() { std::cout << "a()\n"; }
    
    // file b.cpp
    #include <a.h>
    struct b { b() { a(); } } bobject;
    

    如果文件b.cpp 中的全局对象先于文件a.cpp 中的全局对象构造,并且如果std::cout 是通过施瓦茨计数器构造的,a.cpp 是第一个实例,则此代码将失败。施瓦茨计数器不能特别好用的原因至少还有两个:

    1. 为此使用全局对象时,此对象最终会被构造两次。虽然这在实践中做得很好,但我认为这很丑陋。解决此问题的方法是使用适当大小的char 缓冲区来实际定义对象(这些通常会被更改为正确类型的对象的名称)。然而,在这两种情况下,事情都是一团糟。
    2. 当在许多翻译单元中使用由 Schwartz 计数器保护的全局对象时(如 std::cout 的情况),这可能会导致显着的启动延迟:编写良好的代码通常不使用任何全局初始化但是 Schwartz 计数器需要为每个需要加载的目标文件运行一段代码。

    就我个人而言,我得出的结论是,这种技术是一个漂亮的想法,但在实践中却行不通。我使用了三种方法:

    1. 不要使用全局对象。这使得整个讨论变得过时并且效果最好,尤其是在并发代码中。在绝对需要全局资源的情况下,通过引用返回并使用 std::call_once() 初始化的函数静态对象是更好的选择。
    2. 在链接可执行文件时(例如最后一个)将全局对象放置在适当的位置会导致它首先被初始化。我过去曾对此进行过试验,然后我发现我可以在我关心的所有系统上适当地放置目标文件。这里的主要缺点是没有保证,并且在编译器版本之间切换时情况可能会发生变化。但是,对于 C++ 标准库,这是可以接受的(我这样做时只关心全局流对象)。
    3. 将全局对象放入专用共享库中:当共享库被加载时,其初始化代码被执行。共享库中的对象只有在初始化完成后才可用。我发现这很可靠,但需要一个额外的库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多