CppMove Constructor

Move Constructor

Copying a resource-owning object — like the Buffer from the previous page — means allocating new memory and copying every element, even if the original object is about to be destroyed anyway. Moving avoids that waste: instead of duplicating the resource, a move constructor simply transfers ownership of it from one object to another, leaving the source object empty.

The move constructor signature

A move constructor takes its argument as an rvalue reference, written ClassName&&. An rvalue reference binds to temporary objects and to anything explicitly marked as movable with std::move().

A move constructor for Buffer

CPP
class Buffer {
public:
    int* data;
    int size;

    Buffer(int n) : data(new int[n]), size(n) {}

    // Copy constructor (deep copy — see previous page)
    Buffer(const Buffer& other) : data(new int[other.size]), size(other.size) {
        for (int i = 0; i < size; ++i) data[i] = other.data[i];
    }

    // Move constructor: steal 'other's pointer instead of copying the data.
    Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr; // leave 'other' in a valid, empty state
        other.size = 0;
    }

    ~Buffer() { delete[] data; } // safe even if data is nullptr
};

Notice that the move constructor doesn't allocate anything — it just copies the pointer value itself, then sets the source object's pointer to nullptr. Ownership moved; no new memory was allocated, and no data was copied. The source object (other) is left in a valid but unspecified/empty state: its destructor can still run safely (delete[] nullptr is a no-op), but its data is gone.

std::move: requesting a move explicitly

A named variable is always treated as an lvalue, even if you know you're done with it — so the compiler won't pick the move constructor automatically for it. std::move(x) doesn't actually move anything itself; it just casts x to an rvalue reference, which tells the compiler “it's fine to steal from this.”

std::move in action

CPP
#include <utility> // std::move

int main() {
    Buffer a(1000);

    Buffer b = a;             // copy constructor: a is untouched, b gets its own memory
    Buffer c = std::move(a);  // move constructor: c steals a's memory; a is now empty

    // Using 'a' after this point is legal but its buffer is gone (size == 0, data == nullptr).
}
The Rule of Five

Modern C++ extends the Rule of Three into the Rule of Five: once a class manages a resource directly, it typically needs all five special member functions defined together — destructor, copy constructor, copy assignment operator, move constructor, and move assignment operator.

The five special member functions

CPP
class Buffer {
public:
    Buffer(int n);                              // constructor
    ~Buffer();                                  // 1. destructor
    Buffer(const Buffer& other);                // 2. copy constructor
    Buffer& operator=(const Buffer& other);     // 3. copy assignment
    Buffer(Buffer&& other) noexcept;            // 4. move constructor
    Buffer& operator=(Buffer&& other) noexcept; // 5. move assignment
};
Why this matters: a performance example

Consider a function that builds and returns a large std::vector. Without move semantics, returning it by value would mean copying every element into the caller's variable — potentially millions of copies for a large vector.

Returning a large vector: move makes this cheap

CPP
#include <vector>

std::vector<int> makeLargeVector() {
    std::vector<int> result(1'000'000, 42);
    return result; // moved out, not copied
}

int main() {
    // Without move semantics, this would deep-copy 1,000,000 ints.
    // With move semantics (and/or copy elision), ownership of the
    // vector's internal heap buffer is simply transferred to 'v'.
    std::vector<int> v = makeLargeVector();
}
Tip
`std::vector`, `std::string`, and every standard container already implement proper move constructors, so code like this is fast by default — you get the benefit without writing any move logic yourself. You only need to hand-write move constructors for your own classes when they directly own a resource (a raw pointer, a file handle, and so on).
Note
A well-written move constructor should be marked `noexcept`. Standard containers like `std::vector` check this: if a type's move constructor might throw, operations like resizing fall back to the slower copy constructor instead, to preserve their exception-safety guarantees.
What's next
  • The this pointer is what every member function (including constructors) uses internally to refer to the current object.

  • The dedicated Move Semantics and Rvalue References pages later in this tutorial go deeper into how moving works throughout the standard library.