Say, I have a class:
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))
{}
rhs.transform.reset()
to de-allocate the rhs after the move? return *this
at the end?The copy constructor for std::shared_ptr creates a second pointer which shared ownership with the first pointer. The pointee will be destroyed when all std::shared_ptr that point to it are destroyed.
By moving the shared_ptr instead of copying it, we "steal" the atomic reference count and we nullify the other shared_ptr . "stealing" the reference count is not atomic, and it is hundred times faster than copying the shared_ptr (and causing atomic reference increment or decrement).
The move constructor and move assignment operator are simple. Instead of deep copying the source object (a) into the implicit object, we simply move (steal) the source object's resources. This involves shallow copying the source pointer into the implicit object, then setting the source pointer to null.
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class, MemoryBlock , which manages a memory buffer.
Your copy and move constructors are equivalent to the implicit ones. Remove them. You don't need to write them out explicitly as the copy and move constructor of the std::shared_ptr
correctly implement both operations.
Or do I need to to call
rhs.transform.reset()
to de-allocate therhs
after the move?
No, the moved-from object will lose ownership after the move:
shared_ptr(shared_ptr&& r) noexcept; template<class Y> shared_ptr(shared_ptr<Y>&& r) noexcept;
Remark: The second constructor shall not participate in overload resolution unless
Y*
is convertible toT*
.Effects: Move-constructs a shared_ptr instance from r. Postconditions:
*this
shall contain the old value ofr
.r
shall be empty.r.get() == nullptr
.
As for the copy and move-assignment operators, they too will be equivalent. Move-assignment will correctly transfer ownership and the copy-constructor will perform a shallow copy so that both shared_ptr
s have ownership.
If a shallow copy (shared ownership) is really want you want, then shared_ptr
is the correct tool. Otherwise I'd suggest using unique_ptr
if you want to implement unique ownership.
This code is correct. If GameObject
is movable, it makes perfect sense to move transform
. The shared_ptr
move constructor will do the right thing for you here - it will transfer ownership of the Transform
. You do not need to call reset()
, that is an extraneous operation - you should only need to rely on your member object's move constructors to be properly implemented, which for shared_ptr
they most certainly are.
Member-wise copy is correct, and shared_ptr
will copy correctly for you.
Yes.
Note that if your class entirely consists of objects that have all the right operators implemented, you do not need to write them yourself. The default constructors and assignment operators will already do member-wise copy/move, which is exactly correct. Not having to write any of the basic 5 functions is called the 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With