EMCPP条款18:使用std::unique_prt管理具有专属所有权的资源

YiQi 管理员

std::unique_ptr 实现的是专属所有权语义。一个非空的 std::unique_ptr 总是拥有其所指涉到的资源。移动一个 std::unique_ptr 会将所有权从源指针移至目标指针(源指针被置空)。std::unique_ptr 不允许复制。std::unique_ptr 的一个常见用法是在对象继承谱系中作为工厂函数的返回型别。

假设我们有一个以 Investment 为基类的投资型别的继承谱系(例如股票、债券、不动产等)。

1
2
3
4
class Investment { … };
class Stock : public Investment { … };
class Bond : public Investment { … };
class RealEstate : public Investment { … };

这种继承谱系的工厂函数通常会在堆上分配一个对象并且返回一个指涉到它的指针,并当不再需要该对象时,由调用者负责删除之。这堪称 std::unique_ptr 的完美匹配,因为调用者需要对工厂函数返回的资源负责(亦即,对该资源拥有专属所有权),而当 std::unique_ptr 被析构时,又会自动对其所指涉到的对象实施 deleteInvestment 继承谱系的工厂函数应声明如下:

1
2
3
4
template<typename... Ts> 
std::unique_ptr<Investment> makeInvestment(Ts&&... params);
// return std::unique_ptr to an object created
// from the given args

调用者可以在单个作用域内像这样来使用返回的 std::unique_ptr:

1
2
3
4
{
// pInvestment is of type std::unique_ptr<Investment>
auto pInvestment = makeInvestment( arguments );
} // destroy *pInvestment

std::unique_ptr 第二个模板参数是一个自定义析构器。如果这类问题,请回看原文

std::unique_ptr 以两种形式提供,一种是单个对象std::unique_ptr<T>,另一种是数组 std::unique_ptr<T[)>std::unique_ptr 数组形式的存在,作为一种知识上的了解就够了。因为,std::arraystd::vectorstd::string 这些数据结构几乎总是比裸数组更好。

std::unique_ptr 是 C++11 中表达专属所有权的方式,但它还有一个十分吸引人的特性,就是 std::unique_ptr 可以方便高效地转换成 std::shared_ptr:

1
2
// converts std::unique_ptr to std::shared_ptr
std::shared_ptr<Investment> sp = makeInvestment( arguments );

正是这一特性使得 std::unique_ptr 如此适合作为工厂函数的返回型别。工厂函数并不知道调用者是对其返回的对象采取专属所有权语义好,还是共享所有权(例如,std::shared_ptr)更合适。通过返回一个 std::unique_ptr, 工厂函数就向调用者提供了最高效的智能指针,但它也不会阻止调用者把返回值转换成更具弹性的其他兄弟型别的智能指针。

要点速记

  • std::unique_ptr 是小巧、高速的、具各只移型别的智能指针,对托管资源实施专属所有权语义。
  • 默认地,资源析构采用 delete 运算符来实现,但可以指定自定义删除器。有状态的删除器和采用函数指针实现的删除器会增加 std::unique_ptr 型别的对象尺寸。
  • std::unique_ptr 转换成 std::shared_ptr 是容易实现的。
此页目录
EMCPP条款18:使用std::unique_prt管理具有专属所有权的资源