移动共享指针的构造函数和=运算符

时间:2015-04-08 14:42:04

标签: c++ move shared-ptr smart-pointers

说,我有一个班级:

class GameObject ///header file
{
    ....
    std::shared_ptr<Transform> transform;
}
///cpp file
//Copy Ctor
GameObject::GameObject(const GameObject& rhs)
   :transform(rhs.transform)
{}
//Move CTor
GameObject::GameObject(GameObject&& rhs)
    :transform(std::move(rhs.transform))
{}
  1. 为具有shared_ptr成员变量的类创建移动构造函数是否正确?或者我需要在移动后调用rhs.transform.reset()来取消分配rhs吗?
  2. 复制构造函数怎么样?
  3. 据推测,复制和移动作业看起来与ctors基本相同,最后只有return *this

3 个答案:

答案 0 :(得分:10)

您的复制和移动构造函数等效于隐式构造函数。删除它们。您不需要明确地将它们写出来,因为std::shared_ptr的复制和移动构造函数正确地实现了这两个操作。

  

或者我需要在移动后致电rhs.transform.reset()取消分配rhs吗?

不,移动后的对象将在移动后失去所有权:

shared_ptr(shared_ptr&& r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y>&& r) noexcept;
     

备注:除非Y*可转换为T*,否则第二个构造函数不应参与重载决策。

     

效果:从r构造一个shared_ptr实例。   后置条件:*this应包含旧值r r应为空。 r.get() == nullptr

对于复制和移动赋值运算符,它们也是等价的。移动分配将正确转移所有权,复制构造函数将执行浅层复制,以便shared_ptr拥有所有权。

如果您想要一个浅拷贝(共享所有权),那么shared_ptr是正确的工具。否则,如果您要实施唯一所有权,我建议您使用unique_ptr

答案 1 :(得分:1)

  1. 此代码是正确的。如果GameObject可移动,则移动transform非常有意义。 shared_ptr移动构造函数将在此处为您做正确的事情 - 它将转移Transform的所有权。你不需要调用reset(),这是一个无关紧要的操作 - 你应该只需要依赖你的成员对象的移动构造函数来正确实现,对于它们肯定是shared_ptr

  2. 会员明智的副本是正确的,shared_ptr会为您正确复制。

  3. 是。

  4. 请注意,如果您的类完全由实现了所有正确运算符的对象组成,则您不需要自己编写它们。默认构造函数和赋值运算符已经执行成员复制/移动,这完全正确。不必编写任何基本的5个函数称为Rule of Zero

    class GameObject {
        std::shared_ptr<Transform> transform;
        std::shared_ptr<SomethingElse> foo;
    };
    
    GameObject obj = ...;
    GameObject obj2 = obj;             // correct by default
    GameObject obj3 = std::move(obj2); // correct by default
    

答案 2 :(得分:0)

  1. 根据标准,shared_ptr的移动构造函数将原始shared_ptr留空,因此不需要reset()调用。

  2. 您编写的复制构造函数将在多个Transform之间共享一个GameObject对象。也许这就是你想要的。如果没有,您需要transform(new Transform(*rhs.transform))

  3. 之类的内容
  4. 基本上,但您使用赋值而不是构造成员(因为后者在语法上不会有效)。但是要警惕代码重复。另一种方法可能是使用像copy-and-swap这样的习惯用法将工作委托给构造函数:

    GameObject &operator=(const GameObject &rhs) {
      GameObject tmp(rhs);
      tmp.swap(*this);
      return *this;
    }
    
  5. 另外,正如其他几个人已经注意到的那样,如果你的构造函数和赋值与编译器生成的默认值(就像它们当前那样)相同,那么删除它们会更短更安全,只需使用默认为。