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

落伟大大_5154

落伟大大_5154

2026-02-14

821人浏览

原创

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_ptr 的 use_count()
  • 不能直接用 operator-> 或 operator*,必须先 lock()
  • 它的存在本身不影响对象生存期,只依赖底层控制块是否还活着

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

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

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

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

怎么用 weak_ptr 打破循环引用

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

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

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

struct Parent {
    std::shared_ptr<child> child;
};
struct Child {
    std::weak_ptr<parent> parent; // 不再是 shared_ptr
};
</parent></child>
  • 访问前必须调用 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,而是判断哪条引用链不该参与所有权管理——这得回到数据结构设计本身,而不是等内存涨了才回头改。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

2026.04.23

379

31

treenode的用法
treenode的用法

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

2023.12.01

2701

7

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

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

2025.12.22

376

20

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

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

2026.01.06

417

22

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

2026.05.09

472

25

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

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

2023.08.10

4478

6

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

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

2025.12.24

1249

20

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

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

2026.01.21

423

30

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

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

2026.01.21

579

24

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习