Урок №165. Виртуальные деструкторы и Виртуальное присваивание

  Юрий  | 

  |

  Обновл. 15 Сен 2021  | 

 44113

 ǀ   8 

Хотя язык C++ автоматически предоставляет деструкторы для ваших классов, если вы не предоставляете их самостоятельно, все же иногда вы можете сделать это сами.

Виртуальные деструкторы

При работе с наследованием ваши деструкторы всегда должны быть виртуальными. Рассмотрим следующий пример:

Поскольку parent является указателем класса Parent, то при его уничтожении компилятор будет смотреть, является ли деструктор класса Parent виртуальным. Поскольку это не так, то компилятор вызовет только деструктор класса Parent.

Результат выполнения программы:

Calling ~Parent()

Тем не менее, нам нужно, чтобы delete вызывал деструктор класса Child (который, в свою очередь, будет вызывать деструктор класса Parent), иначе m_array не будет удален. Это можно выполнить, сделав деструктор класса Parent виртуальным:

Результат выполнения программы:

Calling ~Child()
Calling ~Parent()

Правило: При работе с наследованием ваши деструкторы должны быть виртуальными.

Виртуальное присваивание


Оператор присваивания можно сделать виртуальным. Однако, в отличие от деструктора, виртуальное присваивание не всегда является хорошей идеей. Почему? Это уже выходит за рамки этого урока. Следовательно, для сохранения простоты в вашем коде, не рекомендуется использовать виртуальное присваивание.

Игнорирование виртуальных функций

В языке С++ мы можем игнорировать вызов переопределений. Например:

Здесь мы хотим, чтобы ссылка класса Parent на объект класса Child вызывала Parent::getName() вместо Child::getName(). Чтобы это сделать, нужно просто использовать оператор разрешения области видимости:

Вы, скорее всего, не будете использовать это очень часто, но знать об этом стоит.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (198 оценок, среднее: 4,83 из 5)
Загрузка...

Комментариев: 8

  1. Константин:

    Однако, в отличие от деструктора, виртуальное присваивание не всегда является хорошей идеей. Почему? Это уже выходит за рамки этого урока.

    Хотелось бы поподробнее узнать, о причинах или получить ссылку на источник, где рассказано об этом, так как уже было с выравниванием данных, а тут просто сказали "это плохо, всё идём дальше", без объяснения причин.

  2. Сергей:

    Не совсем понятно зачем делать деструктор класса Child виртуальным.
    В данном примере у него нет наследников.

    1. Aleksandr:

      Если тут как с методами, то деструктор класса-наследника будет неявно виртуальным, так как является переопределением виртуального деструктора в классе-родителе, ключевое слово указывается, чтобы не забыть это.

    2. Белая Кепка:

      Если у тебя есть переменная типа «указатель на базовый класс», которому ты присваиваешь адрес переменной типа «дочерний класс», то при удалении переменной базового типа, у тебя удалится только данные, к которым ты имеешь доступ через базовый указатель. В это же время, у тебя в памяти остается дочерний класс, и в нем уже может лежать мусор или что-то еще.
      В С++ не просто так деструкторы вызываются в обратном порядке (от дочернего к базовому), это сделано с целью защиты от утечек и защиты от обращения к памяти, которая тебе уже не принадлежит. Поэтому, если ты сделаешь деструкторы виртуальными, то компилятор, при удалении базового указателя, сначала увидит, что указатель указывает на дочерний класс, а у базового класса есть виртуальный деструктор, поэтому он сначала найдет самый дочерний деструктор, освободит в нем память и поползет вверх по иерархии, поочередно очищая память. Виртуальный деструктор — это гарант безопасности и отсутствие непреднамеренных утечек памяти (за исключением того случая, когда ты плохой программист и делаешь то, что делать вообще не стоит) Надеюсь я внес ясность. Успехов!

  3. Алексей:

    Строго говоря, при удалении наследника при помощи delete с указателем на базовый класс будет неопределённое поведение. То, что вызывается деструктор базового класса — лишь один из вариантов проявления неопределённого поведения.

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

    1. Леонид К.:

      Тогда на этапе компиляции будет ошибка:
      1>error C2248: 'Parent::~Parent': cannot access protected member declared in class 'Parent'

      Т.е. это защита от неправильного использования указателя?

      Нужно явно вызывать

  4. Just a guy from Dagestan:

    Классный сайт! Спасибо, что объясняете всё понятно и бесплатно!

    1. Фото аватара Юрий:

      Пожалуйста 🙂

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *