Унаследованы ли виртуальные деструкторы? - PullRequest
75 голосов
/ 04 февраля 2010

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

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

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

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

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

Ответы [ 4 ]

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

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

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

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

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

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

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

0 голосов
/ 04 февраля 2010

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

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

...