Copy Constructor and Move Constructor: Test with vector

Share the Article

Copy constructor and move constructor are different. The Copy constructor creates a fresh copy of resources in new object whereas, the move constructor just transfers the ownership of resources to new object. Generally, copy constructor is a slower function because the creation of resource is an expensive operation as compared to transferring.

Problem with temporary objects

During the course of program execution, C++ compiler creates and destroys a lot of temporary objects. To construct these temporary objects, compiler invokes copy constructor. When the size of objects become large than such unnecessary copying of objects may cause slowdown of program.

For example,

When a vector do not have space for insertion of a new object, then compiler grows the vector by reallocating a larger chunk of memory. After this, all the existing objects are copied to new memory chunk. To perform such copy, the compiler creates temporary objects using copy constructor of each object. The compiler deletes these temporaries after doing copy.

Following program demonstrates this unnecessary copy in vectors.
#include <iostream> //main header #include <vector> //for stl vector using namespace std; //namespace class MainFunda { std::string name; public: MainFunda(std::string s) : name(s) { cout << "Default Constructor: " << name << endl; } MainFunda(const MainFunda& rhs) : name(rhs.name) { cout << "Copy Constructor: From " << name << endl; } ~MainFunda() { cout << "Destructor : " << name << endl; } }; int main() { std::vector va; cout << "Starting Address of Vector=" << &va[0] << endl; va.push_back(MainFunda("one")); cout << endl; cout << "Starting Address of Vector=" << &va[0] << endl; va.push_back(MainFunda("two")); cout << "Starting Address of Vector=" << &va[0] << endl; return 0; }

The output is:

Starting Address of Vector = 0 Default Constructor: one Copy Constructor: From one Destructor : one Starting Address of Vector = 0x19a4c20 Default Constructor: two Copy Constructor: From two Copy Constructor: From one Destructor : one Destructor : two Starting Address of Vector = 0x19a4c50 Destructor : one Destructor : two

Explanation:

  1. The program first creates a temporary object “one” using default constructor.
  2. After this, the temporary object “one” initializes a second object and then compiler pushes this new object on the vector. Compiler invokes copy constructor to make this happen.
  3. The temporary object “one” calls destructor.
  4. The program then creates a second temporary object “two” again using default constructor
  5. Now, again compiler invokes copy constructor to create a new object from temporary and stores this on vector. However, the vector do not have enough storage to store second object. (See text in RED color above). Therefore, compiler generates code to copy vector in a new chunk of memory. To achieve this result, the compiler invokes copy constructor second time to create one more temporary object and copies existing vector element to new location.
  6. Compiler invokes destructor, to delete both the temporary objects (in steps 4 & 5)
  7. In the end, 2 objects are remaining and they are existing on the vector. When main function returns, then destructor deletes both of them.

How move constructor optimizes this

When class contains a move constructor, then unnecessary copying will happen in a different way. Now during vector memory reallocates, the compiler will call move constructor to use temporaries. Compiler know that move operation is better for copying temporary objects.

Following is the move constructor which is added in above program.
MainFunda(MainFunda&& rhs) noexcept : name(std::move(rhs.name)) { cout << "Move Constructor : From " << name << endl; }

The output now is:

Starting Address of Vector = 0 Default Constructor: one Move Constructor : From one Destructor : Starting Address of Vector = 0x1dc7c20 Default Constructor: two Move Constructor : From two Move Constructor : From one Destructor : Destructor : Starting Address of Vector = 0x1dc7c50 Destructor : one Destructor : two

Explanation:

Every thing is same. Except for all those places where initially compiler was calling copy operation, the program gets move operation.

When move happens on this object, then all the resources are transferred.

Please Note:

In the output, some of the destructors no longer show any names. This is because, during the move operation, the compiler has transferred ownership of “std::string” resource from these temporary objects. This transfer happened in initializer list.

MainFunda(MainFunda&& rhs) noexcept : name(std::move(rhs.name))

Main Funda: Compiler in first preference calls move operations for copying temporary objects. If move is not available then copy is called.

Related Topics:

 What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism
emplace_back vs push_back

Share the Article

Leave a Reply

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