C++ 中全局对象销毁和 atexit 之间的顺序

Posted

技术标签:

【中文标题】C++ 中全局对象销毁和 atexit 之间的顺序【英文标题】:Order between destruction of global object and atexit in C++ 【发布时间】:2013-04-07 06:23:51 【问题描述】:

我想知道在 C++ 中销毁全局对象和 atexit 之间是否有确定的顺序

我有一个全局对象并注册atexit 函数,如下所示:

static MyClass g_class;

void onExit()

    // do some destruction


int main()

    atexit(onExit);

    return 0;

我发现 onExit() 在 Visual Studio 2012 和 gcc4.7.2 中在 MyClass::~MyClass() 之前被调用。我确定onExit 总是在全局对象(如g_class)销毁之前被调用吗?

我想知道全局对象注册顺序和atexit 注册顺序使用相同的顺序表。 还是全局对象顺序和atexit顺序没有关系?

已编辑:对不起,我写错了。我在整理示例代码时很困惑。 onExit() 在 ~MyClass() 之前被调用。

【问题讨论】:

我怀疑订单不是 100% 确定的。你为什么在乎? MyClass之前创建另一个全局对象,并在其构造函数中调用atexit( onExit )进行检查。 想要控制对象的生命周期,使用动态的。 实际上,我有一个单例内存管理器对象被 atexit 破坏,全局对象正在使用由该内存管理器管理的成员变量。所以全局对象在 atexit 销毁 memorymanager 之前就被销毁了。 @zelon: [off-topic] 作为不相关的说明,在文件范围(全局范围)变量上使用 static 关键字可能不会按照您的想法确实如此。也就是说,您的示例代码中的static 是多余的。 【参考方案1】:

更新: OP 造成了一些混乱,看起来 VC11 的行为确实符合 C++11 标准的规定。以下答案是在假设没有的情况下编写的。

因此,这个问题的答案:

我确定onExit 总是在全局对象(如g_class)销毁之前被调用吗?

“是”,只要您使用的是完全兼容的编译器。


我发现 MyClass::~MyClass() 在 Visual Studio 2012 中在 onExit() 之前被调用。

如果是这种情况,那么它是 VC11 中的一个错误。根据 C++11 标准的第 3.6.3/1 段:

具有静态存储的已初始化对象(即生命周期 (3.8) 已开始的对象)的析构函数 (12.4) 从main 返回和调用std::exit (18.5) 的结果调用了持续时间。 [...]

此外,根据第 3.6.3/3 段:

如果具有静态存储持续时间的对象的初始化完成是在调用之前排序的 到std::atexit(参见<cstdlib>,18.5),对传递给std::atexit的函数的调用在调用对象的析构函数之前排序。

因此,在您的情况下,应该在 MyClass 的析构函数之前调用 onexit()

据我所知,Clang 3.2 和 GCC 4.8.0 在这方面是合规的,如live example 所示。

【讨论】:

g++ 是否兼容取决于编译器选项和平台。 (它是合规的,但由于在许多平台上,atexit 函数是由平台提供的,它并不总是合理可行的。) 对不起。我写错了。 onexit() 在 ~MyClass 之前调用。我在整理示例代码时很困惑。 @zelon:好的。那么行为是正确的,这就是 C++ 标准所规定的。

以上是关于C++ 中全局对象销毁和 atexit 之间的顺序的主要内容,如果未能解决你的问题,请参考以下文章

C++ 并发销毁

C++内存泄露及常见情况总结

c++ 析构函数 是在啥时候执行

动态内存

销毁全局对象[关闭]

C++学习(四零五)编译选项use-cxa-atexit no-use-cxa-atexit