C ++通过Schwartz计数器进行静态初始化 [英] C++ Static Initialization via Schwartz Counter

查看:159
本文介绍了C ++通过Schwartz计数器进行静态初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



请考虑使用下面显示的Schwartz计数器。



文件Foo.h:

  class Foo 
{
Foo :: Foo
};

文件Foo.cpp:

  #includeFoo.h

//假设包括Mystream.h提供对myStream的访问,
//引起创建一个文件static对象,它初始化
// myStream(又名Schwartz计数器)。
#includeMyStream.h

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



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



然而,假设Foo实例在main()启动之前创建,如果它是全局的,就会发生。如下所示:



文件Global.cpp:

  includeFoo.h

Foo foo;

请注意,Global.cpp没有得到一个文件静态初始化对象像Foo.cpp 。在这种情况下,Schwartz计数器如何确保MyStream初始化程序(因此MyStream对象本身)在foo之前初始化?在这种情况下,Schwartz计数器是否会失败?

解决方案

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

  //文件ah 
void a();

//文件a.cpp
#include< iostream>
void a(){std :: cout<< 一个; }

//文件b.cpp
#include< a.h>
struct b {b(){a(); }} bobject;

如果文件中的全局对象 b.cpp 被创建在文件 a.cpp 中的那些之前,并且如果 std :: cout 是通过Schwartz计数器以 a.cpp 为第一个实例,此代码将失败。有至少两个其他原因为什么Schwartz计数器不能很好地工作:


  1. 当使用全局对象时,最后构建两次。虽然这在实践中工作正确,我认为这是丑陋。解决方法是使用适当大小的 char 缓冲区用于对象的实际定义(这些缓冲区通常作为正确类型的对象被标记为名称)。

  2. 当许多翻译单元使用由Schwartz计数器保护的全局对象时(如 std的情况) :: cout )这可能会导致显着的启动延迟:良好编写的代码通常不使用任何全局初始化,但Schwartz计数器需要运行一段代码为每个目标文件


  3. 个人我得出结论,这种技术是一个 nifty 的想法但它在实践中不工作。我使用了三种方法:


    1. 不要使用全局对象。这使得整个讨论过时,并且在并发代码中最有效。在绝对需要全局资源的地方,通过引用返回并使用 std :: call_once()初始化的函数静态对象是一个更好的替代方案。

    2. 在链接可执行文件(例如最后一个)时将全局对象放置在适当的位置会导致它首先被初始化。我曾经试验过这种在过去,然后我发现,我可以放置对象文件适当地在我所关心的所有系统。这里的主要缺点是没有保证,并且在编译器版本之间切换时可能会改变。对于C ++标准库,这是可以接受的(当我这样做时,我只关心全局流对象)。

    3. 将全局对象放入专用共享库:当共享库被加载,其初始化代码被执行。共享库中的对象只有在初始化完成后才可用。我发现这项工作可靠,但需要额外的图书馆。


    A Schwartz counter is intended to ensure that a global object is initialized before it is used.

    Please consider the use of a Schwartz counter shown below.

    File Foo.h:

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

    File 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";
    }
    

    If Foo::Foo() runs after main() starts, the use of myStream is guaranteed to be safe (i.e. myStream will have been initialized before use) because of the file-static initializer object mentioned in the comments.

    However, suppose the Foo instance gets created before main() starts, as would happen if it were global. This is shown here:

    File Global.cpp:

    #include "Foo.h"
    
    Foo foo;
    

    Note that Global.cpp does not get a file-static initializer object like Foo.cpp does. In this case, how does the Schwartz counter ensure that the MyStream initializer (and therefore the MyStream object itself) are initialized before foo? Or can the Schwartz counter fail in this case?

    解决方案

    The use of "Schwartz counters" (so called after Jerry Schwartz who designed the basics of the IOStreams library as it is now in the standard; note that he can't be blamed for many of odd choices because these were tagged onto the original design) can result in accesses to objects before they are constructed. The most obvious scenario is calling a function during construction of a global object which calls into another translation unit using the its own global constructed via a Schwartz counter (I'm using std::cout as the global being guarded by a Schwartz counter to keep the example short):

    // 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;
    

    If the global objects in file b.cpp are constructed prior to those in the file a.cpp and if std::cout is constructed via a Schwartz counter with a.cpp being the first instance, this code will fail. There is are at least two other reasons why Schwartz counters don't work particularly well:

    1. When using a global object for this, this objects ends up being constructed twice. Although this works in practice when done right, I think this is ugly. A work-around for this is to use a char buffer of appropriate size for the actual definition of the object (these typically get mangled to the name as an object of the correct type). However, in both of these cases things are messy.
    2. When the global objects guarded by a Schwartz counter are used in many translation units (as it the case for std::cout) this may cause a significant start-up delay: well-written code typically doesn't use any global initialization but the Schwartz counter needs to run a piece of code for each of the object files which needs to be loaded.

    Personally I have come to the conclusion that this technique is a nifty idea but it doesn't work in practice. There are three approaches I use instead:

    1. Don't use global objects. This make this whole discussion obsolete and works best especially in concurrent code. Where a global resource is absolutely needed, a function static object returned by reference and initialized using std::call_once() is a much better alternative.
    2. Placing the global object at an appropriate location when linking the executable (e.g. last) causes it to be initialized first. I had experimented with this in the past and back then I found that I can place object files appropriately on all systems I cared for. The main drawback here is that there are no guarantees and things might change when switching between compiler versions. For the C++ standard library this is, however, acceptable (and I only cared about the global stream objects when I did this).
    3. Put the global objects into a dedicated shared library: when the shared library is loaded, its initialization code is executed. The objects in the shared library become available only after the initialization is complete. I found that this works reliably but requires an extra library.

    这篇关于C ++通过Schwartz计数器进行静态初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆