У меня есть четкое понимание большинства ОО-теорий, но меня смущает одна вещь - виртуальные деструкторы.
Я думал, что деструктора всегда называют независимо от того, что и для каждого объекта в цепочке.
Когда вы собираетесь сделать их виртуальными и почему?
virtual
убедитесь, что он начинается сверху, а не посередине.Ответы:
Виртуальные деструкторы полезны, когда вы потенциально можете удалить экземпляр производного класса через указатель на базовый класс:
Здесь вы заметите, что я не объявил деструктором Базы
virtual
. Теперь давайте посмотрим на следующий фрагмент:Поскольку деструктор Base не является
virtual
иb
являетсяBase*
ссылающийся наDerived
объект,delete b
имеет неопределенное поведение :В большинстве реализаций вызов деструктора будет разрешаться, как и любой не виртуальный код, то есть будет вызываться деструктор базового класса, но не деструктор производного класса, что приводит к утечке ресурсов.
Подводя итог, всегда делайте деструкторы базовых классов,
virtual
когда они предназначены для полиморфного манипулирования.Если вы хотите предотвратить удаление экземпляра с помощью указателя базового класса, вы можете сделать деструктор базового класса защищенным и не виртуальным; при этом компилятор не позволит вам вызвать
delete
указатель на базовый класс.Вы можете узнать больше о виртуальности и виртуальном деструкторе базового класса в этой статье от Херба Саттера .
источник
Base
иDerived
есть все переменные автоматического хранения? т.е. нет никакого «специального» или дополнительного пользовательского кода для выполнения в деструкторе. Можно ли вообще писать какие-либо деструкторы? Или у производного класса все еще будет утечка памяти?Виртуальный конструктор невозможен, но возможен виртуальный деструктор. Давайте поэкспериментируем .......
Приведенный выше код выдает следующее:
Конструкция производного объекта следует правилу конструирования, но когда мы удаляем указатель «b» (базовый указатель), мы обнаруживаем, что вызывается только базовый деструктор. Но этого не должно произойти. Чтобы сделать это правильно, мы должны сделать базовый деструктор виртуальным. Теперь давайте посмотрим, что происходит в следующем:
Вывод изменился следующим образом:
Таким образом, уничтожение базового указателя (который занимает выделение для производного объекта!) Следует правилу уничтожения, то есть сначала производному, а затем базовому. С другой стороны, нет ничего похожего на виртуальный конструктор.
источник
Объявить деструкторы виртуальными в полиморфных базовых классах. Это пункт 7 в « Эффективном C ++» Скотта Мейерса . Далее Майерс резюмирует, что если у класса есть какая-либо виртуальная функция, у него должен быть виртуальный деструктор, и что классы, не предназначенные для того, чтобы быть базовыми классами или не предназначенные для полиморфного использования, не должны объявлять виртуальные деструкторы.
источник
const Base& = make_Derived();
. В этом случаеDerived
будет вызван деструктор prvalue, даже если он не виртуальный, поэтому сохраняются накладные расходы, представленные vtables / vpointers. Конечно сфера довольно ограничена. Андрей Александреску упомянул об этом в своей книге « Современный дизайн C ++» .Также помните, что удаление указателя базового класса при отсутствии виртуального деструктора приведет к неопределенному поведению . То, что я узнал совсем недавно:
Как должно переопределять удаление в C ++?
Я использую C ++ в течение многих лет, и мне все еще удается повеситься.
источник
Сделайте деструктор виртуальным всякий раз, когда ваш класс полиморфен.
источник
Вызов деструктора через указатель на базовый класс
Вызов виртуального деструктора ничем не отличается от вызова любой другой виртуальной функции.
Поскольку
base->f()
вызов будет отправленDerived::f()
, и он будет таким же, как и дляbase->~Base()
его главной функции,Derived::~Derived()
будет вызван вызов.То же самое происходит, когда деструктор вызывается косвенно, например
delete base;
. Вызоветdelete
заявление,base->~Base()
которое будет отправленоDerived::~Derived()
.Абстрактный класс с не виртуальным деструктором
Если вы не собираетесь удалять объект через указатель на его базовый класс - тогда нет необходимости иметь виртуальный деструктор. Просто сделайте
protected
так, чтобы он не был вызван случайно:источник
~Derived()
во всех производных классах, даже если это просто~Derived() = default
? Или это подразумевается языком (делая его безопасным опускать)?protected
раздел, или чтобы убедиться, что это виртуально с помощьюoverride
.Мне нравится думать об интерфейсах и реализациях интерфейсов. В C ++ говорят, что интерфейс является чисто виртуальным классом. Деструктор является частью интерфейса и должен быть реализован. Поэтому деструктор должен быть чисто виртуальным. Как насчет конструктора? Конструктор фактически не является частью интерфейса, потому что объект всегда создается в явном виде.
источник
virtual
в базовом классе, он автоматически попадаетvirtual
в производный класс, даже если он не объявлен таковым.Виртуальное ключевое слово для деструктора необходимо, когда вы хотите, чтобы различные деструкторы следовали в правильном порядке, пока объекты удаляются через указатель базового класса. например:
Если деструктор базового класса является виртуальным, то объекты будут уничтожаться в порядке (сначала производный объект, затем базовый). Если деструктор базового класса НЕ является виртуальным, тогда будет удален только объект базового класса (поскольку указатель имеет базовый класс "Base * myObj"). Таким образом, будет утечка памяти для производного объекта.
источник
Проще говоря, виртуальный деструктор должен уничтожать ресурсы в правильном порядке, когда вы удаляете указатель базового класса, указывающий на объект производного класса.
источник
delete
базового указателя приводит к неопределенному поведению.Деструкторы виртуальных базовых классов - это «лучшая практика» - вы всегда должны использовать их, чтобы избежать (трудно обнаружить) утечек памяти. Используя их, вы можете быть уверены, что все деструкторы в цепочке наследования ваших классов вызываются (в правильном порядке). Наследование от базового класса с использованием виртуального деструктора делает деструктор унаследованного класса также автоматически виртуальным (поэтому вам не нужно повторно вводить «виртуальный» в объявлении деструктора унаследованного класса).
источник
Если вы используете
shared_ptr
(только shared_ptr, а не unique_ptr), вам не нужно иметь виртуальный деструктор базового класса:вывод:
источник
virtual
ключевое слово может спасти вас от многих мучений.Что такое виртуальный деструктор или как использовать виртуальный деструктор
Деструктор класса - это функция с тем же именем класса, которому предшествует ~, которая перераспределяет память, выделенную классом. Зачем нам нужен виртуальный деструктор
Смотрите следующий пример с некоторыми виртуальными функциями
В примере также рассказывается, как можно преобразовать букву в верхнюю или нижнюю
Из приведенного выше примера видно, что деструктор для классов MakeUpper и MakeLower не вызывается.
Смотрите следующий пример с виртуальным деструктором
Виртуальный деструктор явно вызовет самый производный деструктор времени выполнения класса, чтобы он мог правильно очистить объект.
Или перейдите по ссылке
https://web.archive.org/web/20130822173509/http://www.programminggallery.com/article_details.php?article_id=138
источник
когда вам нужно вызвать деструктор производного класса из базового класса. вам нужно объявить виртуальный деструктор базового класса в базовом классе.
источник
Я думаю, что суть этого вопроса - виртуальные методы и полиморфизм, а не деструктор. Вот более понятный пример:
Распечатает:
Без
virtual
него распечатают:И теперь вы должны понимать, когда использовать виртуальные деструкторы.
источник
B b{}; A& a{b}; a.foo();
. ПроверкаNULL
- что должно бытьnullptr
- доdelete
- с неправильной неопределенностью - не требуется:delete nullptr;
определяется как неиспользование . Во всяком случае, вы должны были проверить это перед вызовом->foo()
, в противном случае неопределенное поведение может произойти, если что-new
то не получится.)delete
наNULL
указатель (то есть, вам не нуженif (a != NULL)
охранник).Я подумал, что было бы полезно обсудить «неопределенное» поведение или, по крайней мере, «аварийное» неопределенное поведение, которое может возникнуть при удалении через базовый класс (/ struct) без виртуального деструктора или, точнее, без vtable. Код ниже перечисляет несколько простых структур (то же самое будет верно для классов).
Я не предполагаю, нужны ли вам виртуальные деструкторы или нет, хотя я думаю, что в общем случае иметь их стоит. Я просто указываю причину, по которой вы можете столкнуться с падением, если ваш базовый класс (/ struct) не имеет vtable, а ваш производный класс (/ struct) имеет, и вы удаляете объект через базовый класс (/ struct) указатель. В этом случае адрес, который вы передаете свободной подпрограмме кучи, является недействительным и, следовательно, причиной сбоя.
Если вы запустите приведенный выше код, вы ясно увидите, когда возникнет проблема. Когда указатель this базового класса (/ struct) отличается от указателя this производного класса (/ struct), вы столкнетесь с этой проблемой. В приведенном выше примере struct a и b не имеют vtables. Структуры C и D имеют Vtables. Таким образом, указатель a или b на экземпляр объекта ac или d будет зафиксирован для учета виртуальной таблицы. Если вы передадите указатель a или b для удаления, произойдет сбой из-за того, что адрес является недопустимым для свободной подпрограммы кучи.
Если вы планируете удалять производные экземпляры, которые имеют vtables из указателей базового класса, вам необходимо убедиться, что базовый класс имеет vtable. Один из способов сделать это - добавить виртуальный деструктор, который в любом случае может потребоваться для правильной очистки ресурсов.
источник
Основное определение о
virtual
том, что оно определяет, может ли функция-член класса быть переопределена в его производных классах.D-tor класса вызывается в основном в конце области, но есть проблема, например, когда мы определяем экземпляр в куче (динамическое выделение), мы должны удалить его вручную.
Как только инструкция выполняется, вызывается деструктор базового класса, но не для производного.
Практический пример - когда в области управления вам приходится манипулировать эффекторами, исполнительными механизмами.
В конце области действия, если деструктор одного из силовых элементов (Привод) не вызван, будут фатальные последствия.
источник
Любой класс, который наследуется публично, полиморфный или нет, должен иметь виртуальный деструктор. Иными словами, если на него может указывать указатель базового класса, его базовый класс должен иметь виртуальный деструктор.
Если виртуальный, вызывается деструктор производного класса, то конструктор базового класса. Если не виртуальный, вызывается только деструктор базового класса.
источник