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:
- The program first creates a temporary object “one” using default constructor.
- 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.
- The temporary object “one” calls destructor.
- The program then creates a second temporary object “two” again using default constructor
- 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.
- Compiler invokes destructor, to delete both the temporary objects (in steps 4 & 5)
- 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))