cppreference关于std::atexit
的说明:
这些函数可以与具有静态存储期的对象的销毁和彼此同时调用,保证如果A的注册在B之前,则对B的调用在对A的调用之前被排序,静态对象构造函数和对atexit的调用之间的排序也适用于此。
我理解这段话的意思是,如果在静态初始化期间调用std::atexit
,则在注册该函数时调用的std::atexit
所初始化的最后一个静态对象的销毁之前调用已注册函数。我也将“可能并发调用”解释为调用可以发生在静态对象销毁之间,而不是多线程解释的意思。
我想知道的是,在这个顺序的上下文中,对象何时被认为已初始化(即开始初始化还是完成初始化)。我编写了一个简短的测试来测试这一点:
#include <cstdlib>
#include <iostream>
struct foo
{
foo()
{
std::cout << "ctor\n";
std::atexit([]() { std::cout << "atexit\n"; });
}
~foo()
{
std::cout << "dtor\n";
}
};
foo my_foo;
int main()
{
return 0;
}
我得到的输出结果是 (http://cpp.sh/3bllu) :
ctor
dtor
atexit
这让我相信,在我的构造函数完成之前,
my_foo
在这个上下文中并未被视为已初始化。换句话说,该函数在 my_foo
初始化之前被视为已注册,因此注册的函数会在my_foo
销毁之后执行。我似乎找不到任何可以保证这种行为的东西,而且我甚至不确定我对引用段落的最初解释是否正确。我描述的行为是我可以依赖的内容,还是实现定义或未定义的行为?
std::cout
... - SergeyAstatic const
成员?在静态初始化的上下文中,它们本质上与全局变量相同。无论如何,消除所有全局变量并不总是像听起来那么容易,特别是考虑到遗留代码和依赖关系。有时候,为了避免全局变量而进行的花式操作并不值得投入人力或增加代码复杂性。 - François Andrieux