我试图找出何时使用移动语义以及何时使用复制构造函数和赋值运算符作为经验法则。你的类中使用的指针类型(如果有的话)似乎受到这个答案的影响,所以我已经包含了这个。
没有指针 - 基于this答案,如果您有一个包含int和string等基本类型的POD类,则不需要编写自定义移动或复制构造函数和运营商。
unique-ptr - 基于this回答,当使用移动语义时,unique_ptr比shared_ptr更合适,因为资源只能有一个unique_ptr。
shared_ptr - 同样,如果使用复制语义,shared_ptr似乎还有很长的路要走。可以有多个对象副本,因此拥有一个指向资源的共享指针对我来说很有意义。但是,unique_ptr通常优先于shared_ptr,因此如果可以,请避免使用此选项。
可是:
答案 0 :(得分:6)
如名称所示,当资源必须只有一个所有者时,请使用unique_ptr
。 unique_ptr
的复制构造函数被禁用,这意味着它的两个实例不可能存在。但是,它是可移动的...这很好,因为这允许所有权的转移。
同样如名称所示,shared_ptr
表示资源的共享所有权。但是,两个智能指针之间还存在另一个区别:Deleter
的{{1}}是其类型签名的一部分,但它不是unique_ptr
的类型签名的一部分。这是因为shared_ptr
使用“类型擦除”来“删除删除者的类型”。另请注意,shared_ptr
也可以移动以转让所有权(例如shared_ptr
。)
我什么时候应该使用移动语义?
虽然可以复制unique_ptr
,但您可能希望在进行所有权转移时移动它们(而不是创建新引用)。您有义务对shared_ptr
使用移动语义,因为所有权必须是唯一的。
我什么时候应该使用复制语义?
对于智能指针,您应该使用复制来增加unique_ptr
的引用计数。 (如果您不熟悉引用计数的概念,则研究参考计算垃圾收集。)
我应该同时使用它们吗?
是。如上所述,shared_ptr
可以被复制和移动。复制表示递增引用计数,而移动仅表示所有权转移(引用计数保持不变。)
我应该使用none并依赖默认的复制构造函数和赋值运算符吗?
如果要创建对象的逐个成员副本。
答案 1 :(得分:5)
我什么时候应该使用移动语义?
我认为你的意思是,"我什么时候应该给我的班级一个移动构造函数?"答案是每当移动这种类型的对象很有用时,默认的移动构造函数不能正确地完成工作。将资源从一个对象转移到另一个对象有一些好处时,移动很有用。例如,移动对std::string
很有用,因为它允许从临时对象复制对象,而无需重新分配和复制其内部资源,而只需将资源从一个移动到另一个。许多类型都会从中受益。另一方面,移动对std::unique_ptr
非常有用,因为它是唯一可以在不违反其"唯一所有权的情况下按值传递std::unique_ptr
的方法。
我应该何时使用复制语义?
同样,我认为你的意思是,"我什么时候应该给我的班级一个复制构造函数?"每当您需要能够复制对象时,在它们之间复制内部资源,并且默认的复制构造函数不能正确地完成工作。复制对几乎任何类型都很有用,除了那些必须对内部资源强制执行唯一所有权的std::unique_ptr
之外。
我应该同时使用两者吗?
您的类应该在大多数时间提供复制和移动语义。最常见的类应该是可复制的和可移动的。可复制提供了按值传递对象的标准语义。可移动允许通过值传递临时对象时可以获得的优化。这是否意味着必须提供副本或移动构造函数取决于默认构造函数是否执行适当的操作。
我是否应该使用none并依赖默认的复制构造函数和赋值运算符?
默认的复制和移动构造函数只是分别对类的每个成员进行复制或移动。如果这种行为适合复制和移动你的课程,那就太棒了。大多数时候,这应该足够好了。例如,如果我有一个包含std::string
的类,则默认的复制构造函数将复制该字符串,默认的移动构造函数将字符串的资源移动到新对象 - 两者都做适当的工作。如果您的班级包含std::unique_ptr
,则复制将无效,您的课程将只能移动。这可能是您想要的,或者您可能希望实现执行资源深层副本的复制构造函数。您应该实现复制/移动构造函数的最重要的情况是您的类本身执行资源管理(例如,使用new
和delete
)。如果是这样的话,那么默认的构造函数几乎不会在管理这些资源方面做得很好。
这里的所有内容都类似于赋值运算符。