Pimpl成语+感动?

时间:2014-03-24 09:00:58

标签: c++

这样做的标准方法是什么?

我们的class A有一个数据成员std::unique_ptr<Impl> m_impl

例如,A类移动赋值运算符的内容应该是这样的吗?

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

还是这个?

*m_impl = std::move(*other.m_impl);

第一种情况比第二种情况更有效。但它会引发一些问题。

移动后,其他的m_impl数据成员为nullptr。移动对象在被使用时是否会抛出异常,或者由于解除引用nullptr而让用户遇到运行时错误?

对于线程安全类,这也意味着需要同步所有m_impl用法。我不确定调用std::unique_ptr的移动构造函数/赋值运算符是否是线程安全的。

3 个答案:

答案 0 :(得分:2)

移动物体不应该是&#34;活着&#34;再次(有效但未指明)有效但未指明:

  • 允许销毁
  • 允许重新分配
  • 其他禁止使用

第一个选项,即移动成员(即内部的成员)而不是引用,更可取。

答案 1 :(得分:2)

前者。当您使用unique_ptr时,您可能正在这样做,因为对象本身可能首先不可复制和/或移动。因此,移动指针通常不是一种选择。

  

移动后,其他的m_impl数据成员为nullptr。

这是正确的移动 - 来自std::unique_ptr。它需要支持破坏和重新分配,其他所有内容都是未定义的,并且无关紧要(在空指针的情况下发生了段错误)。

  

对于线程安全类,这也意味着需要同步所有m_impl用法。

除非明确说明,否则标准库不是线程安全的,只能是可重入的。如果你的对象需要是线程安全的,你必须自己确保它。

如果线程移动了一个不是唯一所有者的对象,则会导致其他线程出现问题,因为他们仍然会尝试在旧位置访问它。

您可以针对对象上的所有操作锁定移动操作,并定义移动对象上的操作的语义以某种方式返回错误,但这听起来很痛苦并且使用起来非常混乱。

线程安全对象应该是异常。您应该在线程之间处理对象,以便在任何给定时间只有一个拥有该对象,并使所有共享数据不可变(除了可能的引用计数,可以通过合理的性能std::atomic或{{1}来完成},它建立在它之上)。

如果你真的需要一个线程安全的对象,它应该不是可移动的,或者应该像std::shared_ptr那样引用它的内部的句柄, 是线程安全的。< / p>

答案 2 :(得分:2)

让我们看一下来自不同主题的光。

你如何交换pimpl类对象?

void swap(T& x, T& y) {
   std::swap(x.m_impl, y.m_impl);
}

右?

只有围绕m_impl指针移动。因此,使用移动语义,您还应该只移动m_impl指针!

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

但是,作为一种效果,您的班级用户不应在移动的对象中取消引用m_impl。这是在您的类中使用它时移动语义引入的内容。您的课程的用户可以防止这种情况发生,就像您的代码不应该v[0] v移动来自std::vector等等。