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 emptyMark 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
EasyLet'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 aDataPacketclass that manages a payload of integer data. Your class should have:- Private members: a pointer to an integer array (
payload), asizefor the number of elements, and apacketId(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
noexceptthat 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)
- Private members: a pointer to an integer array (
main.cpp: Demonstrate move construction by reading a packet ID and size from input. Then:- Create a
DataPacketcalledoriginalwith the input values - Print
"Original - ID: <id>, Size: <size>, Sum: <sum>" - Create a new packet called
transferredby moving fromoriginalusingstd::move() - Print
"After move:" - Print
"Original - ID: <id>, Size: <size>, Sum: <sum>" - Print
"Transferred - ID: <id>, Size: <size>, Sum: <sum>"
- Create a
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 emptyMark 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;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container