gpt4 book ai didi

c++ - 具有shared_ptr的成员的C++析构函数顺序

转载 作者:行者123 更新时间:2023-12-01 15:10:58 33 4
gpt4 key购买 nike

如果A类包含B类,则当A析构时,B的析构函数将首先被调用,即其嵌套关系的颠倒顺序。

但是,如果A包含shared_ptrB,而B包含指向A的原始指针,我们应该如何处理析构函数以使其安全呢?

考虑以下示例:

#include <iostream>
#include <memory>
#include <unistd.h>

struct B;
struct A {
int i = 1;
std::shared_ptr<B> b;

A() : b(std::make_shared<B>(this)) {}

~A() {
b = nullptr;
std::cout << "A destruct done" << std::endl;
}
};

struct B {
A *a;

B(A *aa) : a(aa) {}

~B() {
usleep(2000000);
std::cout << "value in A: " << a->i << std::endl;
std::cout << "B destruct done" << std::endl;
}
};

int main() {
std::cout << "Hello, World!" << std::endl;
{
A a;
}
std::cout << "done\n";
return 0;
}


您可以在 A的析构函数中看到,我将b显式设置为 nullptr,它将立即触发 B的析构函数,并阻塞直到完成。
输出将是:
Hello, World!
value in A: 1
B destruct done
A destruct done
done

但是如果我注释掉那条线
  ~A() {
// b = nullptr; // <---
std::cout << "A destruct done" << std::endl;
}

输出将是:
Hello, World!
A destruct done
value in A: 1
B destruct done
done


似乎 A的析构函数无需等待 B进行析构就可以完成。但是在这种情况下,我期望段错误,因为当 A已经被破坏时, B试图访问 A的成员,这是无效的。但是,为什么程序不产生段错误?碰巧还可以吗(即 undefined behavior)?

而且,当我改变
 {
A a;
}


  A * a = new A();
delete a;

输出仍然相同,无段故障。

最佳答案

准确地了解正在发生的事情很重要。当A被销毁时,以下事件将按以下顺序发生:

  • A::~A()被调用。
  • A对象的生命周期结束。该对象仍然存在,但不再存在。 ([basic.life] /1.3)
  • 执行A::~A()的主体。
  • A::~A()以相反的声明顺序隐式调用A的直接非静态成员的析构函数([class.dtor] / 9,[class.base.init] /13.3)
  • A::~A()返回。
  • A对象不再存在([class.dtor] / 16)。它曾经占用的内存将变为“已分配存储”([basic.life] / 6),直到被释放。

  • (所有引用均引用C++ 17标准)。

    在第二版的析构函数中:
    ~A() {
    std::cout << "A destruct done" << std::endl;
    }

    语句打印后,成员 b被销毁,这将导致拥有的 B对象被销毁。此时, i尚未被销毁,因此可以安全地访问它。之后, B析构函数返回。然后, i被“销毁”(有关某些细节,请参见 CWG 2256)。最后,返回 A的析构函数。那时,尝试访问成员 i不再合法。

    关于c++ - 具有shared_ptr的成员的C++析构函数顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60196037/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com