复制基类的构造函数

时间:2018-05-18 21:37:23

标签: c++ copy move

cpp核心指南中的列表C.67说:基类应该禁止复制,如果"复制"则提供虚拟克隆。是理想的。

如果在基础中将复制构造函数定义为已删除,则对基类和所有派生类也会禁止移动操作。

另一方面,移动操作可以提高性能。我的问题是,当我们设计一个类层次结构时,我们应该采用什么样的现实方法?

假设我们有以下类层次结构?我们应该如何设计A和B来正确支持复制和移动操作。

class A{
public:
  A(const std::string& as) = deleted;
  //should we define other copy/move operators?

  virtual void foo();//
  virtual ~A();//

private:
  std::string s;
};

class B: public A{
public:
  //how do we define copy/move operators?

  void foo() override;
  ~B() override;
private:
  std::vector<std::string> vs;
};

1 个答案:

答案 0 :(得分:0)

的困惑

复制 vs。 clone

首先,请注意clone不是“多态类型对象的副本”:它们只是具有不同语义的不同操作。

  • 复制(通过复制构造函数)表示“使用另一个创建静态指定类型的对象”。 (回想一下构造函数不能是virtual,因为缺少当前对象,其类可以提供行为。)用户必须指定类型,并且应该期望复制的对象被“重新解释”(切片)为已知的,指定的类型,如果它实际上是派生类型。
  • clone动态已知派生类的对象复制为 类的另一个对象。由于参数确定结果的类型,因此用户不能指定它,并且实际上不(静态地)知道所选择的内容。 (堆分配是必然结果。)

您想要哪一个取决于您希望结果具有的生命周期和类型(包括“与该类型相同的类型”作为选择)。我发现有人会写一个副本(例如,一个指定的类型是具体基类的按值参数)并且对他们选择的内容感到惊讶。

虚拟作业

接下来,请注意,除了赋值(必须通过引用)之外,抽象类不必担心切片。赋值可以是virtual(因为对象已经存在),但它不能静态地避免切片,因为类型不需要匹配:

struct B {
  virtual ~B()=default;
  virtual B& operator=(const B&)=default;
  // ...
};
struct D1 : B {
  D& operator=(const B&) override;
  // ...
};
struct D2 : B {/* ... */};

void f() {
  B &&b=D1();
  b=D2();  // ok
}

作业必须只使用共同的B部分,或者......抛出?如果赋值失败,则将其作为函数提供将更加清晰:如果类型不同,可能bool assign_like(const B&) &;返回false

保护

因此,如果我们想避免切片的风险,我们确实必须至少做一些关于任务的事情。删除赋值运算符的核心指南思想是合理的,但我只想在每个抽象基类中使它protected

混凝土仅留下

如果你从不从具体类继承,那就是你需要防止切片的所有内容:叶子类中的隐式(public)特殊成员函数不切片,自动使用base的相应SMF,可以根据需要自动使用。 (例如,具体类可以按值传递。)

“深层”层次结构

在具体的基类中,每个SMF有两个选择:

  1. 无论如何都要protected否认故意复制对象的可能性(即使源的完整对象类型是静态知道的)。
  2. 让它可用并在此处发送类层次结构的任何混淆用户,以了解副本与clone之间的区别(以及“完全virtual”分配的不可能性。)