наследование — конструкторы копирования C ++ и присваивания в клонируемой иерархии

Там хорошо известно 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 класс тоже?

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

Другие решения

Удаление оператора присваивания копии, вероятно, является хорошей идеей, если вам это не нужно.

Удаление operator=(const Base&) в Base достаточно, так как неявно объявленный оператор присваивания копии определяется как удаленный для производного класса, если в базовом классе нет оператора присваивания копии (см. 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;
}
0