智能指针——当 std::shared_ptr 可能悬空时使用 std::weak_ptr

发布时间:2026/6/10 23:01:58

智能指针——当 std::shared_ptr 可能悬空时使用 std::weak_ptr 文章目录当 std::shared_ptr 可能悬空时使用 std::weak_ptr创建与失效安全的原子访问方式经典使用场景性能与内部机制当 std::shared_ptr 可能悬空时使用 std::weak_ptrstd::weak_ptr 是一种行为类似shared_ptr 但不参与资源共享所有权的智能指针。它的核心职责是跟踪所指对象是否已被销毁即检测悬挂/空悬。关键矛盾weak_ptr 像 shared_ptr 一样指向对象但不影响引用计数。核心要点要点1当指针可能悬空时使用weak_ptr替代裸指针或shared_ptr2使用lock()进行原子检查访问绝不手动检查expired()后再直接解引用3三大典型场景缓存、观察者列表、打破循环引用4性能和shared_ptr同级控制块内含第二个弱引用计数weak count想用指针但不想占有资源、又想在资源销毁后能检测到 —— 就用std::weak_ptr。创建与失效autospwstd::make_sharedWidget();// 引用计数 1std::weak_ptrWidgetwpw(spw);// 引用计数仍为 1不变spwnullptr;// 引用计数 → 0Widget 被销毁// wpw 现在处于 悬挂dangle 状态悬挂的weak_ptr被称为已失效expired可通过expired()检测if(wpw.expired()){// wpw 不再指向任何有效对象}⚠️为什么不能直接解引用std::weak_ptr没有operator*或operator-。如果手动检查expired()后再解引用会引入竞态条件TOCTOU 漏洞在expired()检查和解引用之间其他线程可能销毁最后一个shared_ptr导致未定义行为。---安全的原子访问方式需要「检查 访问」合一的原子操作——通过从weak_ptr创建shared_ptr实现autospwpw.lock();// 若 wpw 失效返回空 shared_ptrif(sp){sp-doSomething();// 安全访问}std::shared_ptrWidgetsp(wpw);// 若 wpw 失效抛出 std::bad_weak_ptr✅最佳实践始终先 lock()再判断是否为 nullptr再访问对象。经典使用场景1️⃣缓存Cache—— 带失效检测的对象缓存std::shared_ptrconstWidgetfastLoadWidget(WidgetID id){staticstd::unordered_mapWidgetID,std::weak_ptrconstWidgetcache;autoobjPtrcache[id].lock();// 尝试从缓存获取if(!objPtr){// 缓存未命中或已失效objPtrloadWidget(id);// 重新加载cache[id]objPtr;// 以 weak_ptr 存入缓存}returnobjPtr;}2️⃣观察者模式Observer PatternclassSubject{std::vectorstd::weak_ptrObserverobservers;public:voidnotify(){for(autoweak:observers){if(autoobsweak.lock()){// 原子检查 获取obs-update();}}}};3️⃣打破 shared_ptr 循环引用方案问题裸指针A 被销毁后 B 持有悬空指针无法检测shared_ptrA↔B 循环引用引用计数永不为 0 → 内存泄漏weak_ptr✅B→A 不影响引用计数A 可正常析构B 可通过lock()检测classB{std::weak_ptrAa_ptr;// 替换 shared_ptr打破循环};性能与内部机制特性说明对象大小与shared_ptr一样大控制块使用同一个控制块引用计数控制块内含第二个「弱引用计数」weak count原子操作构造/析构/赋值都涉及原子操作weak_ptr 不参与对象引用计数的维护但操纵控制块中的弱引用计数用于追踪 weak_ptr 本身的存活直到所有 weak_ptr 也销毁后控制块才释放。

相关新闻