Переопределение виртуального деструктора по умолчанию

Все знают, что десруктор базового класса обычно должен быть виртуальным. Но как насчет деструктора производного класса? В C++11 у нас есть ключевое слово "override" и возможность явно использовать деструктор по умолчанию.

struct Parent
{
  std::string a;
  virtual ~Parent()
  {
  }

};

struct Child: public Parent
{
  std::string b;
  ~Child() override = default;
};

Правильно ли использовать оба ключевых слова "override" и "=default" в деструкторе класса Child? Будет ли компилятор генерировать правильный виртуальный деструктор в этом случае?

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

6 ответов

Правильно ли использовать оба ключевых слова "override" и "=default" в деструкторе класса Child? Будет ли компилятор генерировать правильный виртуальный деструктор в этом случае?

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

мы можем думать, что это хороший стиль кодирования

Это вопрос предпочтений. Для меня это имеет смысл только в том случае, если тип базового класса является шаблонным: тогда он будет обеспечивать требование к базовому классу иметь виртуальный деструктор. В противном случае, когда базовый тип фиксирован, я бы посчитал такой код шумом. Это не так, как будто базовый класс волшебным образом изменится. Но если у вас есть тупые товарищи по команде, которые любят что-то менять, не проверяя код, который зависит от того, что они могут быть взломаны, лучше оставить определение деструктора в качестве дополнительного уровня защиты.

Согласно CppCoreGuidelines C.128 деструктор производного класса не должен быть объявлен virtual или же override,

Если деструктор базового класса объявлен виртуальным, следует избегать объявления деструкторов производного класса virtual или же override, Некоторые базы кода и инструменты могут настаивать на переопределении для деструкторов, но это не рекомендация этих рекомендаций.

override это не что иное, как сеть безопасности. Деструктор дочернего класса всегда будет виртуальным, если деструктор базового класса является виртуальным, независимо от того, как он объявлен - или не объявлен вообще (т.е. с использованием неявно объявленного).

Существует (как минимум) одна причина для использования override здесь - вы гарантируете, что деструктор базового класса всегда виртуален. Это будет ошибка компиляции, если деструктор производного класса считает, что он что-то переопределяет, но нечего переопределять. Это также дает вам удобное место для хранения сгенерированной документации, если вы делаете это.

С другой стороны, я могу придумать две причины не делать этого:

  • Это немного странно и задом наперед для производного класса навязывать поведение базового класса.
  • Если вы определяете деструктор в заголовке (или делаете его встроенным), вы вводите возможность для нечетных ошибок компиляции. Допустим, ваш класс выглядит так:

    struct derived {
        struct impl;
        std::unique_ptr<derived::impl> m_impl;
        ~derived() override = default;
    };
    

    Вы, вероятно, получите ошибку компилятора, потому что деструктор (который встроен в класс здесь) будет искать деструктор для неполного класса, derived::impl,

    Это мой общий способ сказать, что каждая строка кода может стать обязательством, и, возможно, лучше просто пропустить что-то, если оно функционально ничего не делает. Если вам действительно нужно применить виртуальный деструктор в базовом классе от родительского класса, кто-то предложил использовать static_assert совместно с std::has_virtual_destructor, что даст более последовательные результаты, ИМХО.

Я думаю, что "переопределение" вводит в заблуждение деструктора. Когда вы переопределяете виртуальную функцию, вы заменяете ее. Деструкторы связаны, поэтому вы не можете переопределить деструктор буквально

Ссылка CPP говорит, что override гарантирует, что функция virtual и что это действительно переопределяет виртуальную функцию. Итак override Ключевое слово будет убедиться, что деструктор является виртуальным.

Если вы укажете override но нет = default, тогда вы получите ошибку компоновщика.

Вам не нужно ничего делать. Покидая Child dtor undefined работает просто отлично:

#include <iostream>

struct Notify {
    ~Notify() { std::cout << "dtor" << std::endl; }
};

struct Parent {
    std::string a;
    virtual ~Parent() {}
};

struct Child : public Parent {
    std::string b;
    Notify n;
};

int main(int argc, char **argv) {
    Parent *p = new Child();
    delete p;
}

Что будет выводить dtor, Если вы удалите virtual в Parent::~Parentтем не менее, он не будет ничего выводить, потому что это неопределенное поведение, как указано в комментариях.

Хороший стиль будет не упоминать Child::~Child совсем. Если вы не можете поверить, что базовый класс объявил его виртуальным, то ваше предложение с override а также = default буду работать; Я хотел бы надеяться, что есть лучшие способы гарантировать, что вместо того, чтобы засорять ваш код этими объявлениями деструкторов.

Хотя деструкторы не наследуются, в Стандарте четко указано, что виртуальные деструкторы производных классов имеют приоритет над деструкторами базовых классов.

Из стандарта C++ (10.3 Виртуальные функции)

6 Несмотря на то, что деструкторы не наследуются, деструктор в производном классе переопределяет деструктор базового класса, объявленный виртуальным; см. 12.4 и 12.5.

С другой стороны там тоже написано (9.2 ученик)

8 Virt-specifer-seq должен содержать не более одного из каждого virt-specier. Virt-specifer-seq должен появляться только в объявлении виртуальной функции-члена (10.3).

Хотя деструкторы называются специальными функциями-членами, они также являются функциями-членами.

Я уверен, что стандарт C++ должен быть отредактирован таким образом, чтобы было однозначно, может ли деструктор иметь virt-спецификатор override, В настоящее время это не ясно.

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