Move semantics
The C++ programming language implicitly warrants a Copy Constructor, Assignment Operator, and a Destructor (some times virtual) with every class designed by us. This is meant to do resource management while cloning an object or while assigning to an existing object. Sometimes it is very expensive to copy an object and the movement of ownership (through pointers) helps in writing fast code. Modern C++ has got a facility to provide a Move Constructor and a Move assignment operator to help developers avoid copying large objects, during the creation of a new object or assignment to a new object. Rvalue references can act as a hint to the compiler that, when temporary objects are involved, a move version of a constructor or a move version of assignment is better suited for the context:
//----- FloatBuffer.cpp
#include <iostream>
#include <vector>
using namespace std;
class FloatBuffer {
double *bfr; int count;
public:
FloatBuffer():bfr(nullptr),count(0){}
FloatBuffer(int pcount):bfr(new double[pcount]),count(pcount){}
// Copy constructor.
FloatBuffer(const FloatBuffer& other) : count(other.count)
, bfr(new double[other.count])
{ std::copy(other.bfr, other.bfr + count, bfr); }
// Copy assignment operator - source code is obvious
FloatBuffer& operator=(const FloatBuffer& other) {
if (this != &other) {
if ( bfr != nullptr)
delete[] bfr; // free memory of the current object
count = other.count;
bfr = new double[count]; //re-allocate
std::copy(other.bfr, other.bfr + count, bfr);
}
return *this;
}
// Move constructor to enable move semantics
// The Modern STL containers supports move sementcis
FloatBuffer(FloatBuffer&& other) : bfr(nullptr) , count(0) {
cout << "in move constructor" << endl;
// since it is a move constructor, we are not copying elements from
// the source object. We just assign the pointers to steal memory
bfr = other.bfr;
count = other.count;
// Now that we have grabbed our memory, we just assign null to
// source pointer
other.bfr = nullptr;
other.count = 0;
}
// Move assignment operator.
FloatBuffer& operator=(FloatBuffer&& other) {
if (this != &other)
{
// Free the existing resource.
delete[] bfr;
// Copy the data pointer and its length from the
// source object.
bfr = other.bfr;
count = other.count;
// We have stolen the memory, now set the pinter to null
other.bfr = nullptr;
other.count = 0;
}
return *this;
}
};
int main() {
// Create a vector object and add a few elements to it.
// Since STL supports move semantics move methods will be called.
// in this particular case (Modern Compilers are smart)
vector<FloatBuffer> v;
v.push_back(FloatBuffer(25));
v.push_back(FloatBuffer(75));
}
The std::move function can be used to indicate (while passing parameters) that the candidate object is movable and the compiler will invoke appropriate methods (move assignment or move constructor ) to optimize the cost associated with memory management. Basically, std::move is a static_cast to an rvalue reference.