C++ 智能指针:auto_ptr / unique_ptr / shared_ptr / weak_ptr 原理与使用

头文件:<memory>,核心思想:RAII,自动管理堆内存,避免内存泄漏。

一、auto_ptr(C++98,已废弃,不要使用)

特点:拷贝时转移资源所有权。

复制代码
auto_ptr<Date> p1(new Date);
auto_ptr<Date> p2 = p1; // p1把资源管理权转移给p2,p1变成空指针

缺陷:拷贝后原指针悬空,极易踩坑。C++11 后被淘汰,工程里禁止使用。

二、unique_ptr 唯一指针(C++11)

核心:不支持拷贝,只支持移动 ,同一时刻只能有一个unique_ptr持有资源。

复制代码
unique_ptr<Date> u1(new Date);
// unique_ptr<Date> u2 = u1; // 编译报错,拷贝构造被delete
unique_ptr<Date> u3 = move(u1); // 移动,u1置空,u3接管资源
  • 原理:把拷贝构造、赋值重载函数 delete,禁用拷贝;

  • 适用场景:资源不需要共享,独占所有权,性能开销几乎和裸指针一样;

  • 支持自定义删除器。

三、shared_ptr 共享指针(C++11)

核心:引用计数,支持拷贝 ,多个shared_ptr共同管理同一块堆资源。

复制代码
shared_ptr<Date> sp1(new Date);
shared_ptr<Date> sp2 = sp1; // 拷贝,引用计数+1

底层结构两块内存:

  1. _ptr:指向管理的资源;

  2. _pcount:引用计数(堆上的 int)。

析构逻辑:

  • 拷贝:引用计数++;

  • 析构:引用计数--;

  • 当计数减到 0 时,delete释放资源 + 释放计数内存。

make_shared推荐优先使用:

复制代码
auto sp = make_shared<Date>(); // 一次性分配资源+计数内存,效率更高

注意:shared_ptr构造函数是explicit,不能隐式类型转换。

shared_ptr 的致命问题:循环引用

双向链表场景:节点 A 存next指向 B,节点 B 存prev指向 A,都用shared_ptr。 两个对象析构时,双方引用计数都只能减到 1,永远不会等于 0,内存泄漏。

复制代码
n1 -> n2
n2 -> n1
两个节点互相持有shared_ptr,资源无法释放

👉 解决方案:weak_ptr。

四、weak_ptr 弱指针(C++11)

不参与资源所有权,不增加引用计数 ,专门用来解决shared_ptr循环引用。

  • 不能直接访问资源,没有 RAII;

  • 只能绑定到shared_ptr;

  • 调用.lock():返回一个shared_ptr,检查资源是否存活 。如果资源已经释放,返回空shared_ptr。

改造双向链表: 把next/prev从shared_ptr改成weak_ptr。 互相引用时不会增加引用计数,对象析构后计数可以正常减到 0,内存正常释放。

复制代码
struct Node
{
    int val;
    weak_ptr<Node> next;
    weak_ptr<Node> prev;
};

五、通用要点

  1. 智能指针析构默认调用delete释放资源,支持自定义删除器,可以管理数组、文件句柄等;

  2. shared_ptr/unique_ptr支持bool隐式转换:if(sp)判断是否持有有效资源;

  3. 不要把裸指针同时交给多个shared_ptr管理,会造成重复释放。

总结选型

|------------|----------------|-------|--------------------|
| 智能指针 | 拷贝 | 核心原理 | 适用场景 |
| auto_ptr | 转移所有权 | 废弃 | 不要用 |
| unique_ptr | ❌禁止拷贝 ✅支持 move | 独占资源 | 独占所有权,默认首选 |
| shared_ptr | ✅支持拷贝 | 引用计数 | 需要多指针共享资源 |
| weak_ptr | 不持有资源 | 不增加计数 | 解决 shared_ptr 循环引用 |

编码建议:优先用unique_ptr;确需共享再选shared_ptr;存在双向引用 / 环形结构,配合weak_ptr。

相关推荐
汉克老师2 分钟前
GESP2026年9月认证C++一级( 第二部分判断题(1~10题)精讲
c++·gesp·小学生·学c++编程
福兮说3 分钟前
URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错
开发语言·前端·javascript·node.js·url
小的时候可菜了27 分钟前
JS 异步的本质:从单线程到 thenable(一条进化链)
开发语言·前端·javascript
可乐鸡翅yeah_34 分钟前
hls.js 配置 loadStartPosition,点播起始播放位置踩坑
开发语言·javascript·网络协议·ecmascript·m3u8在线
一只旭宝41 分钟前
C++ 后端开发面试题整理
java·c++·面试
xlq2232243 分钟前
全面复习1
c++
Dylan的码园1 小时前
求平均成绩 矩阵数据处理 【循环处理 二维数组】
c++·线性代数·矩阵
en.en..1 小时前
Linux 进程退出exit(函数)
java·开发语言·算法
Cx330❀1 小时前
【Qt 进阶指南】详解 Qt 常用标准对话框与主窗口组件架构
开发语言·qt·ui·性能优化·架构·图形渲染
Light Gao1 小时前
Encoder 与 Decoder 详解:从 Embedding 到 Seq2Seq 的完整数据流
java·开发语言·embedding