EMCPP条款18:使用std::unique_prt管理具有专属所有权的资源
std::unique_ptr
实现的是专属所有权语义。一个非空的 std::unique_ptr
总是拥有其所指涉到的资源。移动一个 std::unique_ptr
会将所有权从源指针移至目标指针(源指针被置空)。std::unique_ptr
不允许复制。std::unique_ptr
的一个常见用法是在对象继承谱系中作为工厂函数的返回型别。
假设我们有一个以 Investment
为基类的投资型别的继承谱系(例如股票、债券、不动产等)。
1 | class Investment { … }; |
这种继承谱系的工厂函数通常会在堆上分配一个对象并且返回一个指涉到它的指针,并当不再需要该对象时,由调用者负责删除之。这堪称 std::unique_ptr
的完美匹配,因为调用者需要对工厂函数返回的资源负责(亦即,对该资源拥有专属所有权),而当 std::unique_ptr
被析构时,又会自动对其所指涉到的对象实施 delete
。Investment
继承谱系的工厂函数应声明如下:
1 | template<typename... Ts> |
调用者可以在单个作用域内像这样来使用返回的 std::unique_ptr
:
1 | { |
std::unique_ptr
第二个模板参数是一个自定义析构器。如果这类问题,请回看原文
std::unique_ptr
以两种形式提供,一种是单个对象std::unique_ptr<T>
,另一种是数组 std::unique_ptr<T[)>
。std::unique_ptr
数组形式的存在,作为一种知识上的了解就够了。因为,std::array
、std::vector
和 std::string
这些数据结构几乎总是比裸数组更好。
std::unique_ptr
是 C++11 中表达专属所有权的方式,但它还有一个十分吸引人的特性,就是 std::unique_ptr
可以方便高效地转换成 std::shared_ptr
:
1 | // converts std::unique_ptr to std::shared_ptr |
正是这一特性使得 std::unique_ptr
如此适合作为工厂函数的返回型别。工厂函数并不知道调用者是对其返回的对象采取专属所有权语义好,还是共享所有权(例如,std::shared_ptr
)更合适。通过返回一个 std::unique_ptr
, 工厂函数就向调用者提供了最高效的智能指针,但它也不会阻止调用者把返回值转换成更具弹性的其他兄弟型别的智能指针。
要点速记
std::unique_ptr
是小巧、高速的、具各只移型别的智能指针,对托管资源实施专属所有权语义。- 默认地,资源析构采用
delete
运算符来实现,但可以指定自定义删除器。有状态的删除器和采用函数指针实现的删除器会增加std::unique_ptr
型别的对象尺寸。 - 将
std::unique_ptr
转换成std::shared_ptr
是容易实现的。