C++ 在遵循pimpl设计模式的类中实现移动语义的正确方法是什么?
做这件事的标准方法是什么 让我们有一个C++ 在遵循pimpl设计模式的类中实现移动语义的正确方法是什么?,c++,C++,做这件事的标准方法是什么 让我们有一个类a,它有一个数据成员std::unique\u ptr m\u impl 例如,类A的移动赋值操作符的内容是否应该如下所示 m_impl = std::move(other.m_impl); 还是这个 *m_impl = std::move(*other.m_impl); 第一种情况比第二种情况更有效。但这会引发一些问题 移动后,另一个的m_impl数据成员为null ptr。“从对象移动”应该在使用时引发异常,还是仅仅让用户由于取消引用nullptr
类a
,它有一个数据成员std::unique\u ptr m\u impl
例如,类A的移动赋值操作符的内容是否应该如下所示
m_impl = std::move(other.m_impl);
还是这个
*m_impl = std::move(*other.m_impl);
第一种情况比第二种情况更有效。但这会引发一些问题
移动后,另一个的m_impl数据成员为null ptr。“从对象移动”应该在使用时引发异常,还是仅仅让用户由于取消引用nullptr而运行时出错
对于线程安全类,这也意味着需要同步所有m_impl的使用。我不确定调用std::unique\u ptr
的移动构造函数/赋值运算符是否是线程安全的。从对象移动的对象不应再“活动”(有效但未指定)有效但未指定:
- 允许破坏
- 允许重新分配
- 禁止其他用途
- 允许破坏
- 允许重新分配
- 禁止其他用途
第一种选择,即移动成员(即内部成员),而不是参考成员,更可取。前者。当您使用
唯一\u ptr
时,可能是因为对象本身首先可能不可复制和/或移动。因此,移动指针对象通常不是一个选项
移动后,另一个的m_impl数据成员为null ptr
这是从std::unique\u ptr
移动过来的。它需要支持销毁和重新分配,其他一切都是未定义的,不管它做什么(空指针情况下为segfault)
对于线程安全类,这也意味着需要同步所有m_impl的使用
除非明确说明,否则标准库不是线程安全的,只是可重入的。如果您的对象需要线程安全,您必须自己确保它
也就是说,如果一个线程移动了一个它不是唯一所有者的对象,它将给其他线程带来问题,因为它们仍将尝试在旧位置访问它
您可以针对对象上的所有操作锁定move操作,并定义moved from对象上操作的语义,以某种方式返回错误,但这听起来很痛苦,而且使用起来非常混乱
线程安全对象实际上应该是例外。您应该致力于在线程之间移交对象,以便在任何给定时间只有一个线程拥有该对象,并且所有共享数据都是不可变的(除了可能的引用计数,这可以通过合理性能的std::atomic
或std::shared\u ptr
来完成,后者构建在它之上)
如果您真的需要线程安全的对象,那么它要么不能移动,要么应该像句柄一样通过
std::shared_ptr
引用它的内部,这是线程安全的。前者。当您使用唯一\u ptr
时,可能是因为对象本身首先可能不可复制和/或移动。因此,移动指针对象通常不是一个选项
移动后,另一个的m_impl数据成员为null ptr
这是从std::unique\u ptr
移动过来的。它需要支持销毁和重新分配,其他一切都是未定义的,不管它做什么(空指针情况下为segfault)
对于线程安全类,这也意味着需要同步所有m_impl的使用
除非明确说明,否则标准库不是线程安全的,只是可重入的。如果您的对象需要线程安全,您必须自己确保它
也就是说,如果一个线程移动了一个它不是唯一所有者的对象,它将给其他线程带来问题,因为它们仍将尝试在旧位置访问它
您可以针对对象上的所有操作锁定move操作,并定义moved from对象上操作的语义,以某种方式返回错误,但这听起来很痛苦,而且使用起来非常混乱
线程安全对象实际上应该是例外。您应该致力于在线程之间移交对象,以便在任何给定时间只有一个线程拥有该对象,并且所有共享数据都是不可变的(除了可能的引用计数,这可以通过合理性能的std::atomic
或std::shared\u ptr
来完成,后者构建在它之上)
如果您真的需要线程安全的对象,那么它要么不能移动,要么应该像句柄一样通过
std::shared_ptr
引用它的内部结构,这是线程安全的。让我们从另一个主题来了解这一点
如何交换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);
但是,作为一种效果,您的类的用户不应该在moved from对象中取消引用
m_impl
。这是move语义在类中使用时引入的内容。由类的用户来防止这种情况的发生,就像如果v
是从std::vector
移动过来的,那么代码就不应该这样做一样。让我们从另一个主题来了解这一点
如何交换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);
但是,作为一种效果,您的类的用户不应该在移动的fr中取消引用m\u impl