0

0

C++中的std::weak_ptr是什么?(它是如何打破循环引用的)

尼克

尼克

发布时间:2026-02-14 18:25:02

|

781人浏览过

|

来源于php中文网

原创

std::weak_ptr是不增加引用计数的观察者,需通过lock()转为shared_ptr才能访问对象;用于打破循环引用,避免内存泄漏,但本身不保证线程安全。

c++中的std::weak_ptr是什么?(它是如何打破循环引用的)

std::weak_ptr 是什么,和 std::shared_ptr 有什么区别

它不是智能指针的“所有权持有者”,而是一个“观察者”——不增加引用计数,也不阻止对象销毁。std::weak_ptr 必须通过 lock() 升级成 std::shared_ptr 才能访问对象,而 lock() 返回一个空的 std::shared_ptr(即 .expired() == true)时,说明目标对象已被释放。

常见错误现象:std::weak_ptr::lock() 后直接解引用,没检查是否为空,导致段错误;或者误以为 weak_ptr 能延长对象生命周期。

  • weak_ptr 构造时不改变 shared_ptruse_count()
  • 不能直接用 operator->operator*,必须先 lock()
  • 它的存在本身不影响对象生存期,只依赖底层控制块是否还活着

为什么循环引用会导致内存泄漏

两个 std::shared_ptr 相互持有对方所管理的对象(比如 A 持有指向 B 的 shared_ptr,B 也持有指向 A 的 shared_ptr),它们的引用计数永远 ≥1,即使外部所有 shared_ptr 都离开了作用域,控制块和对象也无法释放。

典型使用场景:树节点父子双向引用、观察者模式中被观察对象与观察者互相持有、缓存中 key 和 value 形成闭环。

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

快剪魔方
快剪魔方

AI漫剧高效制作工具

下载
  • 调试时可打印 ptr.use_count(),发现本该为 1 却是 2+,就值得怀疑
  • 泄漏不是立刻可见的,常在长期运行服务中缓慢增长
  • ASan/UBSan 无法捕获这种逻辑泄漏,得靠设计阶段规避

怎么用 weak_ptr 打破循环引用

把其中一端的强持有改成弱持有。比如父子关系中,子节点用 std::weak_ptr<parent></parent> 存父节点,父节点仍用 std::shared_ptr<child></child> 管理子节点。

关键点在于:谁该“负责生命周期”,谁该“仅需临时访问”。通常“拥有关系”用 shared_ptr,“非拥有关系”(如反向导航、回调上下文)用 weak_ptr

struct Parent {
    std::shared_ptr<Child> child;
};
struct Child {
    std::weak_ptr<Parent> parent; // 不再是 shared_ptr
};
  • 访问前必须调用 parent.lock(),并检查返回值是否非空
  • 不要在构造函数里直接 lock() 并保存结果——此时父对象可能还没完全构造完
  • 如果频繁访问且确定生命周期安全,可临时提升一次并复用该 shared_ptr,避免重复锁开销

容易被忽略的坑:weak_ptr 不等于“线程安全”

weak_ptr 本身是线程安全的(拷贝/析构无数据竞争),但 lock() + 使用对象的过程不是原子的。多个线程同时 lock() 可能都拿到有效指针,也可能一个拿到、另一个拿到空指针——取决于对象是否恰好在此期间被释放。

更隐蔽的问题是:即使 lock() 成功,对象内容仍可能被其他线程修改,weak_ptr 不提供任何同步保障。

  • 多线程下不要假设 !ptr.expired()ptr.lock() 结果一致,中间可能发生释放
  • 若需强一致性,得配合 mutex 或原子标记(比如用 std::atomic_bool 标记“正在销毁”)
  • 注意 weak_ptr 的拷贝是轻量的,但 lock() 有原子操作开销,高频调用需权衡

真正难的不是加 weak_ptr,而是判断哪条引用链不该参与所有权管理——这得回到数据结构设计本身,而不是等内存涨了才回头改。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

541

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

26

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

38

2026.01.06

线程和进程的区别
线程和进程的区别

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

673

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

325

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

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

24

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

24

2026.01.21

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

94

2026.02.06

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

23

2026.02.13

热门下载

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

精品课程

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

共94课时 | 9.4万人学习

C 教程
C 教程

共75课时 | 4.7万人学习

C++教程
C++教程

共115课时 | 17.7万人学习

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

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