Back to TILs

C++ move_semantics

Date: 2022-12-30Last modified: 2023-03-16

Table of contents

  string s1{"Content of s1 variable"};
  string s2{"Content of s2 variable"};
  string s3{"Content of s3 variable"};

  auto prints = [&](const string &title) {
    cout << '\n' << title << '\n';
    cout << "s1: " << s1 << '\n';
    cout << "s2: " << s2 << '\n';
    cout << "s3: " << s3 << endl;
  };

  prints("Initial state");
  s1 = s2;
  prints("s1 = s2");

  s1 = std::move(s3);
  prints("s1 = std::move(s3)");

  // equivalent to move assigment
  s1 = static_cast<string &&>(s2);
  prints("s1 = static_cast<string&&>(s2)");
  class Data {
   private:
    // a raw pointer to my data
    int *data;
    string trackId;

   public:
    Data(int d, string tId) {
      data = new int;  // heap allocation
      *data = d;
      trackId = tId;
      fmt::print("Data Constructor {} {}\n", *data, trackId);
    }

    ~Data() {
      if (data != nullptr) {
        fmt::print("Data Destructor {} {}\n", *data, trackId);
        delete data;
      } else {
        fmt::print("Data Destructor nullptr");
      }
    }

    // unnecessary copy and inefficiently memory management
    Data(const Data &source) : Data{*source.data, "copy-constructor"} {
      fmt::print("Data Copy Constructor {} {}\n", *data, trackId);
    }
  };
  {
    puts("================");
    vector<Data> myData;
    puts("myData.push_back(Data{10,\"push-back\"});");
    myData.push_back(Data{10, "push-back"});

    puts("myData.push_back(Data{20,\"push-back\"});");
    myData.push_back(Data{20, "push-back"});

    puts("Out of scope");
  }
  class DataWithMove {
   private:
    // a raw pointer to my data
    int *data;
    string trackId;

   public:
    DataWithMove(int d, string tId) {
      data = new int;  // heap allocation
      *data = d;
      trackId = tId;
      fmt::print("DataWithMove Constructor {} {}\n", *data, trackId);
    }

    ~DataWithMove() {
      if (data != nullptr) {
        fmt::print("DataWithMove Destructor {} {}\n", *data, trackId);
        delete data;
      } else {
        fmt::print("DataWithMove Destructor nullptr\n");
      }
    }

    DataWithMove(const DataWithMove &source)
        : DataWithMove{*source.data, "copy-constructor"} {
      fmt::print("DataWithMove Copy Constructor {} {}\n", *data, trackId);
    }

    DataWithMove(DataWithMove &&source)
        : data{source.data}, trackId{"move-constructor"} {
      source.data = nullptr;
      fmt::print("DataWithMove Move constructor {}\n", *data);
    }
  };

Possible output


Initial state
s1: Content of s1 variable
s2: Content of s2 variable
s3: Content of s3 variable

s1 = s2
s1: Content of s2 variable
s2: Content of s2 variable
s3: Content of s3 variable

s1 = std::move(s3)
s1: Content of s3 variable
s2: Content of s2 variable
s3: 

s1 = static_cast<string&&>(s2)
s1: Content of s2 variable
s2: 
s3: 
================
myData.push_back(Data{10,"push-back"});
Data Constructor 10 push-back
Data Constructor 10 copy-constructor
Data Copy Constructor 10 copy-constructor
Data Destructor 10 push-back
myData.push_back(Data{20,"push-back"});
Data Constructor 20 push-back
Data Constructor 20 copy-constructor
Data Copy Constructor 20 copy-constructor
Data Constructor 10 copy-constructor
Data Copy Constructor 10 copy-constructor
Data Destructor 10 copy-constructor
Data Destructor 20 push-back
Out of scope
Data Destructor 10 copy-constructor
Data Destructor 20 copy-constructor
================
myData.push_back(DataWithMove{10,"push-back"});
DataWithMove Constructor 10 push-back
DataWithMove Move constructor 10
DataWithMove Destructor nullptr
myData.push_back(DataWithMove{20,"push-back"});
DataWithMove Constructor 20 push-back
DataWithMove Move constructor 20
DataWithMove Constructor 10 copy-constructor
DataWithMove Copy Constructor 10 copy-constructor
DataWithMove Destructor 10 move-constructor
DataWithMove Destructor nullptr
Out of scope
DataWithMove Destructor 10 copy-constructor
DataWithMove Destructor 20 move-constructor

References