Home

C++ Move Semantics

Description

In the "old" days of C++ a lot of (complex) objects were copied over and over in application code. Yes you could make use of pointers and references to prevent this copying, but eventually you still needed to copy the objects. Since C++11 the move semantics is introduced which, when supported by classes, speeds up the code execution significantly.

The move semantics consists of a special Constructor implementation and a new assignment operator implementation. These are e.g. called when temporary objects are used or when an explicit move is used by calling std::move() on an object. When a class does not support the move semantics then its objects will automatically be copied instead of moved.

Move Constructor

The move constructor looks like a copy constructor but it uses 2 & characters for its argument, so it has the following syntax:

ClassName (ClassName&& other) noexcept;

Note that the parameter can't be made const like a normal copy constructor has, since the move constructor needs to make the 'other' object 'empty' after it has taken the ownership of its data.

The implementation of this special constructor can take over the ownership of the data managed by the 'other' object. It must make sure that the 'other' object becomes 'empty'. So an example implementation might look like:

ClassName::ClassName (ClassName&& other) noexcept
{
    this->m_data = other.m_data;
    other.m_data = nullptr;
}

Move Assignment

The move assignment operator looks like a normal assignment operator except it also uses 2 & characters for its argument, so it looks like the following:
ClassName& operator = (ClassName&& other) noexcept;

Note that also this parameter can't be made const since it also needs to be made 'empty' when its data is taken over by the current object.

The implementation of this special assignment operator looks almost the same as the implementation of the move Constructor but additionally it must first cleanup its own data before it takes over the ownership of all the data of the 'other' object. So an example implementation might look like:

ClassName& ClassName::operator = (ClassName&& other) noexcept
{
    delete this->m_data;
    this->m_data = other.m_data;
    other.m_data = nullptr;

    return *this;
}

Move Semantics With Inheritance

When dealing with inheritance structures implementing the move semantics becomes a bit more tricky since it is required to call the move semantics code of the base class as well.

The move constructor becomes like this:

DerivedClass::DerivedClass (DerivedClass&& other) noexcept
: BaseClass (std::move (other))
{
    this->m_data = other.m_data;
    other.m_data = nullptr;
}

The move assignment operator becomes like this:

DerivedClass& DerivedClass::operator = (DerivedClass&& other) noexcept
{
    BaseClass::operator = (std::move (other));
    
    delete this->m_data;
    this->m_data = other.m_data;
    other.m_data = nullptr;

    return *this;
}

More Information

For more detailed information refer to https://en.cppreference.com/w/cpp/utility/move.