The Move Constructor in C++

Jinku Hu Oct 12, 2023
The Move Constructor in C++

This article will introduce how to use the move constructor in C++.

Use the Move Constructor to Provide Efficient Copy Control for Classes in C++

Copy control of the class defines the core functions needed to specify what happens when the class object gets copied, moved, assigned, or destroyed. These functions have special C++ nomenclature, like copy-constructor and move-constructor functions define how an object is initialized with another object of the same type. copy-assignment and move-assignment functions define how the object gets assigned to the same type of object. Destructor handles the routine that executes when the object goes out of scope. Some of these functions are most likely to be defined by the user, but if not, the compiler itself creates the default prototypes.

When the class object manages the dynamic memory and the data is quite large, the copying operations can be rather computing-intensive. They can use up significant resources affecting the performance. Thus, they often utilize a move constructor to reassign dynamic data without copying it to the new memory location. It is achieved by assigning the pointers of the old object to the corresponding members of the newly initialized or assigned object. Notice that the following example does not include the move constructor, and it causes multiple invocations of the copy constructor, which delegates to the default constructor.

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

class MyClass {
 private:
  int* data;

 public:
  explicit MyClass(int d) {
    data = new int;
    *data = d;
    cout << "Constructor 1 is called" << endl;
  };

  MyClass(const MyClass& source) : MyClass(*source.data) {
    cout << "Copy Constructor is called " << endl;
  }

  int getData() const { return *data; }

  ~MyClass() {
    delete data;
    cout << "Destructor is called" << endl;
  }
};

void printVec(const vector<MyClass>& vec) {
  for (const auto& i : vec) {
    cout << i.getData() << " ";
  }
  cout << endl;
}

int main() {
  vector<MyClass> vec;

  vec.push_back(MyClass(10));
  vec.push_back(MyClass(11));
  printVec(vec);
  cout << "------------------" << endl;

  return EXIT_SUCCESS;
}

Output:

Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called

Once we define a move constructor, which generally should take r-value reference as the first argument (denoted with && notation), the vector initialization becomes more efficient as the new elements of type MyClass are added. Since the move constructor does not allocate new memory and takes over the location held by the passed object, one needs to assign nullptr to the previous object’s members. Otherwise, the destructor will try to free the same memory location twice, throwing the run-time error.

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

class MyClass {
 private:
  int* data;

 public:
  explicit MyClass(int d) {
    data = new int;
    *data = d;
    cout << "Constructor 1 is called" << endl;
  };

  MyClass(const MyClass& source) : MyClass(*source.data) {
    cout << "Copy Constructor is called " << endl;
  }

  MyClass(MyClass&& source) noexcept : data(source.data) {
    source.data = nullptr;
    cout << "Move Constructor is called" << endl;
  }

  int getData() const { return *data; }

  ~MyClass() {
    delete data;
    cout << "Destructor is called" << endl;
  }
};

void printVec(const vector<MyClass>& vec) {
  for (const auto& i : vec) {
    cout << i.getData() << " ";
  }
  cout << endl;
}

int main() {
  vector<MyClass> vec;

  vec.push_back(MyClass(10));
  vec.push_back(MyClass(11));
  printVec(vec);
  cout << "------------------" << endl;

  return EXIT_SUCCESS;
}

Output:

Constructor 1 is called
Move Constructor is called
Destructor is called
Constructor 1 is called
Move Constructor is called
Move Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C++ Class