Виртуальный деструктор базового класса - правило пяти?

У меня есть база State класс интерфейса с виртуальным деструктором по умолчанию.

class State {
public:
    virtual void event() = 0;

    virtual ~State() = default; // relevant part

    virtual void onCreate() {}
    virtual void onDestroy() {}
    virtual void onActivate() {}
    virtual void onDeactivate() {}
};

А потом унаследованные от него классы:

class GameState : public State {
public:
    void event() override;
    // ...
};

class MenuState : public State {
public:
    void event() override;
    // ...
};

Компилятор генерирует операции перемещения по умолчанию, если ни одна операция копирования или деструктор не определены пользователем. Компилятор генерирует операции копирования по умолчанию, если никакая операция перемещения не определена пользователем.

  1. Правильно ли я, объявив виртуальный деструктор по умолчанию, я фактически удалил операции перемещения по умолчанию?

  2. Будут ли операции перемещения работать для производных классов, если базовый класс неявно удалил свои операции перемещения, а базовый класс представляет собой просто интерфейс без членов данных?

  3. Разве здесь действительно разумно следовать Правилу пяти? Явное удаление или использование по умолчанию всех 5 специальных функций-членов кажется довольно непосильной задачей.

1 ответ

Предположим, что ваш базовый класс имеет конструкторы копирования (и, возможно, перемещения) и операторы присваивания. Чего бы вы ожидали в этом случае?

      MenuState menuState;
State state = menuState; // be aware that this State object is not a MenuState any more

Если ваш класс абстрактный (например, еслиonActivate()был чисто виртуальным), выше не имело бы никакого смысла, поскольку экземпляр не был бы разрешен.

Но даже если нет, любойMenuStateчлены будут нарезаны, когда вы поместите их вStateобъект. Если экземпляры вашего базового класса сами по себе не имеют особого смысла, лучше удалить конструкторы копирования и перемещения, а также операторы присваивания.

Для производных (конкретных) классов копирование или перемещение может иметь больше смысла. В этом случае может быть полезно иметь в базовом классе конструкторы копирования и операторы присваивания. Таким образом, вы не можете создать экземпляр самого базового класса, но ваши производные классы могут его использовать.

      MenuState menuState;
MenuState copy = menuState; // might be useful

Если, с другой стороны, базовый класс имеет смысл сам по себе (и, возможно,default) Конструкторы копирования и перемещения и операторы присваивания могут быть разумными. Это может произойти в том случае, если производные классы просто добавляют некоторую дополнительную информацию, которая не требуется при преобразовании производных объектов в базовые объекты.

Так что это действительно зависит, и поэтому лучше явно указать, что предполагается: копировать и перемещать, копировать и перемещать или не копировать вообще.

Однако такие иерархии классов часто используются с указателями (например,std::unique_ptrилиstd::shared_ptr). Затемvirtualметод иногда более полезен.

      auto state = std::make_unique<MenuState>();
std::unique_ptr<State> copy = state->clone();

Однако внутри этот метод может использовать конструктор копирования, который можноpublic(будьте осторожны при нарезке, если ваши производные классы неfinal) илиprotected(если вы хотите убедиться, что толькоclone()используется метод).

Другие вопросы по тегам