【问题标题】:C++ Move Semantics vs Copy Constructor and Assignment Operator in relation to Smart Pointers与智能指针相关的 C++ 移动语义与复制构造函数和赋值运算符
【发布时间】:2014-09-14 07:30:01
【问题描述】:

作为经验法则,我试图弄清楚何时使用移动语义以及何时使用复制构造函数和赋值运算符。您在课堂上使用的指针类型(如果有)似乎受此答案的影响,因此我已将其包括在内。

无指针 - 基于this 的回答,如果您有一个具有 int 和 string 等原始类型的 POD 类,则无需编写自定义移动或复制构造函数和运算符。

unique-ptr - 基于this 的回答,当使用移动语义时,unique_ptr 比 shared_ptr 更适合,因为资源只能有一个 unique_ptr。

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

但是:

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

【问题讨论】:

    标签: c++ pointers c++11 smart-pointers move-semantics


    【解决方案1】:

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

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

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

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

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

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

    我应该同时使用两者吗?

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

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

    当您想要制作对象的逐个成员副本时。

    【讨论】:

      【解决方案2】:
      1. 什么时候应该使用移动语义?

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

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

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

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

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

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

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

      这里的一切都同样适用于赋值运算符。

      【讨论】:

        猜你喜欢
        • 2019-09-29
        • 2013-09-28
        • 1970-01-01
        • 2013-03-16
        • 2023-04-01
        • 1970-01-01
        • 2012-10-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多