C ++静态初始化vs __attribute __((构造函数))

时间:2011-12-08 15:37:05

标签: c++ gcc static-initialization

示例:

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

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

是否确定性首先打印foobar

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

3 个答案:

答案 0 :(得分:14)

首先打印

foo,因为对象按其声明的顺序初始化。跑步看看:

顺便说一句,__attribute__((constructor))不是标准C ++。这是GCC的延伸。因此,程序的行为取决于GCC如何定义它。简而言之,它是实现定义的,首先打印foo

doc说,

  

构造函数属性导致在执行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之前初始化。

答案 1 :(得分: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 构造函数中初始化静态对象实验证明了它在其他静态对象之后销毁。但它是非标准的,所以不可靠。

答案 2 :(得分:1)

这似乎是不确定的。在使用GCC编译时,我的问题中也有foo\nbar\n作为示例的输出。但是,当使用LLVM / Clang编译时,我得到bar\nfoo\n

但是,由于我不确定这可能是Clang中的错误,我填写了错误报告here编辑:我在那里得到了一个答案,它似乎真的是一个尚未修复的Clang中的错误。不知道从中得出什么结论。预期和应该行为在这里确实是确定性的,但是,你不能依赖它,因为至少有一个主要编译器(Clang)做错了(或者与GCC不同,如果我们把它作为规范__attribute__((constructor)))。

请注意,这在现实代码中非常重要且非常重要。例如。 here是一个使用Clang对随机生成器进行播种的示例。