Move semantic is not a copy of something. Actually, you steal their resources. For example, you have created an object, let's say A, that is going to initialized with an object, let's say B. How could happen this case? You can either copy their values or steal their resources. If you do not use B object anymore, I mean its life is done, you can steel its resource. It is move semantic. But if you going to use B object you can copy its values. It is a copy semantic.
#include <iostream>
#include <cstdlib>
class Data {
private:
int* mptr;
size_t mlen;
public:
Data(const int* p, size_t len) : mptr{ static_cast<int*>(std::malloc(sizeof(int) * len)) }, mlen(len) // constructor
{
for (size_t i = 0; i < len; ++i)
{
mptr[i] = p[i];
}
}
~Data() // deconstructor
{
std::free(mptr);
}
Data(const Data& other) : mptr{ static_cast<int*>(std::malloc(sizeof(int) * other.mlen)) }, mlen(other.mlen) // copy constructor
{
for (size_t i = 0; i < other.mlen; ++i)
{
mptr[i] = other.mptr[i];
}
}
Data(Data&& other) : mptr{ other.mptr }, mlen(other.mlen) // move constructor
{
other.mptr = nullptr;
}
Data& operator=(const Data& other) // copy assignment
{
if (this != &other)
{
std::free(mptr);
mptr = static_cast<int*>(std::malloc(sizeof(int) * other.mlen));
for (int i = 0; i < other.mlen; ++i)
{
mptr[i] = other.mptr[i];
}
mlen = other.mlen;
}
return *this;
}
Data& operator=(Data&& other) // move assignment
{
if (this != &other)
{
std::free(mptr);
mptr = other.mptr;
mlen = other.mlen;
other.mptr = nullptr;
}
return *this;
}
void print()const
{
for (size_t i = 0; i < mlen; ++i)
{
std::cout << mptr[i] << " ";
}
std::cout << std::endl;
}
};
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[4] = { 10,11,12,13 };
Data d1{ &arr1[0],5 }; // constructor called
d1.print(); // 1 2 3 4 5
Data d2 = d1; // copy constructor called
d2.print(); // 1 2 3 4 5
Data d3{ Data{&arr2[0],4} }; // move ctor called, temporary object
d3.print(); // 10 11 12 13
d3 = std::move(d2); // move assignment called
d3.print(); // 1 2 3 4 5
//d2.print(); // error because d2.mptr = nullptr
d2 = d1; // copy assignment called
d1.print(); // 1 2 3 4 5
d2.print(); // 1 2 3 4 5
}
In the above code, I have implemented constructor, destructor, copy constructor, copy assignment, move constructor, and move assignment. As I mentioned earlier if I call move semantic. It means I will not use the object anymore.
d1 created via the constructor. d2 created with a copy constructor. It means d2 has the same value as d1 but different memory addresses. When I delete d1, it deletes only its own resources.
But d3 created via move constructor. Because using Data{&arr2[0],4}
this code, compiler creates a temporary object then d3 steal its resources. That temporary object will die end of the line. So that there is no reason to use copy semantic. It will use unnecessary resources.
After that, d3 steals d2 resources via move assignment. After this code, d3 = std::move(d2)
, execution d2 has no resource. It means if you call the print function it will throw an error like d2.print()
. Then d2 again take a resource via copy assignment. mptr of d1 and d2 are completely different.
Why we have assign mptr as a nullptr? Because if you do not assign it when the destructor called it will be freed. So that it will create an error.
Also, 10 is an initial value.