最佳C++移动构造函数实现实践
Best C++ move constructor implementation practice
我正在努力理解move构造函数的实现。我们都知道,如果我们需要在C++类中管理资源,我们需要实现五条规则(C++编程)。
微软给我们举了一个例子:https://msdn.microsoft.com/en-us/library/dd293665.aspx
这里有一个更好的,它使用拷贝交换来避免代码重复:动态分配对象数组
// 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
}
在move构造函数中,直接:
// Can we write src.swap(*this);
// or (*this).swap(src);
因为我觉得(*this) = std::move(src)
有点复杂。因为如果我们无意中写成(*this) = src
,它会调用普通赋值运算符,而不是移动赋值运算符。
除了这个问题,在微软的例子中,他们写了这样的代码:在移动赋值运算符中,我们需要检查自赋值吗?这有可能发生吗?
// 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;
}
swap
函数。
然后使用前三个实现移动构造函数、复制和移动赋值运算符。
例如:
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;
}
};
如果您一直在C++98中使用这个习惯用法,那么一旦添加了move构造函数,您就可以在不编写任何代码的情况下获得move赋值。
在某些情况下,这个成语可能不是最有效的。因为复制操作符总是先构造一个临时操作符,然后与之交换。通过手工编码赋值操作符,可以获得更好的性能。如果有疑问,请检查优化的程序集输出并使用探查器。
我还在互联网上寻找实现移动构造函数和移动赋值的最佳方法。有几种方法,但都不是完美的。
以下是我到目前为止的发现。
以下是我使用的一个Test
类作为示例:
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);
}
};
方法#1:直线前进
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;
}
优点
- 最佳性能
Cons
- 移动构造函数和移动赋值中的代码重复
- 部分析构函数的代码在移动赋值中重复
方法2:破坏+施工
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;
}
优点
- 无代码重复
- 如果没有虚拟功能,性能良好
Cons
- 虚拟方法表(VMT)初始化两次(如果类具有虚拟函数)
- 不能在基类中使用。基类只能实现移动构造函数
方法3:复制‘Swap
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;
}
或复制和移动操作员二合一:
Test& Test::operator=(Test other) noexcept
{
swap(other, *this);
return *this;
}
优点
- 无代码重复
Cons
- 创建了额外的对象
- 在
swap
函数内交换数据成员时创建的额外较小对象 - 交换函数中的某种代码重复
方法#4:通过移动赋值移动构造函数
这就是你@Dongguo在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;
}
优点
- 无代码重复
Cons
- 不适用于包含非默认可构造数据成员的类
- 数据成员在move构造函数中初始化两次
链接
- [Wiki]-移动分配运算符
- [cppreference.com]-移动赋值运算符
- [MSDN]-移动构造函数和移动赋值运算符
- [Vollmann]-C++:关于实现移动分配的更多信息
- 移动,简单
更多答案
- 通过调用Move赋值运算符实现Move构造函数
- 复制和交换习语是什么
- 移动构造函数与移动赋值
相关文章:
- 通过构造函数动态确定类实现
- 运算符+ 的规范实现涉及额外的移动构造函数
- 类中的数组变量C++导致"was not declared in this scope"实现文件的一个函数中错误,但在构造函数中不会导致错误
- 如何为具有私有成员的派生类实现移动构造函数
- 如何正确实现具有原始指针的类的复制构造函数?
- 复制构造函数和赋值运算符的值类实现
- 如何实现共享数据的复制构造函数
- 嵌套的模板结构构造函数实现
- 为什么<T> LLVM 中的预期为 Expect&&... 实现两个构造函数<T>?
- 在C++中正确实现复制构造函数
- 如何实现构造函数,使其仅接受使用 typeid 的输入迭代器?
- 如何将深拷贝构造函数实现到链表中?
- 如何在派生类上强制实现特定数据类型的构造函数?
- 如何实现内联显式类的构造函数?
- C++头/实现文件中的默认和重载构造函数?
- C++:默认构造函数实现
- std::sort 一个实现了移动构造函数的类
- 实现标记联合的移动构造函数
- 我如何实现从其基类获取变量的构造函数
- 实现构造函数时出现LNK2019错误