在 C++ 现代编程中,智能指针是管理动态内存的核心工具。标准库提供了两种最常用的智能指针:std::unique_ptr 和 std::shared_ptr。它们各自承担不同的职责,选择合适的指针能够既提高代码安全性,又保持性能。本文将从概念、内存管理、所有权语义、线程安全、以及典型使用场景等方面,对两者进行对比与说明,并给出实战建议。
1. 基本概念
| 指针 | 所有权类型 | 是否可复制 | 是否可移动 |
|---|---|---|---|
| `std::unique_ptr | |||
| ` | 独占所有权 | 否 | 是 |
| `std::shared_ptr | |||
| ` | 共享所有权 | 是 | 是 |
- 独占所有权:一个
unique_ptr在同一时刻只能有一个拥有者,所有权转移只能通过std::move完成。 - 共享所有权:一个
shared_ptr可以有多个指向同一对象的实例,内部维护引用计数,计数为 0 时自动销毁对象。
2. 内存管理
unique_ptr:在作用域结束或显式reset()时,立即调用delete释放资源。无需额外的引用计数开销。shared_ptr:维护一个计数器(通常与对象一起存放在分配的块中)。每一次复制会增加计数,销毁时递减。计数为 0 时才销毁对象。
因此,unique_ptr 在单线程或确定唯一所有者的场景下性能更佳;shared_ptr 适合需要多处共享生命周期的情况,但需承担计数器的读写开销。
3. 所有权语义
- 移动语义:
unique_ptr必须移动才能转移所有权;shared_ptr可以通过复制共享所有权。 - 不可复制:
unique_ptr禁止复制,以防止出现两份指针指向同一资源而导致双重删除。shared_ptr允许复制,但内部同步引用计数,避免双删。
4. 线程安全
unique_ptr:其析构过程不是线程安全的。若多个线程持有同一unique_ptr,必须自己同步。shared_ptr:计数器的增减是线程安全的(使用原子操作)。但对象本身的状态不受保护,需要外部同步。
5. 典型使用场景
| 场景 | 推荐指针 |
|---|---|
| 单例/生命周期由创建者决定 | unique_ptr |
| 所有权转移(例如工厂返回对象) | unique_ptr |
| 需要共享生命周期(如观察者模式、事件系统) | shared_ptr |
| 对象需要在多线程间共享 | shared_ptr + 外部锁或 std::atomic |
| 管理数组 | unique_ptr<T[]>(推荐)或 shared_ptr + 自定义删除器 |
| 与 C 风格 API 对接(需要裸指针或非所有权) | unique_ptr + std::weak_ptr 结合 |
6. 常见陷阱与建议
-
循环引用
shared_ptr在存在循环引用(A->B, B->A)时会导致内存泄漏。使用std::weak_ptr破坏循环。 -
性能评估
shared_ptr的引用计数是原子操作,导致多线程下的缓存失效。若不需要共享,尽量使用unique_ptr。 -
自定义删除器
对于非new分配的资源(如文件句柄、网络 socket),可以在unique_ptr或shared_ptr中提供自定义删除器。 -
不可用
make_shared的场景
make_shared会把计数器和对象一起分配,减少一次内存分配。若需要unique_ptr或自定义删除器,则需手动new。 -
避免裸指针逃逸
在公共接口中尽量返回shared_ptr或unique_ptr,不要暴露裸指针。
7. 代码示例
// unique_ptr 示例
std::unique_ptr <MyClass> create()
{
return std::make_unique <MyClass>();
}
// shared_ptr 示例
std::shared_ptr <MyClass> getShared()
{
static std::weak_ptr <MyClass> cache;
auto ptr = cache.lock();
if (!ptr) {
ptr = std::make_shared <MyClass>();
cache = ptr;
}
return ptr;
}
在上述 create 函数中,所有权始终归于调用者;getShared 通过 weak_ptr 实现单例缓存,避免循环引用。
8. 结语
std::unique_ptr 与 std::shared_ptr 并不是互斥关系,而是根据所有权需求进行组合使用的工具。正确理解它们的所有权语义、性能特性和线程安全机制,能帮助开发者写出既安全又高效的 C++ 代码。随着 C++20/23 的继续演进,智能指针的使用已成为现代 C++ 编程的基础。祝你编码愉快!