神刀安全网

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 ContainergetContainer()  {  Containerobj;  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 argumentand 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.

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 ContainergetContainer() {  Containerobj;  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());    Containerobj;  obj = getContainer();    return 0; } 

Output:

Constructor: Allocation 20 int MoveConstructor Constructor: Allocation 20 int Constructor: Allocation 20 int MoveAssignmentOperator 

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,

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

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

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » C++11 Move Contsructor & rvalue References

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址