【发布时间】:2014-04-01 23:01:08
【问题描述】:
大多数不可复制的对象都是不可复制的,因为拥有多个对象(例如,std::unique_ptr)是荒谬的或有问题的,但移动它们仍然没问题。但是有哪些例子有充分的理由使对象既不可复制也不可移动?
【问题讨论】:
-
记住,移动一个对象会破坏所有指向它的指针和引用...
大多数不可复制的对象都是不可复制的,因为拥有多个对象(例如,std::unique_ptr)是荒谬的或有问题的,但移动它们仍然没问题。但是有哪些例子有充分的理由使对象既不可复制也不可移动?
【问题讨论】:
当它实现单例模式时。如果一个类只有一个且只有一个实例,那么移动或复制它真的没有意义。
【讨论】:
std::condition_variable(参见此处:condition_variable)。
我怀疑是因为在尝试移动时可能存在数据竞争。
【讨论】:
std::mutex,原因更复杂。
在大型软件的代码中,您会发现许多不可复制和/或不可移动的类,但经常缺少不可复制/不可移动的基类。如果您有一个复杂的类,它引用了一些其他具有拥有引用的对象,那么该对象通常被视为不可复制,除非有人提供了执行深度复制的复制构造函数(也通过复制引用的对象)。这种情况很普通,这些复杂的对象通常没有用户指定的复制构造函数。复制/移动构造函数的另一个问题是它们难以维护,这是一个常见的错误来源,即有人向类添加新成员但忘记更新复制/移动构造函数。
但是,在几乎所有情况下,您都可以通过提供自己的复制构造函数来使类可复制,除非它包含在引用图中明确标记为不可复制的内容。
什么时候让某些东西不可复制才有意义?在某些情况下,不可复制对象封装了一次只能由一个线程或子系统使用的东西,例如文件句柄或任何其他类型的系统资源句柄。有时拥有此类资源的副本没有意义,但您仍然可以使用移动构造函数将句柄的值安全地移动到另一个位置到另一个对象。
什么时候做不可移动的东西?这是一个示例:假设您为线程间同步创建了一个 Lock 对象。在 Windows 上,您使用关键部分实现此 Lock 类,并按值将 CRITICAL_SECTION 放入您的类。 InitializeCriticalSection (http://msdn.microsoft.com/en-us/library/windows/desktop/ms683472%28v=vs.85%29.aspx) 的文档指出:
不能移动或复制临界区对象。该过程也不得修改对象,但必须将其视为逻辑上不透明的。
因此,明智的做法是将 Lock 类标记为不可复制和不可移动。您可以通过仅引用包含CRITICAL_SECTION 的对象来使您的 Lock 类可移动,这样只有内部对象是不可移动的,而您的 Lock 实例是可移动的。即使以这种方式复制 Lock 实例也是不明智的,因为您将无法复制 CRITiCAL_SECTION 实例的内部状态,因此副本在某些方面与原始状态不同。
所以总结一下:有时一个对象不能被复制,因为你自己的代码不能影响环境(比如CRITICAL_SECTION 或一些静态的第 3 方库),但你总是可以让你的类在最坏的情况下可移动通过将一些不可移动的成员变量放入引用所持有的内部对象中。实际上,软件中的许多复杂对象都没有不可移动和不可复制的基类,即使它们的复制/移动会导致错误/崩溃。
【讨论】: