Just like a move constructor, the move assignment operator also makes a class movable. A movable class enables efficient resource transfer from one object to another.
Let’s start with an normal overloaded assignment operator.
#include <iostream>
#include <cstring>
using namespace std;
class mystring {
public:
mystring(const char * s) {
if (s == nullptr) {
s = nullptr;
return;
}
str = new char[strlen(s)];
strcpy(str, s);
}
/*Overloaded Assignment Operator*/
void operator= (const mystring &rhs) {
cout << "Overloaded assignment operator is called." << endl;
if (str != nullptr) delete str;
if (rhs.str == nullptr) {
str = nullptr;
return;
}
str = new char[strlen(rhs.str)];
strcpy(str, rhs.str);
}
void change(const char * s) {
if (s == nullptr || str == nullptr) return; /*Better handling possible. Not done for simplicity.*/
strncpy(str, s, strlen(str));
}
friend ostream& operator<<(ostream& os, const mystring& s) {
if (s.str == nullptr) return os;
os << s.str;
return os;
}
private:
char *str;
};
int main() {
mystring s1("QnA Plus");
mystring s2("");
s2 = s1;
cout << "First string: " << s1 << endl;
cout << "Second string: " << s2 << endl;
return 0;
}
$ g++ -o test test.cpp
$ ./test
Overloaded assignment operator is called.
First string: QnA Plus
Second string: QnA Plus
In this example, we overloaded the assignment operator in the mystring class. This overloaded function was called the object s2 was assigned from s1 – s2 = s1.
From the output, we can see that the value from the source object, s1, was copied to the destination object, s2. Memory was also allocated for s2 to hold the value. That means changing one object will not impact another.
So, this overloaded assignment operator did a perfect job.
But in some situations, we don’t care whether the source object retains its value or not. And we want to transfer the resources from source to destination as efficiently as possible.
A typical ‘swap to strings‘ logic looks like this.
// a and b are of type mystring
mystring tmp("");
tmp = a;
a = b;
b = tmp;
After the first assignment operation, tmp = a, we don’t care whether ‘a‘ retains its value or not because in the next statement, it got overwritten anyway. So, we want to transfer the resources from ‘a‘ to ‘tmp‘ without doing expensive memory allocation and copy.
Changing the current overloaded assignment operator it not a good idea also because in most situations we want both the objects retain their resources after assignment.
Move Assignment Operator
Move assignment operator is the solution. Technically move assignment operator is another overload of the assignment operator that takes a rvalue reference as input.
#include <iostream>
#include <cstring>
using namespace std;
class mystring {
public:
mystring(const char * s) {
if (s == nullptr) {
s = nullptr;
return;
}
str = new char[strlen(s)];
strcpy(str, s);
}
/*Overloaded Assignment Operator*/
void operator= (const mystring &rhs) {
cout << "Overloaded assignment operator is called." << endl;
if (str != nullptr) delete str;
if (rhs.str == nullptr) {
str = nullptr;
return;
}
str = new char[strlen(rhs.str)];
strcpy(str, rhs.str);
}
/*Move Assignment Operator*/
void operator= (mystring&& rhs) {
cout << "Move assignment operator is called." << endl;
if (str != nullptr) delete str;
str = rhs.str;
rhs.str = nullptr;
}
void change(const char * s) {
if (s == nullptr || str == nullptr) return; /*Better handling possible. Not done for simplicity.*/
strncpy(str, s, strlen(str));
}
friend ostream& operator<<(ostream& os, const mystring& s) {
if (s.str == nullptr) return os;
os << s.str;
return os;
}
private:
char *str;
};
int main() {
mystring s1("QnA Plus");
mystring s2("");
s2 = static_cast<mystring&&>(s1);
cout << "First string: " << s1 << endl;
cout << "Second string: " << s2 << endl;
return 0;
}
$ ./test
Move assignment operator is called.
First string:
Second string: QnA Plus
We added the move assignment operator to the class mystring. Notice that this version of the overloaded function takes a rvalue reference (&&) as input. To call it, we cast-ed the source object to a rvalue reference. The s2 = s1 statement was changed to s2 = static_cast<mystring&&>(s1); .
The move assignment operator does not have any memory allocation or copy. So, transferring resource from source object to destination would be very efficient compare to other overloaded assignment operator.
In this function, we set the ‘str‘ member of the source object to nullptr to avoid accidental access and modification.
Please note that the stastic_cast, that we used here, might not work in all conditions, especially, if you work with template type references. It’s better to use C++ standard library provided std::move for this purpose.
So the s2 = static_cast<mystring&&>(s1); line should look like s2 = move(s1); .
To make a class movable, we should have both move constructor and move assignment operator.