Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move Constructor and = operator for Shared Pointer

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))
{}
  1. Is this the correct to create a move constructor for a class that has a shared_ptr member variable? Or do I need to to call rhs.transform.reset() to de-allocate the rhs after the move?
  2. How about the copy constructor?
  3. Presumably, the copy and move assignments look basically the same as the ctors, just with a return *this at the end?
like image 386
rohunb Avatar asked Apr 08 '15 14:04

rohunb


People also ask

What happens when copy constructor of shared pointer called?

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.

What happens when you move a shared pointer?

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).

What is move constructor and assignment operator?

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.

What is the use of move constructor in C++?

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.


2 Answers

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 the rhs 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 to T*.

Effects: Move-constructs a shared_ptr instance from r. Postconditions: *this shall contain the old value of r. 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_ptrs 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.

like image 156
David G Avatar answered Sep 19 '22 10:09

David G


  1. 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.

  2. Member-wise copy is correct, and shared_ptr will copy correctly for you.

  3. 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
like image 43
Barry Avatar answered Sep 20 '22 10:09

Barry