Consider the below example of swapping values of a and b. We note that redundant copies of variables a and b are created just for swapping the values.
A copying operation could be expensive as it involves calling a function, allocating a memory and running a loop; all of which could be avoided using std :: move.
template <class T>
void Swap (T& a, T& b) {
T tmp(a); // We have two copies of a (tmp and a)
a = b; // now we have two copies of b (a and b)
b = tmp; // now we have two copies of tmp (b and tmp)
}
std :: move |
---|
- std :: move ( T&& obj ) enforces move semantics to transfer the resources from one object to another object. The std :: move function accepts either an lvalue or rvalue argument, and returns a rvalue reference without invoking a copy constructor. obj = std :: move ( tmp_obj );Note : After the std :: move ( tmp_obj ) call, the tmp_obj loses its value. |
Swap operation using std::move eliminates creation of redundant copies.
template <class T>
void Swap (T& a, T& b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
String class : Move assignment operator |
---|
- Parameter : The move assignment operator for a class takes an rvalue reference ( && ) to the class type as its parameter. String& operator = (String&& obj) { |
- Check before assigning : To prevent an object getting assigned to itself, a conditional block is used. - Do the assignment - Release the source pointer to avoid multiple free operations by the destructor. if ( this != &obj ) { |
C++ program for a String class demonstrates the move assignment operator.
#include <iostream>
#include <vector>
#include <string>
int main () {
std :: string noddy = "noddy";
std :: string deltoid = "deltoid";
std :: vector < std :: string> str_vec;
std :: cout << "\nBefore push operation" << std :: endl;
std :: cout << "noddy : [" << noddy << "]" << std :: endl;
std :: cout << "deltoid : [" << deltoid << "]" << std :: endl;
std :: cout << "\nContent of vector " << std :: endl;
str_vec.push_back (noddy); // A copy operation
str_vec.push_back (std :: move (deltoid)); // A move operation
for (const auto& str : str_vec ) {
std :: cout << str << std :: endl;
}
std :: cout << "\nAfter push operation" << std :: endl;
std :: cout << "noddy : [" << noddy << "]" << std :: endl;
std :: cout << "deltoid : [" << deltoid << "]" << std :: endl;
return 0;
}
Output
Before push operation
noddy : [noddy]
deltoid : [deltoid]
Content of vector
noddy
deltoid
After push operation
noddy : [noddy]
deltoid : []
#include<iostream>
#include<cstring>
class String {
private:
u_int m_len;
char* m_buff;
public:
// Default constructor
String () {
std :: cout << "Default constructor of String class got called." << std :: endl;
m_len = 0;
}
m_buff = new char;
m_buff[0] = '\0';
// Parameterized constructor
String (const char * str) {
std :: cout << "Parameterized constructor of String class got called." << std :: endl;
m_len = strlen(str);
m_buff = new char[m_len + 1];
strcpy(m_buff, str);
}
String (String&& obj) {
m_len = obj.m_len;
m_buff = obj.m_buff;
obj.m_len = 0;
obj.m_buff = nullptr;
}
// Overloading = operator for String class
String& operator = (const String& obj) {
std :: cout << "Overloaded assignment operator for String class got called." << std :: endl;
if (this == &obj) { // If the source obj is same as the destn object, no need to copy.
return (*this);
} else {
m_len = obj.m_len;
delete [] m_buff; // Delete the old buffer of the destination object.
m_buff = new char[m_len+1];
strcpy(m_buff, obj.m_buff);
return (*this);
}
}
// Move assignment operator for String class
String& operator = (String&& obj) {
std :: cout << "Move assignment operator for String class got called." << std :: endl;
if (this != &obj) { // If the source obj is same as the destn object, no need to move.
// Copy the source length and the source pointer into the target.
m_len = obj.m_len;
m_buff = obj.m_buff;
// Release the pointer from the source object to avoid the double free operation
// by the destructor.
obj.m_len = 0;
obj.m_buff = nullptr;
}
return (*this);
}
~String() {
std :: cout << "Destructor String class got called." << std :: endl;
if (m_buff) {
std :: cout << "Deleting the buffer." << std :: endl;
delete [] m_buff;
}
}
void Display() {
std :: cout << m_buff << std :: endl;
}
};
int main() {
String deltoid("Deltoid");
String deltoid_right;
deltoid_right = std :: move(deltoid); // Call the move assignment operator.
// deltoid_right = deltoid; // Call the overloaded assignment operator.
std :: cout << "Main ends. Now returning." << std :: endl;
return 0;
}
Output
Parameterized constructor of String class got called.
Default constructor of String class got called.
Move assignment operator for String class got called.
Main ends. Now returning.
Destructor String class got called.
Deleting the buffer.
Destructor String class got called.