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。

相关推荐
apcipot_rain6 小时前
C++转Java日记(06)
java·开发语言
小七在进步13 小时前
string的使用(1)
c++·visual studio
SomeOtherTime13 小时前
Rust所有权(AI回答)
开发语言·后端·rust
Easyhood13 小时前
Android 开发入门实践(十四):Compose 副作用入门与四个常用 API
android·开发语言·kotlin·android jetpack·compose
白杨尚青14 小时前
C++进阶篇(六):map 和 set 的模拟实现——用红黑树封装 mymap/myset(迭代器 + operator[])
开发语言·c++·迭代器·stl·set·map·红黑树
kloc17 小时前
聊天室项目入口网关子服务的实现
linux·c++
饮水桶_T17 小时前
JAVA IO流笔记
开发语言·数据结构·算法
csdn_aspnet20 小时前
C++ 3个有序点的方向(Orientation of 3 ordered points)
c++·算法
Felven20 小时前
C. Brr Brrr Patapim
c语言·开发语言
小七在进步21 小时前
string的使用(2)
c++