0

0

C++中的std::shared_ptr循环引用怎么解决?(使用std::weak_ptr打破环)

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-01-17 14:28:02

|

707人浏览过

|

来源于php中文网

原创

std::shared_ptr循环引用会导致内存泄漏,因引用计数无法归零;应使用std::weak_ptr在非拥有权方向打破循环,访问前必须调用lock()检查有效性。

c++中的std::shared_ptr循环引用怎么解决?(使用std::weak_ptr打破环)

std::shared_ptr 循环引用会导致内存泄漏

当两个或多个 std::shared_ptr 相互持有对方管理的对象时,引用计数永远无法归零,对象不会被析构——这不是悬空指针问题,而是资源永久驻留。典型场景是父子类双向关联、观察者与被观察者、图结构节点互相引用等。

用 std::weak_ptr 替换循环中的一方持有关系

std::weak_ptr 不增加引用计数,只“观察”所指向对象是否还存活。它不能直接解引用,必须先调用 lock() 转为 std::shared_ptr,失败则说明对象已被释放。

  • 只在“非拥有权”方向使用 std::weak_ptr:比如子节点持父节点的 std::weak_ptr,而非 std::shared_ptr
  • 访问前必须检查:调用 lock() 后判断返回的 std::shared_ptr 是否为空
  • 不能用 weak_ptr.get() 或直接解引用 —— 编译不通过,强制你处理生命周期不确定性

一个典型的父子类循环引用修复示例

下面代码中,Parent 持有 Childstd::shared_ptr,而 Child 改用 std::weak_ptr 持有 Parent,从而打破循环:

白瓜AI
白瓜AI

白瓜AI,一个免费图文AI创作工具,支持 AI 仿写,图文生成,敏感词检测,图片去水印等等。

下载
#include 
#include 

struct Parent;
struct Child;

struct Parent {
    std::shared_ptr child;
    ~Parent() { std::cout << "Parent destroyed\n"; }
};

struct Child {
    std::weak_ptr parent; // ← 关键:不增加引用计数
    ~Child() { std::cout << "Child destroyed\n"; }
};

int main() {
    auto p = std::make_shared();
    auto c = std::make_shared();
    p->child = c;
    c->parent = p; // weak_ptr assignment,无引用计数变化

    // 安全访问 parent(需检查 lock() 结果)
    if (auto parent_ptr = c->parent.lock()) {
        std::cout << "Parent still alive\n";
    } else {
        std::cout << "Parent already gone\n";
    }
} // ← 此处 p 和 c 都会正确析构

容易忽略的细节和陷阱

很多人以为只要用了 std::weak_ptr 就万事大吉,但实际还有几个关键点:

立即学习C++免费学习笔记(深入)”;

  • std::weak_ptr 本身不保证线程安全:多个线程同时调用 lock() 是安全的,但后续对所获 shared_ptr 的使用仍需同步
  • 不能用 std::weak_ptr 初始化另一个 std::shared_ptr(如 std::shared_ptr(weak_ptr) 会抛 std::bad_weak_ptr 异常),必须用 lock()
  • 在析构函数中调用 lock() 可能返回空 —— 因为此时对象可能正处在销毁链中,其他 shared_ptr 已释放
  • 若存在多重间接引用(A→B→C→A),仅改一处 weak_ptr 不够,得识别整个环并至少打断一处
循环引用不是编译期错误,也不会报 warning,只有在程序长期运行后发现内存持续上涨,或者用 ASan/Valgrind 检测到未释放块时才暴露。一旦怀疑,优先检查所有双向 shared_ptr 成员,把“被依赖方”换成 weak_ptr

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

481

2023.08.10

空指针异常处理
空指针异常处理

本专题整合了空指针异常解决方法,阅读专题下面的文章了解更多详细内容。

22

2025.11.16

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

4

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

1

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

10

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

33

2026.01.15

Java音频处理教程汇总
Java音频处理教程汇总

本专题整合了java音频处理教程大全,阅读专题下面的文章了解更多详细内容。

15

2026.01.15

windows查看wifi密码教程大全
windows查看wifi密码教程大全

本专题整合了windows查看wifi密码教程大全,阅读专题下面的文章了解更多详细内容。

42

2026.01.15

浏览器缓存清理方法汇总
浏览器缓存清理方法汇总

本专题整合了浏览器缓存清理教程汇总,阅读专题下面的文章了解更多详细内容。

7

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.8万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号