2014-07-23 11 views
1

我想弄清楚何时使用移动语义以及何时使用复制构造函数和赋值操作符作为经验法则。你使用的指针类型(如果有的话)似乎受到这个答案的影响,所以我已经包含了这个。C++移动语义与复制构造器和分配操作员关于智能指针

没有指针 - 基于this答案,如果你有原始类型,如int和字符串POD类,你不需要编写自定义移动或复制建设者和运营商。

独特-PTR - 基于this答案,使用移动语义的时候,那么的unique_ptr是在shared_ptr的一个更适合的只能有一个的unique_ptr的资源。

shared_ptr - 同样,如果使用复制语义,shared_ptr似乎是要走的路。可以有多个对象的副本,所以拥有一个指向资源的共享指针对我来说是有意义的。但是,unique_ptr通常优先于shared_ptr,因此如果可以,请避免使用此选项。

但是:

  1. 什么时候应该使用移动语义?
  2. 什么时候应该使用复制语义?
  3. 我是否应该同时使用两者?
  4. 我是否应该使用none并依赖默认的拷贝构造函数和赋值运算符?

回答

6

顾名思义,当必须只存在一个资源所有者时,使用unique_ptrunique_ptr的复制构造函数被禁用,这意味着它的两个实例不可能存在。然而,它是可移动的......这很好,因为它允许所有权转让。

同样如名称所示,shared_ptr代表资源的共享所有权。但是,两个智能指针之间还有另一个区别:unique_ptrDeleter是其类型签名的一部分,但它不是shared_ptr类型签名的一部分。这是因为shared_ptr使用“类型擦除”来“删除删除者的类型”。另请注意,shared_ptr也可以转移到转让所有权(如unique_ptr)。

什么时候应该使用移动语义?

尽管可以复制shared_ptr,但是当您进行所有权转让(而不是创建新的参考)时,您可能需要移动它们。您有义务使用unique_ptr的移动语义,因为所有权必须是唯一的。

什么时候应该使用复制语义?

在智能指针的情况下,您应该使用复制来增加引用计数shared_ptr s。 (如果您不熟悉参考计数的概念,研究参考计算垃圾收集。)

我是否应该同时使用两者?

是的。如上所述,shared_ptr可以被复制和移动。复制表示递增引用计数,而移动仅指示所有权转移(引用计数保持不变)。

我应该使用none还是依赖于默认的复制构造函数和赋值运算符?

当您想制作一个对象的成员一份副本。

+1

简洁,回答所有要点。有一个upvote。 –

5
  1. 什么时候应该使用移动语义?

    我假设你的意思是“我应该什么时候给我的班级一个动作构造函数?”答案是每当这种类型的移动对象有用时,默认的移动构造函数就不能正确地完成这项工作。如果将资源从一个对象转移到另一个对象有一定好处,则移动很有用。例如,移动对于std::string很有用,因为它允许从临时对象复制对象,而不必重新分配和复制其内部资源,而只需将资源从一个移动到另一个。许多类型都会从中受益。另一方面,移动对std::unique_ptr很有用,因为它是在不违反其“唯一所有权”的情况下通过价值传递std::unique_ptr的唯一方法。

  2. 什么时候应该使用复制语义?

    同样,我认为你的意思是,“我应该什么时候给我的课一个复制构造函数?”每当你需要能够复制一个对象时,在它们之间复制内部资源,并且默认的拷贝构造函数不能正确地完成工作。复制对于几乎任何类型都很有用,除了必须对内部资源强制执行唯一所有权的std::unique_ptr之外。

  3. 我是否应该同时使用两者?

    你的类在大多数情况下都应该提供复制和移动语义。最常见的类应该是可复制和可移动的。可复制提供了按值传递对象的标准语义。可移动允许通过价值传递临时对象时获得的优化。这是否意味着必须提供副本或移动构造函数取决于默认构造函数是否执行相应的操作。

  4. 我是否应该使用none并依赖默认的复制构造函数和赋值运算符?

    默认的复制和移动构造函数分别对类的每个成员进行复制或移动。如果这种行为适合复制和移动你的课程,那很好。大多数情况下,这应该足够好。例如,如果我有一个包含std::string的类,默认的复制构造函数将复制字符串,而默认的移动构造函数将字符串的资源移动到新对象 - 都执行相应的作业。如果您的课程包含std::unique_ptr,则复制将无法正常工作,并且您的课程只能移动。这可能是你想要的,或者你可能想要实现一个复制构造函数来执行资源的深层副本。你应该实现复制/移动构造函数的最重要的情况是你的类自己执行资源管理(例如使用newdelete)。如果是这样的话,默认的构造函数几乎从不会很好地管理这些资源。

这里的一切都与赋值运算符类似。