在什么条件下我应该考虑实现移动构造函数和移动运算符?

时间:2011-12-12 09:27:43

标签: c++ constructor c++11 rvalue-reference

对于标准拷贝构造函数和赋值运算符,如果我的类实现了析构函数,我总是考虑实现它们或delete默认值不存在。

对于新的移动构造函数移动运算符,考虑是否需要实现的正确方法是什么?

作为从C ++之前的0x转换系统的第一步,我可以delete默认移动构造函数移动运算符或者应该我让他们一个人呆着?

3 个答案:

答案 0 :(得分:10)

你没有担心它,在某种意义上,当你用户声明析构函数(或12.8 / 9中列出的任何其他东西)时,会阻止默认的移动构造函数产生。因此,与副本的风险不同,默认是错误的。

所以,作为第一关,让他们独自一人。现有代码中可能存在C ++ 11移动语义允许移动的位置,而C ++ 03则指示副本。你的类将继续被复制,如果这在C ++ 03中没有引起任何性能问题,那么我无法立即想到它在C ++ 11中的原因。如果它确实导致了C ++ 03中的性能问题,那么你就有机会修复你以前从未遇到的代码中的错误,但这是一个机会,而不是义务; - )

如果你以后实现移动构造和赋值,它们将被移动,特别是如果你认为你的类的C ++ 11客户端不太可能使用“swaptimization”来避免拷贝,你会想要这样做,比C ++ 03客户端更有可能按值传递您的类型。

在C ++ 11中编写新类时,您需要在C ++ 03中考虑并实现swap的相同条件下考虑并实现移动。可以复制的类实现了C ++ 11的“可移动”概念,(就像可以在C ++ 03中复制的类可以通过std中的默认实现交换),因为“可移动” “没有说明该来源留在哪个州 - 特别是它被允许保持不变。因此,副本 作为移动有效,它不一定是有效的,对于许多类,您会发现与“好”移动或交换不同,副本可以抛出。

您可能会发现,如果您有析构函数(因此没有默认的移动构造函数),您必须为您的类实现move,并且您还有一个可移动但不可复制的数据成员(因此也没有默认的复制构造函数) )。这就是当移动在语义和性能上变得重要时。

答案 1 :(得分:4)

使用C ++ 11,由于库的编写方式,您很少需要提供析构函数或复制语义。编译器提供的成员几乎总是很好(只要它们被正确实现:MSVC迫使你手动实现很多移动语义,这非常麻烦)。

如果您必须实现自定义析构函数,请使用以下方法:

  • 实现移动构造函数和按值分配的赋值运算符(使用copy& swap:请注意,由于它使用了赋值,因此您无法使用std::swap。您必须自己提供私有交换)。注意异常保证(查找std::move_if_noexcept)。
  • 如有必要,请实现复制构造函数。否则,删除它。请注意,非默认复制语义很少有意义。

此外,虚拟析构函数计为自定义析构函数:在声明虚拟析构函数时提供或删除复制+移动语义。

答案 2 :(得分:0)

由于它们被用作优化,因此如果优化适用于您的类,则应实现它们。如果你可以从一个即将被销毁的临时对象“窃取”你的类所持有的内部资源。 std :: vector是一个很好的例子,其中移动构造函数只分配指向内部缓冲区的指针,使临时对象为空(有效地窃取元素)。