使用 std::unique_ptr<T>& 而不是 std::unique_ptr<T> 有什么优势吗?

Are there any advantages of using std::unique_ptr<T>& instead of std::unique_ptr<T>?

使用 std::unique_ptr<T>& 代替 std::unique_ptr<T> 有什么优势吗?例如,在函数参数中?

由于 std::unique_ptr 无法按设计复制,因此只能通过将指针的所有权传递给函数(即移动构造)来调用采用 std::unique_ptr<T> 的函数。调用代码将无法再访问指针。

使用 std::unique_ptr<T>&,您允许函数 'see' 指针并访问其成员,但调用代码保留所有权,因此仍然可以自己使用它。

Herb Sutter 解释了将智能指针传递给具有所有可能变化的函数的所有利弊。请阅读文章以获取更多信息,它非常好:

简而言之,在没有引用的情况下将 unique_ptr 传递给函数是没有意义的,除非您的预期行为是放弃指针的所有权。!

std::unique_ptr 只能移动,因此如果您按值传递 unique_ptr 则您无法在函数调用后提取其内容,但如果您按引用传递则可以检索值。以下是相同的示例代码:

#include <iostream>
#include <memory>
void changeUniquePtrReference(std::unique_ptr<int>& upr)
{
    *upr = 9;   
}
void changeUniquePtrValue(std::unique_ptr<int> upv)
{
    *upv = 10;
}
int main()
{
  std::unique_ptr<int> p(new int);
  *p =8;
  std::cout<<"value of p is "<<*p<<std::endl;
  changeUniquePtrReference(p);
  std::cout<<"value of p is "<<*p<<std::endl;
  changeUniquePtrValue(std::move(p));
  std::cout<<"Memory deallocated so below line will crash.. "<<std::endl;
  std::cout<<"value of p is "<<*p<<std::endl;

  return 0;
}

这两个变体不同,适用于特定情况。

如果您的函数采用 std::unique_ptr<T>,这意味着您获得了托管对象的所有权,并将删除它或将其保存在其他地方。

如果您取得 std::unique_ptr<T> &,您不会取得所有权,但您可以更改指针指向的内容,例如通过将其重置为不同的对象。

如果您不打算更改指针本身,您只需传递一个 T& 或一个 const T&,具体取决于您是否打算修改对象。传递 const std::unique_ptr<T> & 是没有用的,因为它不能让你比 T& 做更多的事情,缺点是潜在的额外间接寻址和对参数的不必要限制。

有几种不同的情况[​​=27=]

  1. 您想将所有权转移给函数
    • 使用std::unique_ptr<T>
  2. 您想允许函数修改指针
    • 使用std::unique_ptr<T> &
  3. 您想允许函数修改指针对象
    • 使用 T &,并在调用站点取消引用
    • 如果指针可能为空,则使用 T * 并在调用站点调用 unique_ptr::get
  4. 您想允许函数观察指针对象
    • 使用const T &,并在调用站点取消引用
    • 如果指针可能为空,则使用 const T * 并在调用站点调用 unique_ptr::get
  5. 您想允许函数拥有指针对象的副本
    • 使用T,并在调用站点取消引用
  6. 您对 unique_ptr<T> 的集合进行了范围循环(或 <algorithm> 调用),并且想要观察这些值,但没有 ranges::indirect (or similar)
    • 使用auto &,知道推断为std::unique_ptr<T> &(如果集合是const则为const std::unique_ptr<T> &),所以要在循环中解引用正文

就我个人而言,我只看到一种情况,您 应该 传递对 unique_ptr 的引用:您正在调用的方法可能 内部 决定 删除或放弃对象。这应该是非常罕见的,因为所有者放弃了所有权的决定,老实说这不是很好的设计。

如果子对象总是夺走所有权或删除对象,那么参数类型应该是unique_ptr并立即转移所有权。

如果子对象只是想访问指针内容,那么参数应该是一个raw指针。您可以为 const unique_ptr reference 提出一个论点,这将阻止您释放它,但这有什么意义呢?如果你觉得你需要一个超越原始的约定,你可以修饰那个原始指针类型名称,这样你就知道你不拥有它。