比方说,我有一个类:
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))
{}
- 为具有shared_ptr成员变量的类创建移动构造函数是否正确?或者我需要调用
rhs.transform.reset()
来在移动后取消分配rhs吗 - 复制构造函数怎么样
- 据推测,复制和移动分配看起来与actor基本相同,只是末尾有一个
return *this
您的复制和移动构造函数与隐式构造函数等价。拆下它们。当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*
,否则第二建造师不得参与过载解决。效果:Move从r构造了一个shared_ptr实例。后置条件:
*this
应包含r
的旧值r
应为空。CCD_ 11
至于复制和移动赋值操作符,它们也是等效的。移动赋值将正确地转移所有权,复制构造函数将执行浅层复制,以便两个shared_ptr
都具有所有权。
如果你真的想要一个浅拷贝(共享所有权),那么shared_ptr
就是正确的工具。否则,如果您想实现唯一所有权,我建议使用unique_ptr
。
-
此代码正确。如果
GameObject
是可移动的,那么移动transform
是完全合理的。shared_ptr
移动构造函数将在这里为您做正确的事情-它将转移Transform
的所有权。您不需要调用reset()
,这是一个无关的操作——您只需要依靠成员对象的move构造函数来正确实现,对于shared_ptr
来说,它们肯定是这样。 -
会员副本是正确的,
shared_ptr
将为您正确复制。 -
是的。
请注意,如果您的类完全由实现了所有正确运算符的对象组成,则不需要自己编写它们。默认构造函数和赋值运算符将已经执行成员复制/移动,这是完全正确的。不必编写基本的5个函数中的任何一个被称为零规则:
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
-
根据标准,
shared_ptr
的move构造函数将原始shared_ptr
保留为空,因此不需要reset()
调用。 -
您编写的复制构造函数将在多个
GameObject
之间共享一个Transform
对象。也许这就是您想要的。如果没有,你需要transform(new Transform(*rhs.transform))
这样的东西。 -
本质上,但您应该使用赋值而不是构造成员(因为后者在语法上无效)。不过,要警惕代码重复。另一种方法是使用类似复制和交换的习惯用法将工作委托回构造函数:
GameObject &operator=(const GameObject &rhs) { GameObject tmp(rhs); tmp.swap(*this); return *this; }
此外,正如其他一些人已经注意到的那样,如果您的构造函数和赋值与编译器生成的默认值(目前的情况)相同,那么删除它们并只使用默认值会更短、更安全。