向量的Schwarz计数器

Schwarz Counter for a vector

本文关键字:计数器 Schwarz 向量      更新时间:2023-10-16

我正在研究一种情况,在这种情况下,我将有一个全局静态std::向量,我需要保证它在各种翻译单元中的某些静态对象之前初始化(构造)。

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

  1. 在全局函数中使用一个静态对象来代替全局静态对象
  2. Schwarz计数器

我对使用Schwarz计数器的担忧是std::vector将被初始化两次。从这个链接中,我得到了"确保全局对象在第一次使用之前只初始化一次的一种有用技术是保持使用它的翻译单元数量的计数。"

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

与此相关的是,Schwarz计数器构造函数中的初始化代码会是什么样子?我只能考虑使用一个新的位置。

我只能说我过去是如何实现它的:我设计一个特殊的"no-op"构造函数,它什么都不做,并使用Schwartz柜台的新位置。类似于:

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/编译器选项

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

有关窗口,请参见:http://support.microsoft.com/kb/104248

对于g++:启用init-priority并使用__attribute__ ((init_priority (n)))

让Swartz计数器工作

一些示例忽略的是,作为空间的对象只是为被分配的适当对齐的对象保留的。这避免了你提到的一种结构。例如,在g++gnu中使用:

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

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

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