A move constructor is a special constructor in C++ that lets us transfer the contents of one object to another without copying the data. It is useful for performance - it's faster than copying.
Syntax of Move Constructor in C++
C++
ClassName(ClassName&& other);
- className&& is an rvalue reference to another object of the same class.
- The double ampersand && is key: it allows the function to bind to temporary (rvalue) objects.
- The other object is the source from the resource will be moved, not copied.
Example:
C++
#include <iostream>
using namespace std;
class Geeks {
private:
int* ptr;
public:
// Constructor
Geeks(int value) {
// Dynamically allocate memory
ptr = new int(value);
cout << "Constructor called\n";
}
// Move Constructor
Geeks(Geeks&& obj) {
cout << "Move Constructor called\n";
// Steal the pointer
ptr = obj.ptr;
obj.ptr = nullptr;
}
// Destructor
~Geeks() {
if (ptr != nullptr) {
cout << "Destructor deleting data: " << *ptr << endl;
} else {
cout << "Destructor called on nullptr\n";
}
delete ptr;
}
// Display function
void display() {
if (ptr)
cout << "Value: " << *ptr << endl;
else
cout << "No data\n";
}
};
int main() {
// Constructor is called
Geeks obj1(42);
// Move constructor is called
Geeks obj2 = std::move(obj1);
cout << "\nAfter move:\n";
cout << "obj1: ";
// Should show "No data"
obj1.display();
cout << "obj2: ";
// Should show "Value: 42"
obj2.display();
return 0;
}
OutputConstructor called
Move Constructor called
After move:
obj1: No data
obj2: Value: 42
Destructor deleting data: 42
Destructor called on nullptr
Why Move Constructors are Used?
Move constructors are used to transfer resources (like memory or file handles) from one object to another without making a copy, which makes the program faster and more efficient. They're especially useful when working with temporary objects or large data.
Example: Program with Only Copy Constructor and No Move Constructor
C++
// C++ program without declaring the
// move constructor
#include <iostream>
#include <vector>
using namespace std;
// Move Class
class Move {
private:
// Declaring the raw pointer as
// the data member of the class
int* data;
public:
// Constructor
Move(int d)
{
// Declare object in the heap
data = new int;
*data = d;
cout << "Constructor is called for " << d << endl;
};
// Copy Constructor to delegated
// Copy constructor
Move(const Move& source)
: Move{ *source.data }
{
// Copying constructor copying
// the data by making deep copy
cout << "Copy Constructor is called - "
<< "Deep copy for " << *source.data << endl;
}
// Destructor
~Move()
{
if (data != nullptr)
// If the pointer is not
// pointing to nullptr
cout << "Destructor is called for " << *data
<< endl;
else
// If the pointer is
// pointing to nullptr
cout << "Destructor is called"
<< " for nullptr" << endl;
// Free the memory assigned to
// data member of the object
delete data;
}
};
// Driver Code
int main()
{
// Create vector of Move Class
vector<Move> vec;
// Inserting object of Move class
vec.push_back(Move{ 10 });
vec.push_back(Move{ 20 });
return 0;
}
OutputConstructor is called for 10
Constructor is called for 10
Copy Constructor is called - Deep copy for 10
Destructor is called for 10
Constructor is called for 20
Constructor is called for 20
Copy Constructor is called - Deep copy for 20
Constructor is called for 10
Copy Constructor is called - Deep copy for 10
Destructor is called for 10
Destructor is called for 20
Destructor is called for 10
Destructor is called for 20
Example: Program with Move Constructor
C++
// C++ program for demonstrating the use of
// move constructor
#include <iostream>
#include <vector>
using namespace std;
// Move Class
class Move {
private:
// Declare the raw pointer as
// the data member of class
int* data;
public:
// Constructor
Move(int d)
{
// Declare object in the heap
data = new int;
*data = d;
cout << "Constructor is called for " << d << endl;
};
// Copy Constructor
Move(const Move& source)
: Move{ *source.data }
{
// Copying the data by making
// deep copy
cout << "Copy Constructor is called -"
<< "Deep copy for " << *source.data << endl;
}
// Move Constructor
Move(Move&& source)
: data{ source.data }
{
cout << "Move Constructor for " << *source.data
<< endl;
source.data = nullptr;
}
// Destructor
~Move()
{
if (data != nullptr)
// If pointer is not pointing
// to nullptr
cout << "Destructor is called for " << *data
<< endl;
else
// If pointer is pointing
// to nullptr
cout << "Destructor is called"
<< " for nullptr " << endl;
// Free up the memory assigned to
// The data member of the object
delete data;
}
};
// Driver Code
int main()
{
// Vector of Move Class
vector<Move> vec;
// Inserting Object of Move Class
vec.push_back(Move{ 10 });
vec.push_back(Move{ 20 });
return 0;
}
OutputConstructor is called for 10
Move Constructor for 10
Destructor is called for nullptr
Constructor is called for 20
Move Constructor for 20
Constructor is called for 10
Copy Constructor is called -Deep copy for 10
Destructor is called for 10
Destructor is called for nullptr
Destructor is called for 10
Destructor is called for 20
Noexcept Move Constructor
A noexcept move constructor is a move constructor that guarantees it won't throw any exceptions.
Example 1: Without noexcept Move Constructor
C++
// C++ program to illustrate what happens when we
// don't use the noexcept move constructor
#include <bits/stdc++.h>
using namespace std;
class A {
public:
A() {}
// Move constructor not marked as noexcept
A(A&& other) {
cout << "Move constructor" << endl;
}
// Copy constructor
A(const A& other) {
cout << "Copy constructor" << endl;
}
};
int main() {
vector<A> v;
// Reserve space for at least two elements to
// avoid immediate resizing
v.reserve(2);
// Uses the move constructor for the temporary objects
v.push_back(A());
v.push_back(A());
cout << "Resize happens" << endl;
// Move constructor may be called again if resizing occurs
v.push_back(A());
return 0;
}
OutputMove constructor
Move constructor
Resize happens
Move constructor
Copy constructor
Copy constructor
Example 2: With noexcept Move Constructor
C++
// C++ Program to illustrate the use of
// noexcept move constructor
#include <vector>
#include <iostream>
using namespace std;
class Test {
public:
Test(){}
// Move constructor marked as noexcept
Test(Test&& other) noexcept {
cout << "Move constructor " << endl;
}
// Copy constructor
Test(const Test& other) {
cout << "Copy constructor " << endl;
}
};
int main() {
vector<Test> vec;
vec.reserve(2); // Reserve space for at least two elements
Test a;
vec.push_back(Test());
vec.push_back(Test()); // Uses the move constructor
cout << "Resize happens" << endl;
vec.push_back(Test());
return 0;
}
OutputMove constructor
Move constructor
Resize happens
Move constructor
Move constructor
Move constructor
Why use noexcept?
- It helps the compiler optimize our code, especially with STL containers like std::vector.
- If a move constructor is not marked noexcept, some STL operations will fall back to slower copy operations to avoid potential exceptions.
Explore
C++ Basics
Core Concepts
OOP in C++
Standard Template Library(STL)
Practice & Problems