智能指针循环引用的问题

在C++中,智能指针为我们提供了自动管理内存的机制,尤其是 std::shared_ptr。但在某些情况下,多个对象之间的互相引用会导致循环引用问题,从而使对象的析构函数无法调用,导致内存泄漏。

1. 代码

#include <iostream>
#include <memory>

using namespace std;

class A;

class B {
public:
    std::shared_ptr<A> ptr_a;

    ~B() {
        std::cout << "B has been destructured " << endl;
    }
};

class A {
public:
    // std::shared_ptr<B> ptr_b;
    std::weak_ptr<B> ptr_b; // 使用 weak_ptr 打破循环引用

    ~A() {
        std::cout << "A has been destructured" << endl;
    }
};

int main() {
    auto ptr_B = std::make_shared<B>();
    auto ptr_A = std::make_shared<A>();

    ptr_B->ptr_a = ptr_A;
    ptr_A->ptr_b = ptr_B;

    cout << "ptr_A: " << ptr_A.use_count() << endl;
    cout << "ptr_B: " << ptr_B.use_count() << endl;

    return 0;
}

2. 当两个都使用 std::shared_ptr 时

  1. 当执行 auto ptr_B = std::make_shared(); 时,ptr_B 指向 B,此时 B 的引用计数为 1。

  2. 当执行 auto ptr_A = std::make_shared(); 时,ptr_A 指向 A,此时 A 的引用计数为 1。

  3. 接下来,两者互相引用:

    • ptr_B->ptr_a = ptr_A; 使 B 对象的 ptr_a 持有 A 对象的 ptr_A,A 的引用计数增加到 2(ptr_A,B::ptr_a)。
    • ptr_A->ptr_b = ptr_B; 使 A 对象的 ptr_b 持有 B 对象的 ptr_B,B 的引用计数增加到 2(ptr_B,A::ptr_b)。
  4. 当 main 函数结束时,ptr_A 和 ptr_B 超出作用域,引用计数分别从 2 减少到 1,但由于它们互相持有对方的 shared_ptr,引用计数无法归零,因此不会调用析构函数,导致内存泄漏。

2.1 程序运行结果

在这里插入图片描述

3. 当其中一方使用 std::weak_ptr 时

std::weak_ptr 是一种弱引用,不会增加引用计数。通过将 A 持有的 ptr_b 改为 std::weak_ptr,可以打破循环引用,确保对象能正常析构。

  1. 当执行 auto ptr_B = std::make_shared<B>(); 时,ptr_B 指向 B,此时 B 的引用计数为 1。
  2. 当执行 auto ptr_A = std::make_shared<A>(); 时,ptr_A 指向 A,此时 A 的引用计数为 1。
  3. 接下来,两者互相引用:
    • ptr_B->ptr_a = ptr_A; 使 B 的 ptr_a 持有 A 对象的 ptr_A,A 的引用计数增加到 2。
    • ptr_A->ptr_b = ptr_B; 使用 weak_ptr,这不会增加 B 的引用计数,因此 B 的引用计数仍为 1。
  4. 当 main 函数结束时:ptr_A 和 ptr_B 超出作用域,引用计数减少:
    • A 的引用计数从 1 变为 0,A 被析构,输出 “A has been destructured”。
    • 在 A 的析构过程中,A::ptr_b 被销毁,B 的引用计数从 2 变为 1。
    • B 的引用计数从 1 变为 0,B 被析构,输出 “B has been destructured”。
      这样,A 和 B 都被正确析构,没有内存泄漏。

3.1 程序运行结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值