与智能指针相关的c++移动语义与复制构造函数和赋值操作符

C++ Move Semantics vs Copy Constructor and Assignment Operator in relation to Smart Pointers

本文关键字:语义 复制 构造函数 赋值操作符 移动 c++ 智能 指针      更新时间:2023-10-16

我想弄清楚什么时候使用移动语义,什么时候使用复制构造函数和赋值运算符作为经验法则。你在类中使用的指针类型(如果有的话)似乎受到这个答案的影响,所以我包含了这个。

没有指针 -根据这个答案,如果您有一个具有int和string等基本类型的POD类,则不需要编写自定义移动或复制构造函数和操作符。

unique-ptr -基于这个答案,当使用move语义时,unique_ptr比shared_ptr更适合,因为资源只能有一个unique_ptr。

shared_ptr -同样,如果使用复制语义,shared_ptr似乎是可行的。对象可以有多个副本,所以拥有一个指向资源的共享指针对我来说是有意义的。然而,unique_ptr通常比shared_ptr更可取,所以如果可以的话,请避免使用该选项。

但:

  1. 我什么时候应该使用移动语义?
  2. 我什么时候应该使用复制语义?
  3. 我应该两者都用吗?
  4. 我应该使用none并依赖默认的复制构造函数和赋值操作符吗?

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

顾名思义,shared_ptr表示资源的共享所有权。然而,这两个智能指针之间还有另一个区别:unique_ptrDeleter是其类型签名的一部分,但它不是shared_ptr的类型签名的一部分。这是因为shared_ptr使用"类型擦除"来擦除删除器的"类型"。还要注意,shared_ptr也可以移动来转移所有权(像unique_ptr一样)

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

虽然shared_ptr可以被复制,但当你进行所有权转移时(而不是创建一个新的引用),你可能想要移动它们。你有义务对unique_ptr使用move语义,因为所有权必须是唯一的。

何时应该使用复制语义?

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

我应该两者都用吗?

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

应该使用none并依赖默认的复制构造函数和赋值操作符吗?

当你想对一个对象进行逐个成员的复制时。

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

    我想你的意思是,"我应该什么时候给我的类一个move构造函数?"答案是每当移动这种类型的对象是有用的,而默认的移动构造函数不能正确地完成这项工作时。当将资源从一个对象转移到另一个对象时,移动是有用的。例如,移动对std::string很有用,因为它允许从临时对象复制对象,而无需重新分配和复制其内部资源,而只需将资源从一个对象移动到另一个对象。许多类型将从中受益。另一方面,移动对std::unique_ptr很有用,因为这是唯一一种通过值传递std::unique_ptr而不违反其"唯一所有权"的方法。

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

    再一次,我假定你的意思是,"我什么时候应该给我的类一个复制构造函数?"当您需要能够复制对象时,其中内部资源在它们之间进行复制,并且默认复制构造函数不能正确地完成这项工作。复制对于几乎任何类型都是有用的,除了像std::unique_ptr这样必须对内部资源强制唯一所有权的类型。

  3. 我应该两者都用吗?

    在大多数情况下,你的类应该同时提供复制和移动语义。最常见的类应该是可复制和可移动的。可复制提供了按值传递对象的标准语义。可移动允许按值传递临时对象时可以获得的优化。这是否意味着必须提供复制或移动构造函数取决于默认构造函数是否做适当的事情。
  4. 我应该使用none并依赖默认的复制构造函数和赋值操作符吗?

    默认的copy和move构造函数只是分别复制或移动类的每个成员。如果这种行为适合复制和移动类,那就太好了。大多数情况下,这就足够了。例如,如果我有一个包含std::string的类,默认的复制构造函数将复制字符串,默认的移动构造函数将把字符串的资源移动到新对象中——两者都做适当的工作。如果您的类包含std::unique_ptr,则复制将无法工作,并且您的类将只能移动。这可能是您想要的,或者您可能想要实现一个复制构造函数来执行资源的深度拷贝。应该实现复制/移动构造函数的最重要的情况是当类本身执行资源管理时(例如使用newdelete)。如果是这种情况,默认构造函数几乎永远不会很好地管理这些资源。