首先调用什么:静态对象的析构函数或atexit处理程序?

问题描述 投票:0回答:2

这种情况下是保证订单还是UB?

#include <iostream>
#include <cassert>

using namespace std;

struct TraceHelper
{
    TraceHelper()
    {
        cout << "TraceHelper::constructor()" << endl;
    }

    ~TraceHelper()
    {
        cout << "TraceHelper::destructor()" << endl;
    }
};

void trace_fn()
{
    static TraceHelper th;
    cout << "trace_fn()" << endl;
}

void my_atexit()
{
    cout << "my_atexit()" << endl;
}

int main()
{
    cout << "Entered main()" << endl;
    assert(0 == atexit(my_atexit));
    trace_fn();
    trace_fn();
    cout << "Exiting main()" << endl;

    return 0;
}

输出:

Entered main()
TraceHelper::constructor()
trace_fn()
trace_fn()
Exiting main()
TraceHelper::destructor()
my_atexit()
c++ destructor atexit
2个回答
1
投票

我们这里看到的是main函数和std::exit:

的交互

从main函数返回,[...]执行正常函数 终止(自动调用变量的析构函数 存储持续时间),然后执行 std::exit,传递参数 返回语句的(如果使用隐式返回则为 0 )作为 退出代码。

就静态对象的破坏而言,与问题相关的是以下几行:

如果完成静态对象A的初始化是 在为某些函数 F 调用 std::atexit 之前排序,调用 终止期间到 F 在开始之前排序 破坏 A.

如果对某个函数 F 的 std::atexit 调用是 sequenced-before 完成静态对象的初始化 A,A 的销毁开始顺序在对 F 的调用之前 在终止期间。

在问题的代码中,函数

my_atexit()
是在函数
std::atexit()
中创建静态对象
th
之前使用
trace_fn()
注册的。因此,
TraceHelper
的析构函数调用发生在调用
my_atexit()
之前。行为符合预期。


1
投票

在您的 specific 案例中(即 local 静态对象的案例),cppreference 有这样的说法(在“自 C++11 起”部分):

  1. 具有线程本地存储持续时间的对象的析构函数 与当前线程相关联,对象的析构函数 具有静态存储持续时间,以及注册的功能
    std::atexit
    并发执行,同时保持 以下保证:

    c) 如果完成 静态对象 A 的初始化在调用之前排序
    std::atexit
    对于某些函数 F,终止期间对 F 的调用是 在 A
    的销毁开始之前排序 d) 如果调用 到
    std::atexit
    对于某些函数 F 在 静态对象A初始化完成,开始 A 的销毁在终止期间对 F 的调用之前进行排序。

因此,由于

th
已初始化 – 根据定义 – 第一次执行通过它的声明,那么您对
atexit
的调用将在第一次调用
trace_fn()
和随后的
tf 构造之前完全排序
.因此,上述引文中的“d”段成立,您看到的销毁/终止序列是明确定义的。

这是C++17标准草案的“等效”部分:

6.8.3.4 终止 [basic.start.term]


5 如果 使用静态存储完成对象的初始化 duration 强烈发生在调用

std::atexit
之前,调用 传递给
std::atexit
的函数在调用之前排序 对象的析构函数。如果强烈呼唤
std::atexit
发生在对象初始化完成之前 静态存储持续时间,调用对象的析构函数是 在调用传递给
std::atexit
的函数之前排序。 …

(这里是最新的在线 C++ 标准草案的等效部分。)

还要注意,这种块作用域

static
对象的析构函数和对已注册
atexit
处理程序的调用被称为“并发”(请参阅有关可能滥用该术语的评论),并提供保证,因此您可以拥有对多个析构函数的调用与对多个退出处理程序的调用“混合”在一起。

© www.soinside.com 2019 - 2024. All rights reserved.