скопировать конструктор на базовый класс - PullRequest
0 голосов
/ 19 мая 2018

Список C.67 в руководстве по ядру cpp гласит: Базовый класс должен подавлять копирование и предоставлять вместо него виртуальный клон, если требуется «копирование».

Если конструктор копирования определен как удаленный в базе, то операции перемещения также подавляются для базового класса и всех производных классов.

С другой стороны, операции перемещения могут повысить производительность.Мой вопрос заключается в том, какой реалистичный подход мы должны принять при разработке иерархии классов?

Предположим, у нас есть следующая иерархия классов?как мы должны спроектировать 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 голосов
/ 19 мая 2018

Путаница

Копировать против клон

Во-первых, обратите внимание, что 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) специальные функции-члены в листовых классах не нарезают , автоматически используют соответствующие SMF-функции базы и, в свою очередь, могут использоваться автоматически по мере необходимости.(Например, конкретные классы могут затем передаваться по значению.)

«Глубокая» иерархия

В конкретном базовом классе у вас есть два варианта для каждого SMF:

  1. Сделайте это protected в любом случае, отрицая возможность умышленного копирования объекта (даже если полный тип объекта источника известен статически).
  2. Оставьте его доступным и отправьте любоесбил с толку пользователей иерархии классов, чтобы узнать о разнице между копией и clone (и невозможностью «полностью virtual» назначения).
...