unique_ptr与auto_ptr
std::unique_ptr 同样是 C++11 中引入的,用于表示对动态分配对象的独占所有权(Exclusive Ownership)。
std::unique_ptr:独占所有权的轻量级管理者
与 shared_ptr 的“共享”理念完全相反,unique_ptr 遵循“独裁”模式:在任何时刻,只能有一个 unique_ptr 指向并拥有一个给定的对象。当这个 unique_ptr 被销毁或重置时,它所拥有的对象也会被立即销毁。
独占所有权 (Exclusive Ownership): 一个资源(内存、文件句柄等)的生命周期由唯一的 unique_ptr 控制。这从根本上杜绝了“谁该删除指针”的混乱问题,所有权模型非常清晰。
轻量级与高性能 (Lightweight & High-Performance): unique_ptr 是一个零成本抽象(Zero-cost Abstraction)。它内部没有引用计数,也没有控制块。在大多数情况下,一个 unique_ptr 的大小与一个原始指针完全相同。且它的操作(如访问成员)与操作原始指针一样快,没有任何额外的性能开销。
不可拷贝,但可移动 (Non-copyable, but Movable): 为了保证所有权的“独占性”,unique_ptr 删除了拷贝构造函数和拷贝赋值运算符。你不能像 shared_ptr 那样简单地复制它。
1 | std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(); |
1 | std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(); |
如何使用
首先是创建 unique_ptr (推荐使用 std::make_unique, C++14 中引入)
1 |
|
所有权的转移是 unique_ptr 的核心操作模式,最常见的场景是从函数返回。
1 | std::unique_ptr<MyClass> create_widget() { |
通过返回值和 std::move,unique_ptr 实现了清晰、安全的所有权在不同作用域之间的传递
现代 C++ 编译器通常会做得更极致。它们会使用一种叫做“返回值优化”的技术。在这种情况下,编译器会发现 create_widget 内部创建的指针最终会进入 main 函数的 my_widget 中,于是它会省略掉中间的“移动”步骤,直接在 my_widget 的内存位置上构造那个 unique_ptr。从外部看,就好像 create_widget 函数直接把对象变到了 main 函数里一样。
此外, 还有一些其他函数:
- 访问:像普通指针一样使用 * 和 ->。
- 获取原始指针:使用 get() 方法,规则和风险与 shared_ptr 的 get() 类似。
- 释放所有权:调用 release() 方法。它会放弃所有权并返回原始指针,但不会删除对象。调用者需要手动管理返回的指针。
- 重置:调用 reset() 方法。它会销毁当前拥有的对象,并可以选择性地接管一个新的对象。
高级特性
unique_ptr 比看起来更灵活,它支持两个强大的高级特性:
自定义删除器 (Custom Deleters)
与 shared_ptr 不同,unique_ptr 的删除器类型是其自身类型的一部分。这使得它仍然是零开销的,但不同删除器类型的 unique_ptr 是不同的类型。这使得 unique_ptr 非常适合用于管理任何需要配对操作的资源,完美实践 RAII(资源获取即初始化)。
1 |
|
数组支持
unique_ptr 对动态分配的数组有特殊的重载版本,使用时需要加上 []。
- 创建:std::make_unique<T[]>(size)
- 析构:它会自动调用 delete[] 而不是 delete,这是正确的数组内存释放方式。
- 访问:它重载了 operator[]
来访问数组元素,但没有 * 和 ->。
1
2
3
4
5
6
7
8// 创建一个包含 10 个整数的动态数组
std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);
// 通过 operator[] 访问元素
for (int i = 0; i < 10; ++i) {
arr_ptr[i] = i * i;
}
// arr_ptr 离开作用域时,会自动调用 delete[] arr_ptr.get();
黄金法则:默认使用 std::unique_ptr。
在现代 C++ 中,当你需要动态分配内存时,unique_ptr 应该是你的第一选择。它的所有权模型清晰,性能无损,完全符合 RAII 思想。何时使用 unique_ptr?
- 当你需要一个指向动态对象的指针,并且该对象的生命周期应该与这个指针的作用域绑定时。
- 作为工厂函数的返回值,安全地将新创建对象的所有权转移出去。
- 在类中作为成员,管理一个只属于该类实例的资源(例如,PIMPL 模式的实现)。
只有当你明确需要共享一个资源的所有权,即多个独立的观察者都需要延长该资源的生命周期时,才应该“升级”到使用 std::shared_ptr。
std::auto_ptr
什么是auto_ptr? 真不熟
std::auto_ptr 是 std::unique_ptr 的祖先, 它在 C++11 中被不推荐使用(deprecated),并在 C++17 中被彻底移除, 完全被std::unique_ptr代替。