侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

智能指针与循环引用问题

2025-12-11 / 0 评论 / 4 阅读

题目

智能指针与循环引用问题

信息

  • 类型:问答
  • 难度:⭐⭐

考点

智能指针使用,循环引用分析,内存泄漏排查

快速回答

当两个对象通过shared_ptr相互引用时会导致循环引用,使引用计数无法归零,从而引发内存泄漏。解决方案:

  • 将其中一个指针改为weak_ptr打破循环
  • 使用weak_ptr::lock()安全访问对象
  • 避免在可能形成环状结构的场景滥用shared_ptr
## 解析

问题背景

在C++中,shared_ptr通过引用计数实现自动内存管理。但当两个对象相互持有对方的shared_ptr时,会形成循环引用,导致引用计数永不归零,对象无法被销毁。

原理说明

shared_ptr的引用计数机制:

  • 每次复制shared_ptr时引用计数+1
  • 每次析构shared_ptr时引用计数-1
  • 当引用计数归零时销毁对象
循环引用导致对象A持有对象B的指针,对象B又持有对象A的指针,形成闭环,引用计数始终≥1。

代码示例

问题代码(内存泄漏)

#include <memory>

class B;

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

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

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();

    a->b_ptr = b;  // A引用B
    b->a_ptr = a;  // B引用A

    // 退出作用域时A和B不会被销毁
    return 0;
}

解决方案代码

class B;

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

class B {
public:
    std::weak_ptr<A> a_ptr;  // 关键修改:使用weak_ptr
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();

    a->b_ptr = b;
    b->a_ptr = a;  // weak_ptr不增加引用计数

    // 安全访问示例
    if(auto ptr = b->a_ptr.lock()) {
        ptr->do_something();
    }
    return 0;  // 对象正常销毁
}

最佳实践

  • 所有权设计:明确对象间的所有权关系(独占/共享/弱引用)
  • weak_ptr使用场景
    • 打破循环引用
    • 缓存系统(不延长对象生命周期)
    • 观察者模式中的观察者列表
  • lock()检查:访问weak_ptr前必须用lock()检查有效性

常见错误

  • 直接解引用weak_ptr(应使用lock()
  • 在循环结构中误用shared_ptr(如双向链表、树结构的父节点)
  • 忽略lock()的返回值检查导致访问悬空指针

扩展知识

  • enable_shared_from_this:在类的成员函数中获取当前对象的shared_ptr
  • 引用计数开销shared_ptr的原子操作有性能代价,高频场景慎用
  • 工具检测:使用Valgrind、AddressSanitizer检测内存泄漏
  • 替代方案
    • 对于独占所有权使用unique_ptr
    • 手动管理生命周期(需谨慎)