7

我在另一个问题中读到,在实现移动构造函数时,std::move 初始化器列表中的每个成员是一个好习惯,因为如果该成员恰好是另一个对象,那么将调用该对象的移动构造函数。像这样...

//Move constructor
Car::Car(Car && obj)
    : 
    prBufferLength(std::move(obj.prBufferLength)), 
    prBuffer(std::move(obj.prBuffer)) 
{
    obj.prBuffer = nullptr;
    obj.prBufferLength = 0;
}

然而,在我见过的所有示例移动赋值运算符中,出于同样的原因,没有提到使用 std::move 。如果成员是一个对象,那么应该使用 std::move 吗?像这样...

//Move assignment
Car Car::operator=(Car && obj)  
{
    delete[] prBuffer;

    prBufferLength = std::move(obj.prBufferLength);
    prBuffer = std::move(obj.prBuffer);

    obj.prBuffer = nullptr;
    obj.prBufferLength = 0;
    return *this;
}

更新:

我很欣赏在我选择的示例中不需要使用 std::move (差)但是我对成员是否是对象感兴趣。

4

5 回答 5

3

在阅读了链接的问题之后,我可以看到第二个最受好评的答案中的建议是std::move在移动构造函数的初始化列表中使用,因为无论它是否是原始类型,它都会做正确的事情。我有点不同意这一点,并认为你应该只std::move在适当的时候打电话,但这是个人喜好。

此外,对于您的移动分配操作员,您拥有它的方式很好,尽管我认为std::move应该亲自删除不必要的调用。另一种选择是使用std::swap哪个会为您做正确的事情。

Car Car::operator=(Car && obj)  
{
    std::swap(this->prBufferLength, obj.prBufferLength);
    std::swap(this->prBuffer, obj.prBuffer); 
    return *this;
}

上述移动赋值运算符和您的移动赋值运算符之间的区别在于,内存的释放会延迟,而您的版本会立即释放内存,这在某些情况下可能很重要。

于 2012-11-15T21:05:07.643 回答
1

它看起来像prBuffer一个指针并且prBufferLength是某种整数类型,因此move在这种特殊情况下不会有任何区别,因为它们都是基本类型。

如果prBufferstd::string例如,那么您应该使用move来强制使用移动构造函数或移动赋值运算符。

于 2012-11-15T20:43:10.297 回答
1

就像 C++ 和生活中的许多事情一样,这个问题没有明确的“是”或“否”答案。

也就是说,作为一般规则,如果传入成员将被清除/重置/清空并将传入成员分配给目标成员将导致调用分配运算符,那么您将需要使用 std::move 以便移动分配将调用运算符而不是复制赋值运算符。

如果不调用赋值运算符(即只进行浅拷贝),则不需要使用 std::move。

于 2012-11-15T20:48:43.490 回答
1

如果您的成员对象将从移动中受益,您当然应该移动它们。您链接到的答案中展示了另一种策略,即交换。交换就像移动,但它是双向移动的。如果您的类管理资源,则这具有传入的对象(右值)接收不再需要的数据的效果。然后该数据被该对象的析构函数销毁。例如,您的移动赋值运算符可以这样编写:

Car Car::operator=(Car && obj)  
{
    // don't need this, it will be handled by obj's destructor
    // delete[] prBuffer; 

    using std::swap;
    swap(prBuffer, obj.prBuffer);
    swap(prBufferLength, obj.prBufferLength);

    return *this;
}

还可以看看Copy-and-Swap成语。这允许您对移动和复制使用相同的赋值运算符,但有一个小缺点,即自赋值会导致不必要的复制。

于 2012-11-15T21:04:31.477 回答
0

我相信实现移动赋值的更好方法是使用移动构造函数创建一个新的临时对象,然后将其与当前对象交换,就像复制赋值一样。

它不仅可以避免代码重复,还可以防止您因忘记移动成员而意外出错。

于 2012-11-15T21:00:53.137 回答