【问题标题】:Making a class non-copyable: Private undefined methods vs Deleted methods使类不可复制:私有未定义方法与已删除方法
【发布时间】:2017-02-14 17:27:49
【问题描述】:

在 C++11 之前,我看到过这样的代码:

class Car {
public:
  Car() {}
private:
  Car(const Car&);
  Car& operator=(const Car&);
};

对于 C++11(及更高版本),我看到这样的代码:

class Car {
public:
  Car() {}
private:
  Car(const Car&) = delete;
  Car& operator=(const Car&) = delete;
};

它们的行为是否相同?如果不是,请解释。

参考:https://ariya.io/2015/01/c-class-and-preventing-object-copy

【问题讨论】:

    标签: c++ c++11 noncopyable


    【解决方案1】:

    它们在大多数方面相似,但在其他一些方面有所不同。

    考虑以下外部代码试图复制类的对象:

    int main() {
        Car c;
        Car other{c};
    }
    

    这两个版本都会导致上述代码失败。不过,此时,开发人员会查看界面以了解原因。对于delete 版本,很明显Car 并不是要被复制的。对于private 版本,(至少没有注释),它提出了一个疑问,复制构造函数是否可能被意外放置在private 部分。

    现在考虑 member 代码试图复制类的对象:

    void Car::foo() {
        Car c;
        Car other{c}; 
    }
    

    delete 版本像以前一样失败。 private 版本是链接错误。这引发了一个更大的疑问——忘记定义已声明的方法并不少见。也许这就是这里发生的事情? delete 版本没有这个问题。

    编辑 Scott Meyers 在Effective Modern C++ 的第 11 项中讨论了这一点比起私有未定义的函数,更喜欢删除的函数

    【讨论】:

    • 很好地参考了 Scott Meyers 项目。我昨晚发现的。
    【解决方案2】:

    =delete 会给出更有意义的错误信息。有关已删除函数的错误消息告诉您它不存在,并且没有人可以使用所述构造函数创建对象。说它是私有的并没有提供该信息 - 它只是说调用者不能调用它,而不是没有人可以。

    此外,如果从类内部/外部调用已删除的构造函数,其行为不会有所不同(因为可以从类内部调用私有构造函数)。

    https://godbolt.org/g/06R9AQ

    【讨论】:

      【解决方案3】:

      请注意,您发布的两个 sn-ps 给出的错误完全相同,类似于:

      'Car(const Car&)' 在此上下文中是私有的

      这是因为您在这两种情况下都将成员方法定义为私有方法。
      如果你想欣赏这些差异,你应该有 public deleted 复制构造函数和复制操作符,即:

      class Car {
      public:
        Car() {}
      // private: <-- this should not be here
        Car(const Car&) = delete;
        Car& operator=(const Car&) = delete;
      };
      

      这样,您将被告知那些成员方法已被有意地删除

      使用已删除的函数'Car(const Car&)'

      将它们设置为私有并没有明确说明我想删除它们
      例如,您可能希望强制您的类的用户使用工厂方法来创建该类的实例。

      无论如何,(不再如此)新功能不是免费的,并且以非预期的方式使用它们不会带来预期的好处

      【讨论】:

      • 将已删除的方法公开以改善编译器错误的好处!
      • @kevinarpe 可访问性首先得到验证。
      【解决方案4】:

      Car&amp; operator=(const Car&amp;) = delete; 明确表示“禁止复制分配”。

      = delete; 也可用于 Bjarne 的blog 中描述的任何功能:

      struct Z {
          // ...
      
          Z(long long);     // can initialize with an long long
          Z(long) = delete; // but not anything less
      };
      

      【讨论】:

      • 我不知道删除可以用于其他目的。很好的补充。
      猜你喜欢
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      • 2022-07-23
      • 2011-01-05
      • 2011-10-18
      • 2012-09-17
      • 2020-12-24
      相关资源
      最近更新 更多