可克隆层次结构中的C ++副本构造函数和赋值

时间:2018-03-01 16:02:00

标签: c++ inheritance cloning

通过指向clone类的指针复制Derived对象有众所周知的Base习惯用法。

class Base{
  int b;
public: 
  virtual unique_ptr<Base> clone() const = 0;
  virtual ~Base() = default;
};

class Derived : public Base {
  int d;
public:
  virtual unique_ptr<Base> clone() const override {
    return std::make_unique<Derived>(*this);
  }
}

但是,在这种情况下,我找不到如何定义复制构造函数和赋值的明确说明。这就是我认为它应该在Base类:

中完成的方式
class Base {
protected:
  Base(const Base&) = default;
private:
  Base& operator=(const Base&) = delete;
}

是否有必要(为了避免潜在的切片)?这是正确的方法吗?是否足够或者我是否应该将此类声明添加到Derived类?

2 个答案:

答案 0 :(得分:0)

由于派生类使用复制构造函数来创建克隆,因此您可能希望将复制构造函数设置为非公共,以避免意外切片但可以对派生类进行访问。

protected符合此要求。它需要应用于每个类的复制构造函数,因为编译器生成的复制构造函数是public。将相同的处理应用于赋值运算符也是有意义的。

这也阻止std::make_unique访问复制构造函数:

class A
{
protected:
    A(A const&) = default;
    A& operator=(A const&) = default;
public:
    A();
    virtual std::unique_ptr<A> clone() const = 0;
};

class B : public A
{
protected:
    B(B const&) = default;
    B& operator=(B const&) = default;
public:
    B();

    std::unique_ptr<A> clone() const override { 
        return std::unique_ptr<A>(new B(*this)); 
    }
};

答案 1 :(得分:0)

除非您需要,否则删除复制赋值运算符可能是一个好主意。

operator=(const Base&)中删除Base就足够了,因为如果基类没有复制赋值运算符,则隐式声明的复制赋值运算符被定义为已删除的派生类(请参阅cppreference.com

如果您真的想要复制赋值,可以将复制赋值运算符设置为虚拟,并通过

小心地在派生类中实现正确的行为
  1. 调用Base::operator=分配基类成员,
  2. 使用dynamic_cast分配派生类的成员,以确保参数的类型正确。
  3. 如果操作正确,这可以避免对象切片并保留正确的类型。

    示例(省略了复制构造函数详细信息):

      struct Point {
        virtual Point& operator=(const Point& p) =default;
        int x;
        int y;
      };  
    
      struct Point3d :public Point{
        virtual Point3d& operator=(const Point& p);
        int z;
      };
    
      Point3d& Point3d::operator=(const Point& p)
      {
        Point::operator=(p);
        auto p3d = dynamic_cast<const Point3d*>(&p);
        if(p3d){
          z = p3d->z; 
        } else {
          z = 0;
        }
        return *this;
      }