Item 18: Use std::unique_ptr
for exclusive-ownership resource management
std::unique_ptr 提供对象的独占所有权。不能复制 std::unique_ptr,只允许移动,,它将所有权从源指针转移到目标指针。
std::unique_ptr
的常见用法是作为工厂函数返回类型。
1 | class Investment { … }; |
Investment
继承关系的工厂函数可以这样声明:
1 | template<typename... Ts> //返回指向对象的std::unique_ptr, |
默认情况下,当 std::unique_ptr 超出生命周期范围时,销毁将通过delete
进行,但是在构造过程中,std::unique_ptr
对象可以被设置为使用自定义删除器。
假设想要在 Investment 对象被销毁之前打印日志,可以按如下方式实现:
1 | auto delInvmt = [](Investment* pInvestment) //自定义删除器 |
从这个例子可以看出:
- 所有的自定义的删除行为接受要销毁对象的原始指针,然后执行销毁操作。
- 使用自定义删除器时,删除器类型必须作为第二个类型实参传给
std::unique_ptr
。 - 为了将自定义删除器与智能指针关联,需要把自定义删除器作为构造函数的第二个实参。
- 不能将原始指针(比如
new
创建)赋值给std::unique_ptr
,从原始指针到智能指针的隐式转换会出问题。 - 自定义删除器的一个形参,类型是基类类型,不管创建的对象的真实类型是基类还是子类,为此基类必须有虚析构函数。
使用默认删除器时(如delete
),std::unique_ptr
对象和原始指针大小相同。当使用自定义删除器时,可能会增加std::unique_ptr 的大小。函数指针形式的删除器,会使std::unique_ptr
的大小增加一个字。对于函数对象形式的删除器来说,变化的大小取决于函数对象中存储的状态多少,无状态函数对象(比如不捕获变量的lambda表达式)对大小没有影响,因此尽量使用lambda。
1 | auto delInvmt1 = [](Investment* pInvestment) //无状态lambda的 |
std::unique_ptr
有两种形式,一种用于单个对象(std::unique_ptr<T>
),一种用于数组(std::unique_ptr<T[]>
)。
std::unique_ptr
还可以轻松高效的转换为std::shared_ptr
:
1 | std::shared_ptr<Investment> sp = //将std::unique_ptr |
重点
- std::unique_ptr 是一个轻量、快速、只能移动(move-only)的智能指针,用于管理具有独占所有权的资源。
- 默认情况下, 资源销毁通过delete实现,但可以指定自定义删除器。
- 有状态的自定义删除器和函数指针作为删除器会增加 std::unique_ptr 对象的大小。
- 将 std::unique_ptr 转换为 std::shared_ptr 很容易方便。