Move semantics (C++)

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.)