【问题标题】:How does a move constructor work?移动构造函数如何工作?
【发布时间】:2016-10-24 07:03:21
【问题描述】:

我无法理解 C++ 移动语义。假设我想从一个方法返回一个大对象。为了使这个操作高效,我为大对象类使用了一个移动构造函数。我调用一个方法来获取一个大对象并将它传递给另一个将处理它的方法。

processLargeObject(getLargeObject());

我编写了一个方法getLargeObject(),在其中创建了一个大对象。如果我在没有 new 运算符的情况下创建对象,我会明白存储是在堆栈上分配的。在我事先知道对象的大小并用数组表示的情况下,这个大对象可以定义为double foo[1000000000];。现在在移动构造函数中,如果我说

foo = other.foo;

那么被移动的对象仍将驻留在堆栈中,并且可以随着堆栈的扩展而被覆盖。

所以这不能是移动构造函数的使用方式。移动构造函数是否应该仅用于移动位于堆上的对象?

【问题讨论】:

  • 如果foo 是一个双精度数组,foo = other.foo; 将不会编译。无论如何,您将如何移动一个普通的双精度数组?
  • 这是否意味着我必须遍历数组并创建一个副本?
  • 您是否尝试过声明和使用一个在普通数组中包含十亿双精度数的对象?似乎是溢出堆栈的好方法。你应该使用vector<double> 然后移动才有意义。
  • 我认为向量的实现方式,由于它的移动构造函数,它会被移动。我只是想了解移动语义。
  • 移动该数组的内容不是你想要的吗? vector 会为你做这件事。阅读this,它有一些关于移动语义的好答案。

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


【解决方案1】:

移动构造函数是如何工作的?

移动构造函数应该做一个拷贝——而不是一个拷贝构造函数应该做的一个拷贝。除了进行浅拷贝之外,移动构造函数“窃取”了被移动对象指向的所有资源(例如外部内存缓冲区,例如 std::vector 拥有的缓冲区)。

为了让这个操作更高效,我为大对象类使用了一个移动构造函数。

从迂腐的角度来说,对象本身的庞大并不会使移动比复制更有效。当对象指向 到一些可能被“窃取”的大型资源时,移动是有效的。如果您的意思是这样的外部资源是使对象“大”的原因,那么就足够公平了。

移动构造函数是否应该只用于移动位于堆上的对象?

无论对象存储在何处 - 无论是自动的、动态的还是静态的,都可以从中移动。只要类型是可移动的。

这个大对象可以定义为double foo[1000000000];

这是一个非常大的对象的一个​​很好的例子,它没有指向任何可能被盗的外部资源。移动一个双精度数组与复制它完全相同。

【讨论】:

  • 谢谢。我得到了这个部分。我不明白的是,移动的对象是否需要驻留在堆上?
  • 为了让移动构造函数获得显着的好处,大部分数据应该存储在对象中指针所指的堆上的数据中。 (因此,std::array<double, 1000000> 的移动构造通常与复制构造一样昂贵;但是具有 1000000 个元素的 std::vector<double> 移动构造的速度非常快。)
  • 谢谢!!!现在对我来说很有意义。以下是对良好编程实践的一个很好的总结:在没有 new 运算符的情况下创建对象,以便将内存泄漏的可能性降到最低。创建的对象可以在堆上保存内存。对析构函数进行编码,以便释放堆内存。这种方式保证内存被退回。编写一个移动构造函数,使移动的对象指向旧对象的位置,并使旧对象指向一个空指针。
  • @AggieMan 使用std::unique_ptrstd::shared_ptrstd::vector 而不是自己管理内存。如果您的数据是固定大小的,您始终可以使用std::unique_ptr<std::array<...>>。在堆栈上创建一个以double foo[1000000000] 为成员的对象只是自找麻烦。堆栈有限。
猜你喜欢
  • 2018-09-23
  • 2018-06-26
  • 1970-01-01
  • 2011-03-08
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
相关资源
最近更新 更多