当前位置: 首页 > backend >正文

C++11新特性_标准库_智能指针_std::weak_ptr

     在 C++ 中,std::shared_ptr 通过引用计数实现了共享所有权,但如果存在循环引用(两个对象通过 std::shared_ptr 互相指向对方),会导致引用计数无法归零,最终引发内存泄漏。std::weak_ptr 是一种 “弱引用” 智能指针,不参与引用计数,专门用于解决循环引用问题。

循环引用的问题

当两个对象通过 std::shared_ptr 互相引用时,会形成循环依赖:每个对象的引用计数被对方的 std::shared_ptr 持握,即使外部不再有任何 std::shared_ptr 指向它们,双方的引用计数仍无法减至 0,导致对象无法被销毁,内存泄漏。

循环引用示例(内存泄漏)
#include <iostream>
#include <memory>class B;  // 前置声明class A {
public:std::shared_ptr<B> b_ptr;  // A 持有 B 的共享指针~A() { std::cout << "A 析构" << std::endl; }
};class B {
public:std::shared_ptr<A> a_ptr;  // B 持有 A 的共享指针~B() { std::cout << "B 析构" << std::endl; }
};int main() {auto a = std::make_shared<A>();  // a 的引用计数 = 1auto b = std::make_shared<B>();  // b 的引用计数 = 1a->b_ptr = b;  // b 的引用计数 +=1 → 2b->a_ptr = a;  // a 的引用计数 +=1 → 2// 此时,a 和 b 的引用计数均为 2// 当 main 函数结束时,a 和 b 离开作用域,引用计数减为 1(被对方的 shared_ptr 持有)// 由于引用计数无法归零,A 和 B 的析构函数不会被调用,内存泄漏!return 0;
}

运行结果:
终端不会输出 A 析构 和 B 析构,说明对象未被销毁。

std::weak_ptr 解决循环引用

std::weak_ptr 是一种弱引用指针,不参与引用计数,仅用于观察 std::shared_ptr 管理的对象。它的核心作用是:

  • 指向 std::shared_ptr 管理的对象,但不会增加引用计数;
  • 可以通过 lock() 方法获取 std::shared_ptr(若对象存活),确保安全访问;
  • 配合 expired() 方法判断对象是否已被销毁。
用 std::weak_ptr 打破循环引用

修改上述示例,将其中一个类的成员变量改为 std::weak_ptr,即可打破循环:

#include <iostream>
#include <memory>class B;  // 前置声明class A {
public:std::shared_ptr<B> b_ptr;  // A 仍用 shared_ptr 持有 B(假设 A 需要“强”拥有 B)~A() { std::cout << "A 析构" << std::endl; }
};class B {
public:std::weak_ptr<A> a_ptr;  // B 用 weak_ptr 观察 A(弱引用,不参与计数)~B() { std::cout << "B 析构" << std::endl; }
};int main() {auto a = std::make_shared<A>();  // a 的引用计数 = 1auto b = std::make_shared<B>();  // b 的引用计数 = 1a->b_ptr = b;  // b 的引用计数 +=1 → 2b->a_ptr = a;  // a 的引用计数 **不变化**(weak_ptr 不增加计数)// 当 main 函数结束时:// - a 离开作用域,引用计数减为 0(因为 b->a_ptr 是 weak_ptr,不持有计数)//   → A 对象被销毁,触发 A 的析构函数;// - a->b_ptr 被销毁,b 的引用计数减为 1(原计数 2 → 1);// - b 离开作用域,引用计数减为 0 → B 对象被销毁,触发 B 的析构函数。return 0;
}

运行结果:

A 析构
B 析构

关键机制总结

  1. std::weak_ptr 不参与引用计数
    当 B 的成员 a_ptr 是 std::weak_ptr 时,它不会增加 A 对象的引用计数。因此,当外部 astd::shared_ptr<A>)离开作用域时,A 的引用计数减为 0,触发 A 的析构,进而释放 A 持有的 B 的 std::shared_ptra->b_ptr),最终 B 的引用计数也减为 0,触发 B 的析构。

  2. 安全访问对象
    若 B 需要访问 A 对象,需通过 std::weak_ptr::lock() 方法获取 std::shared_ptr,确保对象存活:

    void B::doSomething() {if (auto a = a_ptr.lock()) {  // 若 A 对象存活,lock() 返回有效的 shared_ptr// 安全访问 a 的成员} else {// A 已销毁,处理空指针逻辑}
    }
    
  3. 判断对象存活
    通过 std::weak_ptr::expired() 方法可判断对象是否已被销毁(即引用计数是否为 0):

    if (!a_ptr.expired()) {// 对象存活
    }
    

总结

std::weak_ptr 通过弱引用机制(不参与引用计数)打破循环引用,确保对象在无外部强引用时能被正确销毁。它通常用于以下场景:

  • 解决 std::shared_ptr 的循环引用问题;
  • 缓存或观察者模式中,需要观察对象但不影响其生命周期;
  • 避免因强引用导致的对象无法析构。
http://www.xdnf.cn/news/3650.html

相关文章:

  • MATLAB技巧——norm和vecnorm两个函数讲解与辨析
  • Linux的环境变量
  • “会话技术”——Cookie_(2/2)原理与使用细节
  • [更新完毕]2025五一杯C题五一杯数学建模思路代码文章教学:社交媒体平台用户分析问题
  • Linux 信号
  • 反射机制补充
  • 滥用绑定变量导致Oracle实例宕机
  • Python数据结构与算法
  • [面试]SoC验证工程师面试常见问题(一)
  • AE脚本 关键帧缓入缓出曲线调节工具 Flow v1.5.0 Win/Mac
  • 使用 Tesseract 实现藏文OCR
  • 2025eBay母亲节消费图谱:非标商品5倍溢价背后的情感经济革命
  • Codeforces Round 1022 (Div. 2) D. Needle in a Numstack(二分)
  • 深入解析C++11基于范围的for循环:更优雅的容器遍历方式
  • 系统思考与第一性原理
  • sizeof的用途
  • 第 6 篇:AVL 树与 SB 树:不同维度的平衡探索 (对比项)
  • Redis源码阅读(一)跳表
  • P2196 [NOIP 1996 提高组] 挖地雷
  • Dify 安装 使用
  • 算法笔记.分解质因数
  • pytorch自然语言处理(NLP)
  • 一些读入时需要用到getchar()的时机
  • 微服务中组件扫描(ComponentScan)的工作原理
  • 序列数据(Sequential Data)​​:按顺序排列的动态信息载体
  • 深入拆解 MinerU 解析处理流程
  • 如何在linux服务器下载gitee上的模型
  • 【点对点协议(PPP)全解析】从原理到工程实践
  • JSON与字典的区别及示例
  • 数据结构6 · BinaryTree二叉树模板