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