例子:
struct Foo { Foo() { printf("foo\n"); } };
static Foo foo;
__attribute__((constructor)) static void _bar() { printf("bar\n"); }
它是确定性的foo
还是bar
先打印出来的?
(我希望并且期望静态对象的构造函数总是首先执行但不确定,并且关于构造函数属性的 GCC 文档没有说明任何内容。)
例子:
struct Foo { Foo() { printf("foo\n"); } };
static Foo foo;
__attribute__((constructor)) static void _bar() { printf("bar\n"); }
它是确定性的foo
还是bar
先打印出来的?
(我希望并且期望静态对象的构造函数总是首先执行但不确定,并且关于构造函数属性的 GCC 文档没有说明任何内容。)
foo
将首先打印,因为对象按其声明的顺序进行初始化。运行看看:
顺便说一句,__attribute__((constructor))
它不是标准 C++。它是 GCC 的扩展。所以你的程序的行为取决于 GCC 是如何定义它的。简而言之,它是实现定义的,根据它foo
首先打印。
医生说,
构造函数属性使函数在执行进入main()之前被自动调用。类似地,析构函数属性使函数在 main() 完成或 exit() 被调用后自动调用。具有这些属性的函数对于初始化将在程序执行期间隐式使用的数据很有用。
您可以提供一个可选的整数优先级来控制构造函数和析构函数的运行顺序。具有较小优先级编号的构造函数在具有较大优先级编号的构造函数之前运行;相反的关系适用于析构函数。因此,如果您有一个分配资源的构造函数和一个释放相同资源的析构函数,则这两个函数通常具有相同的优先级。构造函数和析构函数的优先级与为命名空间范围的 C++ 对象指定的优先级相同(请参阅 C++ 属性)。
我认为粗体文本暗示,对象按照它们的声明顺序进行初始化,正如我之前所说,在线演示也几乎证实了这一点。
我猜你也想读这个:
如果要控制/更改初始化顺序,可以使用init_priority
属性,提供优先级。取自页面:
Some_Class A __attribute__ ((init_priority (2000)));
Some_Class B __attribute__ ((init_priority (543)));
在这里,B
是之前初始化的A
。
它没有由标准定义,但这是我的实验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 构造函数中初始化一个静态对象通过实验证明了它在其他静态对象之后被破坏。但它是非标准的,所以不可靠。