【问题标题】:Transferring the ownership of object from one unique_ptr to another unique_ptr in C++11?在 C++11 中将对象的所有权从一个 unique_ptr 转移到另一个 unique_ptr?
【发布时间】:2014-12-06 17:59:48
【问题描述】:

C++11 中,我们可以使用std::move() 将一个对象的所有权转移给另一个unique_ptr。所有权转移后,让出所有权的智能指针变为nullget()返回nullptr.

std::unique_ptr<int> p1(new int(42));
std::unique_ptr<int> p2 = std::move(p1); // Transfer ownership

在将所有权转移给另一个 unique_ptr 时,这在哪些情况下有用?

【问题讨论】:

  • 就是这样。标准库中的智能指针实际上不应被视为自动释放的指针,而是根据所有权。您是否有一些数据只能由单个实体“拥有”,然后使用唯一指针。
  • 当您处理线程或套接字等不可复制数据并且需要将其从一个位置替换到另一个位置(例如,将其放入向量中)时,它很有用。

标签: c++ c++11 std move-semantics


【解决方案1】:

以下情况涉及将所有权从一个unique_ptr 转移到另一个:从函数返回,并作为参数传递给构造函数等函数。

假设你有一些多态类型Animal

struct Animal {
  virtual ~Animal() {}
  virtual void speak() = 0;
};

带有具体的子类CatDog

struct Cat : Animal {
  void speak() override { std::cout << "Meow!\n"; }
};

struct Dog : Animal {
  void speak() override { std::cout << "Woof!\n"; }
};

你想要一个简单的工厂,它根据所要求的服从价值来创造宠物。然后工厂必须返回一个指针。我们希望宠物工厂将创建的宠物的所有权转移给调用者,因此合理的返回类型是std::unique_ptr&lt;Animal&gt;

std::unique_ptr<Animal> createPet(double obedience) {
  if (obedience > 5.0)
    return std::make_unique<Dog>();
  return std::make_unique<Cat>();
} 

现在,假设我们要创建一个拥有宠物的House,那么我们可能希望将宠物传递给House 的构造函数。关于如何最好地将 unique_ptr 传递给构造函数存在一些争论 (see comments on this blog post),但它看起来像这样:

class House {
 private:
  std::unique_ptr<Animal> pet_;
 public:
  House(std::unique_ptr<Animal> pet) : pet_(std::move(pet)) {}
};

我们已将unique_ptr 传递给构造函数,然后将其“移动”到成员变量中。

调用代码可能类似于:

  auto pet = createPet(6.0);
  House house(std::move(pet));

构造House 后,pet 变量将为nullptr,因为我们已将宠物的所有权转移给House

Live demo

【讨论】:

  • 将构造函数更改为对 unique_ptr 进行 r 值引用会更好吗?例如。房子(std::unique_ptr&& 宠物)。那么你就不需要在 pet_ 的初始化列表中调用 std::move() 了吗?
  • @jfritz42 r 值引用本身就是一个 l 值,因此您仍然需要 std::moveTry it.
  • 好点!顺便说一句,这似乎是一个新的 C++ 习语。我想知道是否有人给它起了名字。 “转让所有权成语”?
  • 注意你也可以做移动赋值:uptr1 = std::move(uptr2)
【解决方案2】:

例如,如果你调用一个函数,你可以在参数列表中move你的unique_ptr,这样它就可以成为你函数签名的一部分

foo ( std::unique_ptr<T>&& ptr )

你可以用 foo 调用

foo( std::move(myPtr) );

请注意,std::move 是无条件强制转换,unique_ptr 是具有状态的对象,该状态的一部分是 unique_ptr 正在管理的指针,std::move 您正在强制转换整个对象,你并没有真正改变任何关于所有权的事情,std::unique_ptr 在使用 std::move 时并没有什么特别之处,因为 std::move 并不真正关心任何具体的事情,正如我所说的那样,这是一个无条件强制转换,unique_ptr 只是得到转换后,作为 unique_ptr&lt;T&gt; 类型实例的整个对象将被转换。

如果你想谈谈unique_ptr指向的对象的所有权转移,你应该考虑std::unique_ptr&lt;T&gt;本身提供的swap

【讨论】:

  • unique_ptr 在您将 move 转移到另一个 unique_ptr 时将托管对象的所有权转移到另一个 unique_ptr
  • @JonathanPotter 这在语义上是不正确的,move 什么也没做,它只是一个强制转换,它的语义是将表达式变为右值,而不是在 @987654342 的情况下转移所有权@,当函数调用发生时,unique_ptr 将被移动,这是一个副作用,如果您的目的是更改所有权,这是使用错误语义的最神秘的方法,只是不要这样做,使用swap,它具有正确的语义,它只是做你想做的事。
  • @user1308004,虽然std::move 确实没有实际移动,但它只是准备将其移出,这种区别在大多数情况下并不重要,因此他们决定称它为@ 987654346@。我看不出使用std::move 将所有权从一个unique_ptr 转移到另一个有什么神秘之处。这就是 unique_ptr 有一个移动构造函数和移动赋值的原因。
  • 如果你想使用swap 在函数调用中转移所有权,你必须通过非常量引用传递,它根本不表达意图。通过值或 rvalue-ref 传递可以更好地表达意图。
  • 我不建议通过 const 引用传递。我建议按值传递或右值引用。
猜你喜欢
  • 2018-04-30
  • 2015-05-16
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 2014-12-18
  • 1970-01-01
相关资源
最近更新 更多