C++ 的循环引用问题

循环引用是指多个对象通过std::shared_ptr相互引用,形成一个闭环,导致它们的引用计数无法降为0.即使这些对象不再被程序的其他部分引用,它们的内存也不会被释放,因为每个对象的std::shared_ptr都被其他对象持有。循环引用会导致内存泄漏,因为资源无法被析构。std::shared_ptr的引用计数机制依赖于计数归零来释放资源,循环引用破坏了这一机制。

以下示例展示了两个类AB相互持有对方的std::shared_ptr:

#include <iostream>
#include <memory>

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr; // A 持有 B 的 shared_ptr
    ~A() { std::cout << "A destructed\n"; }
};

class B {
public:
    std::shared_ptr<A> a_ptr; // B 持有 A 的 shared_ptr
    ~B() { std::cout << "B destructed\n"; }
};

int main() {
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();
        a->b_ptr = b; // A 持有 B
        b->a_ptr = a; // B 持有 A
        std::cout << "Reference count of a: " << a.use_count() << "\n"; // 2
        std::cout << "Reference count of b: " << b.use_count() << "\n"; // 2
    } // 离开作用域,a 和 b 的引用计数仍为 1,不会析构
    std::cout << "Scope exited\n";
    return 0;
}

输出:

Reference count of a: 2
Reference count of b: 2
Scope exited

说明

  • amain 中的 std::shared_ptr<A>,指向 Object A(引用计数 = 1)。
  • bmain 中的 std::shared_ptr<B>,指向 Object B(引用计数 = 1)。
  • Object A 的成员 b_ptrstd::shared_ptr<B>)指向 Object B,使 Object B 的引用计数增至 2。
  • Object B 的成员 a_ptrstd::shared_ptr<A>)指向 Object A,使 Object A 的引用计数增至 2。
  • 形成循环:A -> B -> A

离开作用域时

  • a 销毁,Object A 的引用计数从 2 减到 1(因为 Ba_ptr 仍在引用)。
  • b 销毁,Object B 的引用计数从 2 减到 1(因为 Ab_ptr 仍在引用)。
  • 结果:Object AObject B 的引用计数都不为 0,无法释放,内存泄漏。

图示

通过将一方的 std::shared_ptr 替换为 std::weak_ptr,可以打破循环引用。std::weak_ptr 引用资源但不增加引用计数,因此不会阻止资源释放。

#include <iostream>
#include <memory>

class B;
class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destructed\n"; }
};

class B {
public:
    std::weak_ptr<A> a_ptr; // 使用 weak_ptr
    ~B() { std::cout << "B destructed\n"; }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b; // A 持有 B
    b->a_ptr = a; // B 弱引用 A
    return 0;
}

输出

B destructed
A destructed

说明

  • a 指向 Object A(引用计数 = 1)。
  • b 指向 Object B(引用计数 = 1)。
  • Object Ab_ptrstd::shared_ptr<B>)指向 Object BObject B 的引用计数增至 2。
  • Object Ba_ptrstd::weak_ptr<A>)指向 Object A,但不增加 Object A 的引用计数(仍为 1)。
  • 没有形成强引用循环,只有单向强引用:A -> B

离开作用域时

  • a 销毁,Object A 的引用计数从 1 减到 0,Object A 被释放,触发 A 的析构函数。
  • Object A 销毁时,其 b_ptr 销毁,Object B 的引用计数从 2 减到 1。
  • b 销毁,Object B 的引用计数从 1 减到 0,Object B 被释放,触发 B 的析构函数。
  • 结果:资源正确释放,无内存泄漏。

图示

std::weak_ptr 引用资源但不控制其生命周期(不增加引用计数)。当资源被所有 std::shared_ptr 释放后,std::weak_ptr 自动变为“过期”(expired),不会阻止资源析构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值