I am trying to understand the implementation of move-constructor. We all know if we need to manage resources in a C++ class, we need to implement Rule of five (C++ programming).
Microsoft gives us an example: https://msdn.microsoft.com/en-us/library/dd293665.aspx
Here is better one, which uses copy-swap to avoid code duplication: Dynamically allocating an array of objects
// C++11
A(A&& src) noexcept
: mSize(0)
, mArray(NULL)
{
// Can we write src.swap(*this);
// or (*this).swap(src);
(*this) = std::move(src); // Implements in terms of assignment
}
In the move-constructor, directly:
// Can we write src.swap(*this);
// or (*this).swap(src);
Because I think (*this) = std::move(src)
is a little more complicated. Because if we write as (*this) = src
inadvertently, it would call normal assignment operator instead of the move-assignment-operator.
Apart from this question, in Microsoft's example, they wrote code like this: in move-assignment-operator, do we need to check self-assignment? Is it possible to happen?
// Move assignment operator.
MemoryBlock& operator=(MemoryBlock&& other)
{
std::cout << "In operator=(MemoryBlock&&). length = "
<< other._length << "." << std::endl;
if (this != &other)
{
// Free the existing resource.
delete[] _data;
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
return *this;
}
One way is to implement the default constructor, copy constructor and swap
function.
And then implement the move constructor, copy and move assignment operators using the first three.
E.g.:
struct X
{
X();
X(X const&);
void swap(X&) noexcept;
X(X&& b)
: X() // delegate to the default constructor
{
b.swap(*this);
}
// Note that this operator implements both copy and move assignments.
// It accepts its argument by value, which invokes the appropriate (copy or move) constructor.
X& operator=(X b) {
b.swap(*this);
return *this;
}
};
If you have been using this idiom in C++98, then once you add the move constructor you get the move assignment without writing a single line of code.
In some cases this idiom may be not the most efficient. Because the copy operator always first constructs a temporary and then swaps with it. By hand coding the assignment operators it may be possible to get better performance. When in doubt, check optimized assembly output and use a profiler.
I'm also looking over the Internet to find the best way to implement move constructor and move assignment. There are a few approaches but neither are perfect.
Following are my findings so far.
Here is a Test
class I'm using as an example:
class Test {
private:
std::string name_;
void* handle_ = nullptr;
public:
Test(std::string name)
: name_(std::move(name))
, handle_(malloc(128))
{
}
~Test()
{
if(handle_) free(handle_);
}
Test(Test&& other) noexcept; // we are going to implement it
Test& operator=(Test&& other) noexcept; // we are going to implement it
void swap(Test& v) noexcept
{
std::swap(this->handle_, v.handle_);
std::swap(this->name_, v.name_);
}
private:
friend void swap(Test& v1, Test& v2) noexcept
{
v1.swap(v2);
}
};
Test::Test(Test&& other) noexcept
: handle_(std::exchange(other.handle_, nullptr))
, name_(std::move(other.name_))
{
}
Test& Test::operator=(Test&& other) noexcept
{
if(handle_) free(handle_);
handle_ = std::exchange(other.handle_, nullptr);
name_ = std::move(other.name_);
return *this;
}
Test::Test(Test&& other) noexcept
: handle_(std::exchange(other.handle_, nullptr))
, name_(std::move(other.name_))
{
}
Test& Test::operator=(Test&& other) noexcept
{
this->~Test();
new (this) Test(std::move(other));
return *this;
}
Test::Test(Test&& other) noexcept
: handle_(std::exchange(other.handle_, nullptr))
, name_(std::move(other.name_))
{
}
Test& Test::operator=(Test&& other) noexcept
{
Test (std::move(other)).swap(*this);
return *this;
}
or copy and move operators 2-in-1:
Test& Test::operator=(Test other) noexcept
{
swap(other, *this);
return *this;
}
swap
functionThat is what you @Dongguo have found on MSDN
Test::Test(Test&& other) noexcept
{
*this = std::move(other);
}
Test& Test::operator=(Test&& other) noexcept
{
if(handle_) free(handle_);
handle_ = std::exchange(other.handle_, nullptr);
name_ = std::move(other.name_);
return *this;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With