Наследуются ли виртуальные деструкторы?


Если у меня есть базовый класс с виртуальным деструктором. Есть ли производный класс для объявления виртуального деструктора тоже?

class base {
public:
    virtual ~base () {}
};

class derived : base {
public:
    virtual ~derived () {} // 1)
    ~derived () {}  // 2)
};

конкретные вопросы:

  1. это 1) и 2) то же самое? Является ли 2) автоматически виртуальным из-за своей базы или он "останавливает" виртуальность?
  2. можно ли опустить производный деструктор, если он не имеет ничего общего?
  3. какова наилучшая практика для объявления производного деструктора? Объявите его виртуальным, невиртуальным или опустите если это возможно?
4 70

4 ответа:

  1. Да, они одинаковы. Производный класс, не объявляющий что-то виртуальное, не мешает ему быть виртуальным. Фактически, нет способа остановить любой метод (включая деструктор) от виртуального в производном классе, если он был виртуальным в базовом классе. В >=C++11 вы можете использовать final чтобы предотвратить его переопределение в производных классах, но это не мешает ему быть виртуальным.
  2. да, деструктор в производном классе может быть опущен, если он не имеет ничего общего делать. И не имеет значения, является ли он виртуальным.
  3. Я бы опустил его, если это возможно. И я всегда использую virtual ключевое слово снова для виртуальных функций в производных классах по причинам ясности. Люди не должны проходить весь путь вверх по иерархии наследования, чтобы выяснить, что функция является виртуальной. Кроме того, если ваш класс является копируемым или перемещаемым без необходимости объявлять свои собственные конструкторы копирования или перемещения, объявляя деструктор любого типа (даже если вы определяете его как default) заставит вас объявить конструкторы копирования и перемещения и операторы присваивания, если вы хотите их, поскольку компилятор больше не будет помещать их для вас.

как небольшой пункт для пункта 3. В комментариях было указано, что если деструктор не объявлен, компилятор генерирует значение по умолчанию (которое по-прежнему является виртуальным). И это значение по умолчанию является встроенной функцией.

встроенные функции потенциально подвергают большую часть вашей программы изменениям в других частях вашей программы и сделать двоичную совместимость для общих библиотек сложно. Кроме того, увеличенная связь может привести к большому количеству перекомпиляции перед лицом определенных видов изменений. Например, если вы решите, что вам действительно нужна реализация для вашего виртуального деструктора, то каждый фрагмент кода, который его вызвал, нужно будет перекомпилировать. Тогда как если бы вы объявили его в теле класса, а затем определили его пустым в .cpp файл, который вы бы хорошо изменить его без перекомпилируемой.

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

виртуальная функция-член сделает неявно любую перегрузку этой функции виртуальной.

таким образом, виртуальный в 1) является" необязательным", деструктор базового класса, являющийся виртуальным, также делает все дочерние деструкторы виртуальными.

  1. деструктор автоматически является виртуальным, как и все методы. Вы не можете запретить методу быть виртуальным в C++ (если он уже объявлен виртуальным, то есть нет эквивалента "final" в Java)
  2. да его можно опустить.
  3. Я бы объявил виртуальный деструктор, если я намерен, чтобы этот класс был подклассом, независимо от того, является ли он подклассом другого класса или нет, я также предпочитаю объявлять методы виртуальными, даже если это не нужно. Это позволит поддерживать работу подклассов, если вы когда-нибудь решите удалить наследование. Но я полагаю, что это просто вопрос стиля.

1/ Да 2/ Да, он будет сгенерирован компилятором 3 / выбор между объявлением его виртуальным или нет должен следовать вашему соглашению для переопределенных виртуальных членов -- IMHO, есть хорошие аргументы в обоих направлениях, просто выберите один и следуйте ему.

Я бы опустил его, если это возможно, но есть одна вещь, которая может побудить вас объявить его: если вы используете компилятор, созданный один, он неявно встроен. Есть время, когда вы хотите избежать встроенных членов (динамические библиотеки для экземпляр.)