C++11 Move Contsructor & rvalue References

In this article we will discuss the use of rvalue references in C++11 move semantics.

Problem of Temporary Objects

The Idea behind this move semantics is to reduce the load of these temporary objects on memory. Every time we return a object from a function then a temporary object is created, which eventually get copied. In then end we create 2 copies of an object whereas, we need only one. Let’s understand by an example,

Suppose we have a Container class that contains a integer pointer as member variable,

class Container {
	int * m_Data;
public:
	Container() {
		//Allocate an array of 20 int on heap
		m_Data = new int[20];

		std::cout << "Constructor: Allocation 20 int" << std::endl;
	}
	~Container() {
		if (m_Data) {
			delete[] m_Data;
			m_Data = NULL;
		}
	}
	Container(const Container & obj) {
		//Allocate an array of 20 int on heap
		m_Data = new int[20];

		//Copy the data from passed object
		for (int i = 0; i < 20; i++)
			m_Data[i] = obj.m_Data[i];

		std::cout << "Copy Constructor: Allocation 20 int" << std::endl;
	}

};

When we create an object of Container class, then its default constructor internally allocates an array of 20 int on heap and assign it to its member variable.

Similarly, Container class’s copy constructor allocates an array of 20 int on heap, then copy the contents of passed objects array into it and then assign it to its member variable.

Generally, we use Factory classes to create object of our classes. On similar lines, let’s create a simple function that creates an object of Class Container and returns i.e.

// Create am object of Container and return
Container getContainer() 
{
	Container obj;
	return obj;
}

Now in main function we created a vector of Container type and inserted an object returned by getContainer() function i.e.

int main() {
	// Create a vector of Container Type
	std::vector<Container> vecOfContainers;

	//Add object returned by function into the vector
	vecOfContainers.push_back(getContainer());

	return 0;
}

Now, at last there is one object in the vector  vecOfContainers. But we actually created 2 objects for it because getContainer() function returned a temporary object which got copied into a new object and then destructed. This 2nd object got inserted in vector. So, 2 objects of class Container are created at following step in above code,

  • One inside the getContainer() function using Container class’s default constructor
  • Second while adding object in vector using Container class’ copy constructor.

To create each of these 2 objects it allocated an array of 20 int on heap 2 times and at last only one was used. So, its clearly a wastage of resources and effort.

How to solve this problem of resource and effort wastage due to temporary objects? Is there a way to move the 1st object instead of creating 2nd one and copying contents to it?

Answer is yes. This is where move semantics and rvalue references comes into picture.

Solving Problem of Temporary Objects using rvalue references & Move Constructor

The getContainer() function here is a rvalue, so it can be referred by a rvalue reference. Also, using rvalue reference we can also overload functions. This time, we will overload the Constructor of class Container and this new Constructor will be called move constructor.

Move Constructor

Move constructor takes a rvalue reference as an argument and that makes it overloaded because Copy Constructor takes the const lvalue reference as an argument. In Move constructor we just move the member variables of passed object into the new object’s member variables, instead of allocating new memory for them.

Let’s see the move constructor for class Container i.e.

Container(Container && obj)
{
	// Just copy the pointer
	m_Data = obj.m_Data;

	// Set the passed object's member to NULL
	obj.m_Data = NULL;

	std::cout<<"Move Constructor"<<std::endl;
}

In the move constructor, we just copied the pointer. Now member variable m_Data points to the same memory on heap. Then we set the m_Data of passed object to NULL. So, we didn’t allocated any memory on heap in move constructor, we just shifted the control of memory.

Now if we create the vector  of class container and push a object returned from getContainer() into it. Then a new object will created from this temporary object but as getContainer() is a rvalue, so Move Constructor of this new Container class’s object will be called and in that memory will be just shifted. So, actually on heap we will create only one array of integers.

[showads ad=inside_post]

 

Similar to Move Constructor we can have Move Assignment operator that will just shift the content. Checkout the complete example as follows,

#include <iostream>
#include <vector>

class Container {
	int * m_Data;
public:
	Container() {
		//Allocate an array of 20 int on heap
		m_Data = new int[20];

		std::cout << "Constructor: Allocation 20 int" << std::endl;
	}
	~Container() {
		if (m_Data) {
			delete[] m_Data;
			m_Data = NULL;
		}
	}
	//Copy Constructor
	Container(const Container & obj) {
		//Allocate an array of 20 int on heap
		m_Data = new int[20];

		//Copy the data from passed object
		for (int i = 0; i < 20; i++)
			m_Data[i] = obj.m_Data[i];

		std::cout << "Copy Constructor: Allocation 20 int" << std::endl;
	}

	//Assignment Operator
	Container & operator=(const Container & obj) {

		if(this != &obj)
		{
			//Allocate an array of 20 int on heap
			m_Data = new int[20];

			//Copy the data from passed object
			for (int i = 0; i < 20; i++)
				m_Data[i] = obj.m_Data[i];

			std::cout << "Assigment Operator: Allocation 20 int" << std::endl;
		}
	}

	// Move Constructor
	Container(Container && obj)
	{
		// Just copy the pointer
		m_Data = obj.m_Data;

		// Set the passed object's member to NULL
		obj.m_Data = NULL;

		std::cout<<"Move Constructor"<<std::endl;
	}

	// Move Assignment Operator
	Container& operator=(Container && obj)
	{
		if(this != &obj)
		{
			// Just copy the pointer
			m_Data = obj.m_Data;

			// Set the passed object's member to NULL
			obj.m_Data = NULL;

			std::cout<<"Move Assignment Operator"<<std::endl;
		}
	}

};

// Create am object of Container and return
Container getContainer()
{
	Container obj;
	return obj;
}
int main() {
	// Create a vector of Container Type
	std::vector<Container> vecOfContainers;

	//Add object returned by function into the vector
	vecOfContainers.push_back(getContainer());

	Container obj;
	obj = getContainer();

	return 0;
}

Output:

Constructor: Allocation 20 int
Move Constructor
Constructor: Allocation 20 int
Constructor: Allocation 20 int
Move Assignment Operator

In the above example, Move constructor of class Container will be called because getContainer() returns a rvalue and Container class has a overloaded version of Constructor that accepts rvalue in rvalue reference. Inside this Move constructor memory is just shifted.

Similarly in following lines,

Container obj;
obj = getContainer(); // Move Assignment will be called

Move Assignment Operator was called instead of assignment operator and memory just got shifted.

5 thoughts on “C++11 Move Contsructor & rvalue References”

  1. Great tutorial! You jest forgot to return *this in overloaded operators
    Keep doing great work! Maybe C++17? 🙂

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top