Menu
Coddy logo textTech

Move Constructor

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 21 of 104.

A move constructor transfers resources from a temporary object instead of copying them. While a copy constructor duplicates data, a move constructor "steals" the resources, leaving the source object in a valid but empty state. This avoids expensive deep copies when the source object is about to be destroyed anyway.

The move constructor takes an rvalue reference, denoted by &&:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Move constructor
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Steal the pointer
        size = other.size;
        other.data = nullptr;    // Leave source in valid state
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

The key difference from copying: we don't allocate new memory. We simply take ownership of the existing memory and set the source's pointer to nullptr so its destructor won't delete our data.

Move constructors are called when initializing from temporary objects or when using std::move():

Buffer createBuffer() {
    return Buffer(1000);    // Move constructor used (return value)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Explicit move - b1 is now empty

Mark move constructors as noexcept when possible. This tells the compiler the operation won't throw exceptions, enabling important optimizations in containers like std::vector.

challenge icon

Challenge

Easy

Let's build a data packet system that demonstrates how move constructors efficiently transfer ownership of resources. You'll create a DataPacket class that manages a dynamically allocated array of bytes, and implement a move constructor that "steals" the data instead of copying it.

You'll create two files to organize your code:

  • DataPacket.h: Define a DataPacket class that manages a payload of integer data. Your class should have:
    • Private members: a pointer to an integer array (payload), a size for the number of elements, and a packetId (string) to identify the packet
    • A parameterized constructor that takes a packet ID and size, allocates the array, and fills it with values from 0 to size-1. Print "Packet <id> created with size <size>"
    • A move constructor marked noexcept that transfers ownership of the payload from the source object. Print "Packet <id> moved". Remember to leave the source in a valid empty state (nullptr, size 0)
    • A destructor that frees the memory if the pointer isn't null, and prints "Packet <id> destroyed"
    • A getSize() method that returns the current size
    • A getId() method that returns the packet ID
    • A getSum() method that returns the sum of all elements in the payload (return 0 if payload is null)
  • main.cpp: Demonstrate move construction by reading a packet ID and size from input. Then:
    • Create a DataPacket called original with the input values
    • Print "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Create a new packet called transferred by moving from original using std::move()
    • Print "After move:"
    • Print "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Print "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

The input format will be:

  • First line: packet ID (string)
  • Second line: size (integer)

After the move, the original packet should have size 0 and sum 0 (since its data was transferred), while the transferred packet should have all the original data. This demonstrates how move semantics avoid expensive deep copies by simply transferring pointer ownership.

Include your header file in main.cpp using #include "DataPacket.h" and don't forget to include <utility> for std::move().

Cheat sheet

A move constructor transfers resources from a temporary object instead of copying them, avoiding expensive deep copies. It takes an rvalue reference (&&) and "steals" resources, leaving the source object in a valid but empty state.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Move constructor
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Steal the pointer
        size = other.size;
        other.data = nullptr;    // Leave source in valid state
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

Move constructors are called when initializing from temporary objects or when using std::move():

Buffer createBuffer() {
    return Buffer(1000);    // Move constructor used (return value)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Explicit move - b1 is now empty

Mark move constructors as noexcept when possible to enable optimizations in standard containers.

Try it yourself

#include <iostream>
#include <string>
#include <utility>
#include "DataPacket.h"

using namespace std;

int main() {
    // Read input
    string packetId;
    int size;
    cin >> packetId;
    cin >> size;

    // TODO: Create a DataPacket called 'original' with the input values

    // TODO: Print "Original - ID: <id>, Size: <size>, Sum: <sum>"

    // TODO: Create a new packet called 'transferred' by moving from original
    // Hint: Use std::move()

    // TODO: Print "After move:"

    // TODO: Print "Original - ID: <id>, Size: <size>, Sum: <sum>"

    // TODO: Print "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming