C ++移动语义 - 究竟要实现什么?

时间:2013-05-29 07:58:30

标签: c++ c++11 move move-semantics

这种“移动”语义的目的究竟是什么?我理解,如果你没有通过引用传入一个副本是由非原始类型,但“移动”如何改变任何东西?我们为什么要“移动”数据?为什么它不能保持在同一地址而不被复制?如果它被发送到另一个地址,这不仅仅是“复制和删除”吗?

简而言之,我并没有真正了解语义实现的动作。

3 个答案:

答案 0 :(得分:2)

移动语义结合了传递值和传递引用的优点。您可以静态分配类,因此您不必对其生命周期负责,并且可以将它们作为参数传递并轻松地从函数返回。另一方面,在这种情况下,当通常会复制对象时,它们会被移动(只复制它们的内部结构)。这个操作可以比复制花费更少的时间来实现(因为你知道,rhs对象将不再被使用)。

MyObj * f()
{
    // Ok, but caller has to take care of
    // freeing the result
    return new MyObj();
}

MyObj f()
{
    // Assuming, that MyObj does not have move-ctor
    // This may be time-costly
    MyObj result;
    return result;
}

MyObj f()
{
    // This is both fast and safe
    MyObj result;
    return std::move(result);

    // Note, if MyObj implements a move-ctor,
    // usually you don't have to call std::move.
}

答案 1 :(得分:1)

  

为什么不能将它保存在同一地址而不是复制

这实际上是语义通常所做的。它通常将资源(通常是内存,但可以是文件句柄等)保持在完全相同的状态,但它会更新对象中的引用。

想象两个向量,srcdestsrc向量包含在堆上分配的大块数据,dest为空。当src移动到dest时,所有发生的事情都是dest更新为指向堆上的内存块,而src更新为指向dest {在这种情况下,{1}}指向的是什么。

为什么这有用?因为这意味着可以写入vector,并确信只有一个向量将指向它分配的内存块。这意味着析构函数可以确保它清理已分配的内存。

这可以扩展为管理其他资源的对象,例如文件句柄。现在可以编写可以拥有文件句柄的对象。这些对象可以移动,但不可复制。因为STL容器支持可移动对象,所以可以将它们放入容器中,这比在C ++ 03中容易得多。它们的文件句柄或其他资源,只保证引用它,析构函数可以适当地关闭它。

答案 2 :(得分:0)

我将回答一个关于向量代数的简单例子:

class Vector{
  size_t dim_;
  double *data_;
public:
  Vector(const Vector &arg)
    : dim_(arg.dim_)
    , data_(new double[dim_])
  {
    std::copy_n(arg.data_, dim_, data_);
  }

  Vector(Vector &&arg)
    : dim_(arg.dim_)
    , data_(arg.data_)
  {
    arg.data_ = nullptr;
  }

  ~Vector()
  {
    delete[] data_;
  }

  Vector& operator+= (const Vector &arg)
  {
    if (arg.dim_ != dim_) throw error;
    for (size_t idx = 0; idx < dim_; ++idx) data_[idx] += arg.data_[idx];
    return *this;
  }
};

Vector operator+ (Vector a, const Vector &b)
{
  a += b;
  return a;
}

extern Vector v1, v2;

int main()
{
  Vector v(v1 + v2);
}

加法按值返回一个新向量。由于它是一个r值,它将被移动v,这意味着不会发生潜在巨大数组data_的额外副本。