【问题标题】:Will the Move ctor effective in all scenarios?Move ctor 在所有情况下都有效吗?
【发布时间】:2016-04-20 11:22:35
【问题描述】:

我刚刚从https://msdn.microsoft.com/en-us/library/dd293665.aspx 发现了以下代码,它显示了如何实现移动 ctor:

MemoryBlock(MemoryBlock&& other)
   : _data(nullptr)
   , _length(0)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // 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;
}

此代码附带的问题/疑问之一是:如果类 MemoryBlock 包含一些类类型成员变量(比如 someclassvar ),并且如果该成员相当大,那么下面的行会在修改后的move ctor有效(假设这个someclassvar没有move ctor)?

MemoryBlock(MemoryBlock&& other)
   : _data(nullptr)
   , _length(0),someclassvar(other.someclassvar)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the 
   // source object.
   _data = other._data;
   _length = other._length;
  // someclassvar=other.someclassvar;

   // 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;
   delete other.someclassvar;
}

这里someclassvar = other.someclassvar; 我相信这会调用复制赋值运算符,那么这种行为有效吗?此外,如果someclassvar 包含移动分配和复制分配,可以预期调用哪个?

move ctor 在所有情况下都有效吗?如果没有,是否有副本 ctor 更好?

【问题讨论】:

  • someclassvar = std::move(other.someclassvar);
  • (assumed that this someclassvar doesn't have move ctor) 有 std::move 做这项工作吗?
  • 没有。 std::move 将 move 如果 move ctor / move = 可用,否则 copy
  • 首先,Move 并没有真正移动,它只是无条件地将参数转换为移动 ctor 将接受的右值,我的问题是我构造的移动 ctor 在所有情况下都有效吗?
  • 如您所见,someclassvar (可能)是某个指针,因此有问题的行将是一个指针分配。

标签: c++ move-semantics


【解决方案1】:

首先,如果可能的话,在任何构造函数中,您应该更喜欢在构造函数的初始化列表中初始化您的成员。其次,在移动构造函数中,您应该使用std::move 来移动可移动的成员,或者如果它们不可移动则复制它们:

MemoryBlock(MemoryBlock&& other)
: _data(std::move(other._data)), 
  _length(std::move(other._length)),
  someclassvar(std::move(other._someclassvar)) {
   // optionally set state of moved object
}

现在std::move 不会移动任何对象,而是将它们转换为右值引用。因此,由于重载决议,将会发生两件事:

  1. 如果要移动的类型具有用户定义或隐式定义的移动构造函数,则将调用此移动构造函数,因此对象将相应地移动到其新宿主。
  2. 如果要移动的类型没有任何已定义的移动构造函数,则将调用该类型的复制构造函数。这归因于右值引用可以绑定到const 左值引用这一事实,因此在移动构造函数的帮助下,复制构造函数可以匹配重载解析。

Live Demo

现在复制分配和移动分配几乎相同。正在做:

someclassvar = other.someclassvar;

会将other.someclassvar 复制分配给someclassvar。为什么?由于重载解决规则。但是,如果你这样做:

someclassvar = std::move(other.someclassvar);

如果someclassvar 有一个移动赋值运算符,那么出于与上述相同的原因,它将被调用。另一方面,如果someclassvar 没有移动赋值运算符,则将调用其赋值运算符。这里不涉及构造函数,因为该语句是一个赋值。

【讨论】:

  • 不错..我只是复制了代码,但我会按照你说的那样使用初始化列表。
【解决方案2】:
MemoryBlock(MemoryBlock&& other)
   : _data(nullptr)
   , _length(0), someclassvar(std::move(other.someclassvar))
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // 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;
}

如果someclassvar 类有move ctor/ move =,那么std::move(other.someclassvar) 将是move,否则它将是copy。

如果需要高效,请为 someclassvar 类提供 move ctor/ move =。

【讨论】:

  • 在上述情况下复制 ctor/assignment 与移动 ctor/assignment 的结果相同吗?
猜你喜欢
  • 2013-08-18
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多