C++ weak_ptr用法详解 _ 观察者模式与解决循环引用【干货】
weak_ptr不持有引用计数,不能直接解引用,必须通过lock()获取shared_ptr以安全访问对象。在观察者模式中,用weak_ptr存储Subject引用可避免循环引用,Subject销毁后lock()返回空。lock()与expired()各有用途,且需注意线程安全与裸指针陷阱。
C++ 的 weak_ptr 是个好东西,但也是个容易让人在细节上栽跟头的工具。它不持有引用计数,所以不能像普通指针那样直接解引用,必须通过 lock() 先拿到 shared_ptr 才能安全操作。这个机制背后的逻辑是什么,又该怎么用好它,咱们来拆开聊聊。
weak_ptr 为什么不能直接访问对象
原因其实很简单:weak_ptr 本身不持有引用计数,它只是对 shared_ptr 所管理对象的一个“弱观察”。它没有重载 * 和 -> 运算符,所以直接调用 get() 或者解引用,编译阶段就会报错。
正确的做法是调用 lock() 方法,它会返回一个 shared_ptr。如果原来的对象还在,这个 shared_ptr 就是有效的;如果对象已经被释放,返回的就是空 shared_ptr,判断一下 !ptr 就能知道。
- 千万别写
wp.get()->do_something()—— 编译直接报错 - 也别写
*wp—— 同样编译不过 - 必须写成
if (auto sp = wp.lock()) { sp->do_something(); } lock()本身是线程安全的,但检查和实际使用之间仍然存在一个竞态窗口,所以“检查后立刻用”是个关键习惯
在观察者模式中如何用 weak_ptr 避免循环引用
观察者模式是循环引用的经典温床。典型场景是 Subject 持有一堆 shared_ptr,而 Observer 又持有一个指向 Subject 的 shared_ptr。这样一来,两者互相强引用,析构函数永远别想被调用。
解决办法很简单:Observer 改用 weak_ptr 来存储对 Subject 的引用。这样 Observer 不会增加 Subject 的引用计数,Subject 销毁后 Observer 仍然可以安全存在,只是下次调用 lock() 时会失败。
- Subject 侧保持
shared_ptr给外部使用,内部通知逻辑不变 - Observer 在构造时接收
shared_ptr,但内部存成weak_ptr - 每次回调前必须
if (auto subj = subject_wp.lock()) { subj->notify(...); } - 注意:不要在构造函数里把
this注册进 Subject 后,又在析构函数里反注册。此时 Subject 可能已经销毁,lock()返回空,反注册逻辑应该静默跳过
weak_ptr::lock() 和 expired() 的行为差异
expired() 是一个轻量级的检查,只读取控制块中的引用计数,不加锁也不增加计数,所以速度很快。lock() 则要原子地尝试将弱引用提升为强引用,开销略高,而且有可能失败。
- 仅判断是否失效,用
wp.expired()—— 快,适合日志、断言或快速跳过 - 需要后续使用对象,必须用
wp.lock()—— 它同时完成检查和“保活”,避免检查后对象被销毁 - 千万别写
if (!wp.expired()) { wp.lock()->do_something(); }这种代码,两次访问控制块之间,对象可能已经被其他线程销毁了,结果就是未定义行为 - 更糟的是
wp.lock().get()->do_something(),万一lock()返回空shared_ptr,get()返回空指针,解引用直接崩溃
weak_ptr 无法解决所有循环引用,注意原始指针陷阱
有人会试图用裸指针 Subject* 来替代 weak_ptr,觉得“我手动保证不 dangling 就行”。但在多线程、异步回调、延迟执行这些场景下,你根本没法确定 Subject 的生命周期边界,这种做法极其危险。
weak_ptr的价值不在于“省内存”,而在于提供一种可验证、线程安全的对象存活检查机制- 如果 Observer 的生命周期严格短于 Subject(比如栈上的临时对象),用裸指针倒也不是不行,但必须用文档明确约束,而且禁止跨函数传递或存储
- 任何涉及
std::function、lambda 捕获、消息队列延迟调用的场景,裸指针基本就等于悬垂指针 - 注意:
weak_ptr本身不防逻辑错误。比如误把不同shared_ptr创建的weak_ptr混用,控制块不一致会导致lock()永远失败
真正容易被忽略的点是:weak_ptr 必须由同一个 shared_ptr 初始化而来,跨实例赋值或从不同源头构造,会让观察彻底失效。
WPS Office是一款面向macOS用户的综合办公套件,将文字处理、电子表格、演示文稿、PDF工具、模板与云协作整合在同一应用中。它兼容常见Microsoft Office格式,支持Intel及Apple芯片Mac,基础功能可免费使用,
Shapr3D是一款面向工业设计、机械工程、建筑概念和三维打印工作流的CAD软件。Mac版采用Parasolid建模内核,支持草图约束、实体建模、工程图、可视化渲染及常见CAD格式交换,并可通过账户在多台设备之间同步项目。
REAPER是Cockos开发的数字音频工作站,提供多轨音频与MIDI录制、剪辑、处理、混音和母带制作工具。Mac版兼容Intel与Apple芯片,支持AU、VST、VST3、CLAP等插件格式,并提供高度可定制的工作流程。
Adobe Photoshop 是面向摄影师、设计师和内容创作者的专业图像处理工具,可在 Windows 上完成照片修饰、选区抠图、图层合成、文字排版、绘画及生成式编辑。软件采用订阅授权,由 Creative Cloud 管理安装、更新与许
Adobe InDesign 是面向印刷与数字出版的专业页面排版工具,可制作书籍、杂志、宣传册、海报、交互式 PDF 和电子书。它提供精细的文字样式、多页面管理、印前检查及多格式输出能力,并能与 Photoshop、Illustrator
Adobe Illustrator 是用于绘制矢量图形、插画、图标、徽标和版式的专业设计工具。它以路径和锚点为核心,作品放大后仍能保持清晰,并提供画板、文字、渐变、图像描摹及精确变换等功能,适合品牌视觉、印刷物料、数字界面和商业插画创作。













