从C ++ 11开始,当使用移动赋值运算符时,我应该std::swap
我的所有数据,包括POD类型吗?我想这对下面的例子没什么影响,但我想知道普遍接受的最佳做法是什么。
示例代码:
class a
{
double* m_d;
unsigned int n;
public:
/// Another question: Should this be a const reference return?
const a& operator=(a&& other)
{
std::swap(m_d, other.m_d); /// correct
std::swap(n, other.n); /// correct ?
/// or
// n = other.n;
// other.n = 0;
}
}
您可能想要考虑以下形式的构造函数: - 即:总是"有意义的"或定义的值存储在n
或m_d
。
a() : m_d(nullptr), n(0)
{
}
答案 0 :(得分:2)
我认为这应该以这种方式重写。
class a
{
public:
a& operator=(a&& other)
{
delete this->m_d; // avoid leaking
this->m_d = other.m_d;
other.m_d = nullptr;
this->n = other.n;
other.n = 0; // n may represents array size
return *this;
}
private:
double* m_d;
unsigned int n;
};
答案 1 :(得分:1)
不,如果效率有任何问题,请不要交换POD。与正常分配相比,没有任何好处,它只会导致不必要的副本。还要考虑是否甚至需要将从POD移动到0设置。
我甚至不会交换指针。如果这是一个拥有关系,请使用unique_ptr并从中移动,否则将其视为POD(将其复制并在之后将其设置为nullptr或程序逻辑需要的任何内容)。
如果您不必将POD设置为零并使用智能指针,则根本不需要实施移动运算符。
关于问题的第二部分: 正如Mateusz所说,赋值运算符应该总是返回一个正常(非常量)引用。
答案 2 :(得分:1)
我应该std ::交换我的所有数据
一般情况下。移动语义可以使事情变得更快,并且交换直接存储在对象中的数据通常比复制它更慢,并且可能为某些移动的数据成员分配一些值。
针对您的具体情况......
class a
{
double* m_d;
unsigned int n;
......仅仅考虑数据成员才知道什么是有道理的。例如,如果您对非POD成员使用假定的交换组合,则使用其他方式......
std::swap(m_d, other.m_d);
n = other.n;
other.n = 0;
...在移动构造函数或赋值运算符中,如果说析构函数在m_d
为n
时跳过删除0
,或者如果它被删除,则它可能仍会使您的程序状态无效在使用指向新分配的内存的指针覆盖n == 0
之前检查m_d
,可能会泄漏旧内存。您必须决定类不变量:m_d
和n
的有效关系,以确保您的移动构造函数和/或赋值运算符使状态对将来的操作有效。 (大多数情况下,移动对象的析构函数可能是唯一可以运行的东西,但它对于重用移动对象的程序是有效的 - 例如为其分配一个新值并继续工作它在循环的下一次迭代中....)
另外,如果您的不变量允许非nullptr
m_d
而n == 0
,则交换m_d
是吸引人的,因为它会让移动的对象持续控制移动对象可能具有的任何缓冲区:可以节省以后分配缓冲区的时间;专业的平衡,如果以后不需要缓冲区,你将其分配的时间超过了必要的时间,如果它不够大,你最终会删除和更新更大的缓冲区缓冲,但至少你是懒惰的,这往往有助于提高性能(但如果你不得不关心,可以进行描述)。