C++ 向量的Schwarz计数器

C++ 向量的Schwarz计数器,c++,c++11,C++,C++11,我正在看一个例子,在这个例子中,我需要保证一个全局静态std::vector在各种翻译单元中的某些静态对象之前被初始化(构造) 当我研究如何处理这个问题时,我遇到了两个建议的解决方案: 在全局函数中使用静态对象来代替全局静态对象 施瓦兹计数器 我对使用Schwarz计数器的担心是std::vector将被初始化两次。从link中,我得到了“一种确保全局对象在第一次使用之前只初始化一次的有用技术,那就是保持使用它的翻译单元数量的计数。” 全局只初始化一次是如何工作的?根据我的推理,它将被初始化两次

我正在看一个例子,在这个例子中,我需要保证一个全局静态std::vector在各种翻译单元中的某些静态对象之前被初始化(构造)

当我研究如何处理这个问题时,我遇到了两个建议的解决方案:

  • 在全局函数中使用静态对象来代替全局静态对象
  • 施瓦兹计数器
  • 我对使用Schwarz计数器的担心是std::vector将被初始化两次。从link中,我得到了“一种确保全局对象在第一次使用之前只初始化一次的有用技术,那就是保持使用它的翻译单元数量的计数。”

    全局只初始化一次是如何工作的?根据我的推理,它将被初始化两次。一次在静态初始化的正常过程中,一次在Schwarz计数器的第一个实例初始化时


    请注意,在Schwarz计数器构造函数中初始化代码是什么样子的?我只能考虑使用新的布局。

    我只能说我过去是如何实现它的:我设计 一个特殊的“无操作”构造函数,它不执行任何操作,并使用 施瓦茨计数器中的新位置。比如:

    class ForUseAsStatic
    {
    public:
        enum MakeCtorNoop { makeCtorNoop };
        ForUseAsStatic();   //  normal ctor, called by Schwartz counter.
        ForUseAsStatic( MakeCtorNoop );
                            //  no-op constructor, used when
                            //  defining the variable.
    };
    
    从形式上讲,这并不能保证编译器可以 在调用构造函数之前再次将内存设置为0, 但我从来没有听说过有这样的编译器

    也可以在类中放置某种标志 自身,由构造函数测试。这只适用于 当然是静态对象(因为它在 工作指令)

    另一种可能的技术(我在一些网站上看到过) 库)是为中的对象声明内存 汇编程序,或者如果编译器有某种方法 强制对齐。数据名通常不会损坏,因此 即使它是形式上未定义的行为,通常也会起作用。 (当然,对于标准库,库作者可以 请求编译器中的扩展,以帮助他们使用 问题。)

    最后:今天,singleton习语或类似的东西被广泛使用 通常倾向于这种工作环境。这确实意味着你 必须编写
    myobj().xxx
    ,而不仅仅是
    myobj.xxx
    ,但是
    一般认为这不是一个问题。

    您可以使用另一个间接级别:将全局变量作为指向向量的指针(将初始化为零),让您的计数器
    新建
    向量,并将结果存储在指针中。

    我认为静态初始化问题没有正确答案。行为未定义,解决方案的选择取决于具体情况。这取决于:

    • 编译器及其实现
      • 大多数编译器在单个编译单元内提供保证
      • 顺序可以由链接器决定,有时还受#pragma的影响
      • 初始化可以在main开始执行之前的任何时间点进行
      • 是否为具有静态变量的全局函数调用析构函数以及何时调用
    • 应用程序架构
      • 无论是否使用.DLL,某些环境都减少了对DLL中静态构造/销毁的管理支持,尤其是在加载需求时
      • 无论应用程序是否是线程化的,这都会影响调用带有静态变量的全局函数的方式
    最好的建议可能是通过另一种方式设计系统来避免这一惨败,尽管这可能不太实际。听起来您的应用程序不需要考虑一些关于可移植性的问题,而是针对具有特定编译器的特定环境

    #pragma/编译器选项

    您可能没有考虑的一个选项是,是否有一些编译器支持您所需要的内容

    有关windows,请参阅:

    对于g++: 启用
    init priority
    并使用
    \uuuuu属性((init\u priority(n))

    让Swartz计数器工作

    一些示例忽略了一个对象,即空间只是为被分配的对象保留的,该对象被适当地对齐。这避免了您提到的构造之一。例如,在g++gnu中使用:

    typedef char fake_istream[sizeof(istream)] __attribute__ ((aligned(__alignof__(istream))))
    ...
    fake_istream cin;
    

    为对象分配空间。该编译单元之外的所有代码都将该区域称为
    extern istream cin
    (通过标题),并用一个新的字段初始化。应该注意确保漂亮的计数器是线程安全的(原子的)。

    相关:关于字节数组策略:在C++03中,您可以声明的大小总是超过在构造时对齐所需的大小,在C++11中,您有
    std::aligned\u storage::type
    。这对于自定义类型来说很好,但是对于STL类型,比如std::vector,它如何工作呢?@Graznarak,它根本不工作。您可以尝试第二种可能性:声明原始内存,并依靠编译器不会将类型信息转换为变量名这一事实,或者您可以将STL类型封装在提供相同接口的类中。(这个类将包含一个指向STL类型的指针,并对其使用动态分配,以便能够准确地控制它的构造和破坏时间。)我想说这通常是个好主意,但我倾向于在嵌入式系统中工作,除非绝对必要,否则不赞成内存碎片和动态分配。“在编程中,没有一个问题不能通过添加另一个间接层来解决……接受删除一个间接层”