C++ 智能指针详解:shared_ptr, auto_ptr 和 weak_ptr
1. shared_ptr:共享所有权智能指针
1.1 核心概念与定义
1.1.1 什么是 shared_ptr
std::shared_ptr 是 C++11 引入的共享所有权智能指针,它通过引用计数(Reference Counting) 机制实现多个智能指针共享同一个对象的所有权。与 unique_ptr 的独占所有权不同,shared_ptr 允许多个指针实例共同管理同一个动态分配的对象。
=、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
当最后一个指向对象的 shared_ptr 被销毁或重置时,对象才会被自动删除。这种机制使得 shared_ptr 在需要共享资源的场景下非常有用。
1.1.2 共享所有权语义
shared_ptr 的核心是共享所有权模型。多个 shared_ptr 实例可以指向同一个对象,它们通过内部的引用计数器来跟踪有多少个 shared_ptr 正在共享该对象。
1  | std::shared_ptr<int> ptr1(new int(42));  | 
1.1.3 引用计数机制
引用计数是 shared_ptr 的核心实现机制:
- 当新的 
shared_ptr指向现有对象时,引用计数增加 - 当 
shared_ptr被销毁或指向新对象时,引用计数减少 - 当引用计数降为 0 时,对象被自动删除
 
1.2 内部实现与控制块
1.2.1 控制块(Control Block)结构
shared_ptr 的内部实现比 unique_ptr 复杂,它包含两个指针:
- 指向管理对象的指针
 - 指向控制块的指针
 
控制块包含:
- 引用计数器(use count)
 - 弱引用计数器(weak count)
 - 删除器(deleter)
 - 分配器(allocator)
 
1  | template<typename T>  | 
1.2.2 引用计数的原子操作
为了保证线程安全,shared_ptr 的引用计数操作必须是原子的:
1  | // 近似实现  | 
1.3 常用接口与用法
1.3.1 构造与析构
1  | // 多种构造方式  | 
1.3.2 所有权管理接口
1  | std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();  | 
1.3.3 操作符重载
1  | std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();  | 
1.4 性能特点与开销
1.4.1 内存开销
- 控制块开销:每个被管理的对象都有一个控制块
 - 指针开销:
shared_ptr本身包含两个指针(对象指针和控制块指针) - 总大小:通常是原始指针的两倍大小
 
1.4.2 运行时开销
- 原子操作:引用计数的增减需要原子操作,有性能开销
 - 控制块分配:需要额外的内存分配(除非使用 
make_shared) 
1.4.3 make_shared 的优势
1  | // 传统方式:两次内存分配(对象 + 控制块)  | 
make_shared 将对象和控制块分配在连续内存中,提高了缓存局部性并减少了一次内存分配。
1.5 实际应用场景
1.5.1 共享资源管理
1  | class Resource {  | 
1.5.2 缓存系统
1  | class Cache {  | 
2. weak_ptr:弱引用智能指针
2.1 核心概念与定义
2.1.1 什么是 weak_ptr
std::weak_ptr 是一种不控制对象生命周期的智能指针,它是对由 shared_ptr 管理的对象的弱引用。weak_ptr 不会增加对象的引用计数,因此不会阻止对象的销毁。
2.1.2 弱引用语义
weak_ptr 的主要用途是打破 shared_ptr 的循环引用问题。它允许观察一个对象而不影响其生命周期。
1  | std::shared_ptr<MyClass> shared = std::make_shared<MyClass>();  | 
2.2 内部实现机制
2.2.1 与控制块的交互
weak_ptr 内部也包含指向控制块的指针,但它只操作弱引用计数:
1  | template<typename T>  | 
2.3 常用接口与用法
2.3.1 基本操作
1  | std::shared_ptr<MyClass> shared = std::make_shared<MyClass>();  | 
2.3.2 解决循环引用问题
1  | class Node {  | 
2.4 实际应用场景
2.4.1 观察者模式
1  | class Subject;  | 
2.4.2 缓存系统优化
1  | class CacheWithWeakPtr {  | 
3. auto_ptr:已废弃的智能指针
3.1 历史背景与设计缺陷
3.1.1 auto_ptr 的起源
std::auto_ptr 是 C++98 标准中引入的第一个智能指针,旨在提供基本的自动内存管理功能。它在当时是一个重要的创新,但后来被发现存在严重的设计缺陷。
3.1.2 主要设计缺陷
- 有问题的拷贝语义:
auto_ptr的拷贝操作会转移所有权 - 不适用于标准库容器:由于非常规的拷贝语义,不能安全地在 STL 容器中使用
 - 缺乏移动语义支持:在 C++11 之前,没有移动语义,导致 awkward 的所有权转移
 
3.2 问题示例与缺陷分析
3.2.1 危险的所有权转移
1  | // C++98/03 中的 auto_ptr(已废弃)  | 
3.2.2 与 STL 容器的不兼容
1  | std::vector<std::auto_ptr<int>> vec; // 危险的用法!  | 
3.3 从 auto_ptr 到 unique_ptr 的演进
3.3.1 为什么 unique_ptr 更好
1  | // unique_ptr 的明确语义  | 
3.3.2 迁移指南
如果遇到旧的 auto_ptr 代码,应该迁移到 unique_ptr:
1  | // 旧的 auto_ptr 代码  | 
3.4 现代替代方案总结
| 特性 | auto_ptr (已废弃) | unique_ptr | shared_ptr | weak_ptr | 
|---|---|---|---|---|
| 所有权模型 | 转移所有权 | 独占所有权 | 共享所有权 | 弱引用 | 
| 拷贝语义 | 转移所有权(危险) | 禁止拷贝 | 共享所有权 | 不增加引用计数 | 
| 移动语义 | 无(C++98) | 支持移动 | 支持移动 | 支持移动 | 
| 容器兼容性 | 不兼容 | 兼容(需移动) | 兼容 | 兼容 | 
| 线程安全 | 无 | 无(但可单独保护) | 引用计数原子操作 | 原子操作 | 
| 推荐使用 | 不应使用 | 独占资源 | 共享资源 | 打破循环引用 | 
4. 智能指针的最佳实践与陷阱
4.1 选择正确的智能指针
4.1.1 决策流程
是否需要共享所有权?
- 否 → 使用 
unique_ptr - 是 → 使用 
shared_ptr 
- 否 → 使用 
 是否需要观察而不拥有?
- 是 → 配合使用 
weak_ptr 
- 是 → 配合使用 
 是否有循环引用风险?
- 是 → 使用 
weak_ptr打破循环 
- 是 → 使用 
 
4.1.2 使用场景总结
1  | // 1. 独占资源 - unique_ptr  | 
4.2 常见陷阱与解决方案
4.2.1 循环引用问题
1  | // 错误示例:循环引用导致内存泄漏  | 
4.2.2 避免原始指针与智能指针混用
1  | // 危险的做法  | 
4.2.3 this 指针问题
1  | class MyClass : public std::enable_shared_from_this<MyClass> {  | 
4.3 性能优化建议
4.3.1 优先使用 make_shared 和 make_unique
1  | // 不推荐:两次内存分配  | 
4.3.2 避免不必要的 shared_ptr 拷贝
1  | // 不好的做法:不必要的拷贝  | 
通过理解和正确应用这些智能指针,可以编写出更安全、更高效的 C++ 代码,避免常见的内存管理错误。
- 本文作者: 迪丽惹Bug
 - 本文链接: https://lyroom.github.io/2025/09/23/C-智能指针详解:shared-ptr-auto-ptr-和-weak-ptr/
 - 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!