C++ 的循环引用问题

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

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

cpp 复制代码
#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;
}

输出:

cpp 复制代码
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 引用资源但不增加引用计数,因此不会阻止资源释放。

cpp 复制代码
#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;
}

输出

css 复制代码
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),不会阻止资源析构。

相关推荐
inferno3 分钟前
CSS 基础(第二部分)
前端·css
BD_Marathon6 分钟前
Router_路由传参
前端·javascript·vue.js
闲云一鹤13 分钟前
Cesium 去掉默认瓦片和地形,解决网络不好时地图加载缓慢的问题
前端·cesium
Dreamcatcher_AC14 分钟前
前端面试高频13问
前端·javascript·vue.js
AI陪跑15 分钟前
深入剖析:GrapesJS 中 addStyle() 导致拖放失效的问题
前端·javascript·react.js
登山人在路上16 分钟前
Vue中导出和导入
前端·javascript·vue.js
消失的旧时光-194318 分钟前
Flutter 路由从 Navigator 到 go_router:嵌套路由 / 登录守卫 / 深链一次讲透
前端·javascript·网络
掘金酱22 分钟前
🏆2025 AI/Vibe Coding 对我的影响 | 年终技术征文
前端·人工智能·后端
智算菩萨25 分钟前
【Python基础】字典(Dictionary):AI的“键值对”信息存储的基石
前端·人工智能·python
C_心欲无痕31 分钟前
vue3 - shallowReadonly浅层只读响应式对象
前端·javascript·vue.js