C++ 智能指针:从裸指针缺陷到标准库实践
**[作者的个人Gitee>🌟](友人A (friend-a188881041351) - Gitee.com)**🌟
每日一言:"**🌸🌸存在是一场无尽的对话,我们既是提问者,也是答案。🔅🔅"
文章目录
- [C++ 智能指针:从裸指针缺陷到标准库实践](#C++ 智能指针:从裸指针缺陷到标准库实践)
-
- [1. 裸指针的四大硬伤](#1. 裸指针的四大硬伤)
- [2. RAII:把"资源"变成"对象"](#2. RAII:把“资源”变成“对象”)
- [3. 智能指针选型速查表](#3. 智能指针选型速查表)
- [4. 独占场景:unique_ptr](#4. 独占场景:unique_ptr)
- [5. 共享场景:shared_ptr](#5. 共享场景:shared_ptr)
- [6. 循环引用与 weak_ptr](#6. 循环引用与 weak_ptr)
- [7. 删除器:让智能指针管理任何资源](#7. 删除器:让智能指针管理任何资源)
- [8. 不可用的过时方案](#8. 不可用的过时方案)
- [9. 快速迁移清单](#9. 快速迁移清单)
- [10. 结论](#10. 结论)
1. 裸指针的四大硬伤
| 问题 | 典型代码 | 后果 |
|---|---|---|
| 异常泄漏 | int* p=new int[1024]; DoSomething(); delete[] p; |
DoSomething() 抛异常 → delete[] 未执行 → 泄漏 |
| 忘记释放 | void f(){ int* p=new int; return; } |
函数早退 → 直接泄漏 |
| 重复释放 | delete p; delete p; |
未定义行为,多数平台崩溃 |
| 所有权模糊 | void f(int* p); |
调用方不知道是否该 delete,极易 double-delete 或泄漏 |
2. RAII:把"资源"变成"对象"
核心理念:资源在构造函数中获得,在析构函数中释放。
C++ 标准库已经帮你写好模板,只需选合适的智能指针即可。
3. 智能指针选型速查表
| 场景 | 推荐指针 | 关键属性 | 注意 |
|---|---|---|---|
| 独占,不可拷贝 | unique_ptr<T> |
move-only | 默认 delete,可自定义 deleter |
| 共享,可拷贝 | shared_ptr<T> |
引用计数 | 注意循环引用 → 搭配 weak_ptr |
| 观察,不拥有 | weak_ptr<T> |
不计数 | 使用前须 .lock() 判空 |
4. 独占场景:unique_ptr
cpp
// 单对象
std::unique_ptr<Date> up = std::make_unique<Date>(2024, 9, 11);
// 数组
std::unique_ptr<Date[]> up_arr = std::make_unique<Date[]>(5);
// 自定义释放
auto del = [](FILE* fp){ fclose(fp); };
std::unique_ptr<FILE, decltype(del)> up_file(fopen("log.txt", "w"), del);
要点
- 禁止拷贝,只能
std::move - 析构时自动
delete或调用自定义 deleter make_unique异常安全且只进行一次内存分配
5. 共享场景:shared_ptr
cpp
auto sp1 = std::make_shared<Date>(2024, 9, 11);
std::shared_ptr<Date> sp2 = sp1; // 引用计数 +1
assert(sp1.use_count() == 2);
// 自定义 deleter
std::shared_ptr<FILE> sp_file(fopen("cfg.json", "r"),
[](FILE* fp){ fclose(fp); });
线程安全
- 引用计数本身原子化,多线程拷贝/析构安全
- 指向对象本身的并发读写仍需外部加锁
6. 循环引用与 weak_ptr
cpp
struct Node {
std::shared_ptr<Node> next; // 导致循环引用
std::weak_ptr<Node> prev; // 打破循环
};
诊断步骤
- 两个
shared_ptr互相引用 → 引用计数始终 ≥1 - 把"非拥有"一方换成
weak_ptr→ 退出作用域时计数可归零 → 资源正确释放 - 访问前用
.lock()提升为shared_ptr,为空说明对象已析构
7. 删除器:让智能指针管理任何资源
| 资源类型 | 示例删除器 | 用法 |
|---|---|---|
new[] |
std::default_delete<T[]> |
unique_ptr<T[]> 已内置 |
fopen |
fclose |
shared_ptr<FILE>(fp, fclose) |
malloc |
free |
shared_ptr<void>(p, free) |
| 复合资源 | lambda | [](T* p){ custom_cleanup(p); } |
8. 不可用的过时方案
auto_ptr:C++98 遗留,拷贝转移所有权,极易悬空;C++11 起已废弃- 裸指针 + 手工
delete:仅用于教学或底层库,业务代码禁止
9. 快速迁移清单
- 全局搜索
new/new[] - 若对象生命周期唯一 → 替换为
unique_ptr/make_unique - 若需多处共享 → 替换为
shared_ptr/make_shared - 若存在双向引用 → 把"反向"指针改为
weak_ptr - 单元测试跑一遍 Valgrind / ASan,确认 0 泄漏
10. 结论
裸指针的泄漏、重复释放、所有权不清等问题在异常路径下几乎无法靠人工保证。
以 unique_ptr 处理独占,shared_ptr+weak_ptr 处理共享,配合自定义删除器,可覆盖 99% 的动态资源管理需求,且默认异常安全。
从今天起,业务代码中禁用 delete,让智能指针成为默认选择。
ique_ptr 处理独占,shared_ptr+weak_ptr处理共享,配合自定义删除器,可覆盖 99% 的动态资源管理需求,且默认异常安全。 从今天起,业务代码中禁用delete`,让智能指针成为默认选择。
如有错误,恳请指出。