【问题标题】:C++ copy constructors and assignments in cloneable hierarchy可克隆层次结构中的 C++ 复制构造函数和赋值
【发布时间】:2018-08-09 18:18:52
【问题描述】:

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

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 类中?

【问题讨论】:

  • 如果您处理指向对象的指针,您将永远不会分配或复制构造函数。
  • 这取决于您当前的用例。如果您只想允许克隆 - 或者克隆和复制都可以接受。为了允许克隆和复制 - 根本不用担心那些东西。对于不允许复制 - 删除你的分配并保护你的复制构造函数 - 就像你展示的那样。
  • 谢谢,已修复。 @Piotr,就我而言,派生类中的复制构造函数会自动受到保护吗?

标签: c++ inheritance cloning


【解决方案1】:

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

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)); 
    }
};

【讨论】:

  • 只保护基类拷贝构造函数就够了吗?
  • protected 需要应用于每个类的复制构造函数。
【解决方案2】:

删除复制赋值运算符可能是个好主意,除非你需要它。

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

如果你真的想要复制赋值,你可以将复制赋值操作符设为虚拟,并在派生类中仔细实现正确的行为

  1. 调用Base::operator= 分配基类成员,并且
  2. 分配派生类的成员,使用dynamic_cast 确保参数的类型正确。

如果操作正确,这将避免对象切片并保留正确的类型。

一个例子(省略了复制构造函数的细节):

  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;
  }

【讨论】:

    猜你喜欢
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-20
    • 2016-09-13
    • 2020-06-13
    相关资源
    最近更新 更多