Ответ 1
Как автор слайдов я попробую уточнить.
Если вы пишете код, явно выделяя экземпляр Derived
с помощью new
и уничтожая его с помощью delete
с помощью указателя базового класса, тогда вам нужно определить деструктор virtual
, иначе вы закончите с неполным уничтожением Derived
. Однако я рекомендую полностью воздержаться от new
и delete
и использовать исключительно shared_ptr
для обращения к выделенным кучи полиморфным объектам, например
shared_ptr<Base> pb=make_shared<Derived>();
Таким образом, общий указатель отслеживает используемый исходный деструктор, даже если shared_ptr<Base>
используется для его представления. Однажды, последняя ссылка shared_ptr
выходит за пределы области видимости или будет reset, ~Derived()
будет вызвана и память будет выпущена. Поэтому вам не нужно делать ~Base()
virtual.
unique_ptr<Base>
и make_unique<Derived>
не предоставляют эту функцию, поскольку они не обеспечивают механику shared_ptr
относительно делетера, поскольку уникальный указатель намного проще и нацелен на наименьшие служебные данные и, следовательно, не является сохраняя дополнительный указатель на функцию, необходимый для делетера. С unique_ptr
функция делетера является частью типа, и, следовательно, uniqe_ptr с делетером, ссылающимся на ~Derived
, не будет совместим с unique_ptr<Base>
с использованием дедулятора по умолчанию, что в любом случае было бы неверным для производного экземпляра, если ~Base
не был виртуальным.
Индивидуальные предложения, которые я делаю, должны быть легко следовать и следовать всем вместе. Они пытаются создать более простой код, позволяя всем управления ресурсами выполнять компоненты библиотеки и генерируемый компилятором код.
Определение (виртуального) деструктора в классе запретит оператор-конструктор/присваивание, предоставленный компилятором, и может запретить также предоставленный компилятором экземпляр-конструктор/оператор присваивания в будущих версиях С++. Воскрешение их стало проще с =default
, но все же выглядит как много шаблонов. И лучший код - это код, который вам не нужно писать, потому что он не может быть неправильным (я знаю, что все еще есть исключения из этого правила).
Подводя итог "Не определяйте (виртуальный) деструктор" как следствие моего "Правила нуля":
Всякий раз, когда вы разрабатываете иерархию классов полиморфных (OO) в современном С++ и хотите/должны выделять свои экземпляры в куче и обращаться к ним через указатель базового класса, используйте make_shared<Derived>()
для их создания и shared_ptr<Base>
, чтобы сохранить их вокруг, Это позволяет сохранить "Правило нуля".
Это не означает, что вы должны выделять все полиморфные объекты в куче. Например, определение функции, принимающей параметр (Base&)
as, может быть вызвано с локальной переменной Derived
без проблем и будет вести себя полиморфно относительно виртуальных функций-членов Base
.
По моему мнению, динамический полиморфизм ОО сильно используется во многих системах. Мы не должны программировать, как Java, когда мы используем С++, если у нас нет проблемы, где правильный полиморфизм с выделенными кучами объектами является правильным решением.