Since C++11 we can now use move semantics (move constructor, move assignment operator). They provide a perfomance boost over copy semantics, but only work on rvalue references.
#include <utility>
#include <cstring>
#include <memory>
#include <mutex>
#include <iostream>
class Matrix
{
public:
// Constructor
Matrix(unsigned rows, unsigned cols) : m_Mtx(), m_Rows(rows), m_Cols(cols)
{
std::cout << "Matrix constructor" << std::endl;
createMatrix();
}
// Copy-Constructor
Matrix(const Matrix& other) : m_Mtx()
{
std::cout << "Matrix copy-constructor" << std::endl;
{
std::lock_guard<std::mutex> lock(other.m_Mtx);
m_Rows = other.m_Rows;
m_Cols = other.m_Cols;
createMatrix();
for (int i = 0; i < m_Rows; i++)
{
std::memcpy(m_Mat[i], other.m_Mat[i], m_Cols * sizeof(m_Cols));
}
}
}
// Move-Constructor
Matrix(Matrix&& other) : m_Mtx()
{
std::cout << "Matrix move-constructor" << std::endl;
{
std::lock_guard<std::mutex> lock(other.m_Mtx);
m_Rows = other.m_Rows;
m_Cols = other.m_Cols;
m_Mat = other.m_Mat;
other.m_Rows = 0;
other.m_Cols = 0;
other.m_Mat = nullptr;
}
}
// Destructor
~Matrix()
{
std::cout << "Matrix destructor" << std::endl;
removeMatrix();
m_Rows = 0;
m_Cols = 0;
m_Mat = nullptr;
}
private:
void createMatrix()
{
m_Mat = new unsigned*[m_Rows];
for (unsigned i = 0; i < m_Rows; ++i)
{
m_Mat[i] = new unsigned[m_Cols];
}
}
void removeMatrix()
{
for(unsigned i = 0; i < m_Rows; ++i)
{
delete[] m_Mat[i];
}
delete[] m_Mat;
}
mutable std::mutex m_Mtx;
unsigned m_Rows;
unsigned m_Cols;
unsigned** m_Mat;
};
int main()
{
std::cout << "== Step 0: Create baseMatrix (on stack) ==" << std::endl;
Matrix baseMatrix(100000, 10000);
std::cout << "== Step 1: Copy baseMatrix to destMatrix1 ==" << std::endl;
Matrix destMatrix1(baseMatrix); // Very slow
std::cout << "== Step 2: Move baseMatrix to destMatrix2 ==" << std::endl;
Matrix destMatrix2(std::move(baseMatrix)); // Very fast
// std::move() makes baseMatrix an rValue reference,
// which is empty after the move-c'tor
std::cout << "== Step 3: Cleanup in stack order ==" << std::endl;
return 0;
}
== Step 0: Create baseMatrix (on stack) == Matrix constructor == Step 1: Copy baseMatrix to destMatrix1 == Matrix copy-constructor == Step 2: Move baseMatrix to destMatrix2 == Matrix move-constructor == Step 3: Cleanup in stack order == Matrix destructor Matrix destructor Matrix destructor
This example just contains the move constructor, but normally you want to implement a move assignment operator as well.
Alternative: Let the compiler implicitly create a move constructor / move assignment operator for you. Be aware of the requirements and restrictions.
Don’t use std::move when (Named) Return Value Optimization comes in place, cause it’s even faster. (Just constructor instead of constructor + move operation.)