【问题标题】:C++ static initialization vs __attribute__((constructor))C++ 静态初始化 vs __attribute__((constructor))
【发布时间】:2012-01-16 00:52:56
【问题描述】:

例子:

struct Foo { Foo() { printf("foo\n"); } };
static Foo foo;

__attribute__((constructor)) static void _bar() { printf("bar\n"); }

首先打印foo 还是bar 是否具有确定性?

(我希望并且期望静态对象的构造函数总是首先执行但不确定,关于构造函数属性的 GCC 文档没有说明任何内容。)

【问题讨论】:

  • 你在哪里使用这种编译器功能??
  • @AlexTheo:这很常见。参见例如codesearch.google.com/#search/…>。您通常在每次想要初始化某些东西时使用它。
  • 实际上我更喜欢 static const bool _isInitialized 之类的东西,并制作一个私有初始化函数,用它来初始化我的对象,例如 const bool MyClass::_isInitialized = initFunction();但这些仅适用于我喜欢首先初始化的对象。否则构造函数应该做的工作。
  • @AlexTheo 这种方法在 C 中不起作用。当然您可以在 C++ 中使用构造函数,但这与 C 中的静态初始化器具有相同的效果。
  • @nevelis 我们在谈论 C++!!!

标签: c++ gcc static-initialization


【解决方案1】:

foo 将首先打印,因为对象按照其声明的顺序进行初始化。运行看看:

顺便说一句,__attribute__((constructor)) 不是标准 C++。它是 GCC 的扩展。所以你的程序的行为取决于 GCC 是如何定义它的。简而言之,它是实现定义的,根据它foo首先打印。

doc 说,

constructor属性使函数在执行进入main()之前被自动调用。类似地,析构函数属性使函数在 main() 完成或 exit() 被调用后自动调用。具有这些属性的函数对于初始化将在程序执行期间隐式使用的数据很有用。

您可以提供一个可选的整数优先级来控制构造函数和析构函数的运行顺序。具有较小优先级编号的构造函数在具有较大优先级编号的构造函数之前运行;相反的关系适用于析构函数。因此,如果您有一个分配资源的构造函数和一个释放相同资源的析构函数,则这两个函数通常具有相同的优先级。 构造函数和析构函数的优先级与为命名空间范围的 C++ 对象指定的优先级相同(请参阅 C++ 属性)。

我认为粗体字表示,对象按照它们的声明顺序进行初始化,正如我之前所说,online demo 也证实了这一点。

我猜你也想读这个:

如果你想控制/改变初始化顺序,你可以使用init_priority属性,提供优先级。取自the page

Some_Class  A  __attribute__ ((init_priority (2000)));
Some_Class  B  __attribute__ ((init_priority (543)));

这里,BA 之前初始化。

【讨论】:

  • 嗯,所有的cmets都被删除了?这怎么可能。所以,这又是我的评论:对我来说有趣的是这个信息以及最后一个链接,特别是。 this: 在标准 C++ 中,在命名空间范围内定义的对象保证按照其在给定翻译单元中的定义顺序进行初始化。
  • 其实我只是在另一个例子上测试了一下,好像不是这样的。
  • @Albert:发布代码。实际代码,无需稍作改动。同时发布你得到的输出。
  • 这个答案是错误的。根据docs:“构造函数和析构函数的优先级与为命名空间范围 C++ 对象指定的优先级相同(请参阅 C++ 属性)。但是,目前,C++ 对象的构造函数的顺序为静态存储持续时间和调用用属性构造函数修饰的函数是未指定的。”因此,正如@Albert 在他的回答中提到的那样,输出是不确定的。
【解决方案2】:

标准没有定义,但这是我的实验https://github.com/SanSanch5/static-initialization-order-example

gcc-9 上的输出:

CFoo constructed
dll constructor
CBaz constructed
CBar constructed
foo
dll destructor
CBar destructed
CBaz destructed
CFoo destructed

clang-12 上的输出:

CFoo constructed
dll constructor
CBaz constructed
CBar constructed
foo
CBar destructed
dll destructor
CBaz destructed
CFoo destructed

因此,在静态初始化之前调用了 dll 构造函数,但正如您所见,gcc 和 clang 的销毁略有不同。但是在 dll 构造函数中初始化一个静态对象通过实验证明它在其他静态对象之后被破坏。但它是非标准的,所以不可靠。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多