Подтвердить что ты не робот

Виртуальный деструктор с виртуальными членами на С++ 11

В этих слайдах о стандарте С++ 11/14 на слайде 15 автор пишет, что "многие классические правила кодирования [ ] больше не применимо" в С++ 11. Он предлагает список из трех примеров, и я согласен с Правилом Три и управлением памятью.

Однако его вторым примером является "Виртуальный деструктор с виртуальными членами" (только это). Что это значит? Я знаю, что нужно объявить виртуальным деструктором базового класса, чтобы вызвать правильный деструктор, если у нас есть что-то вроде

Base *b = new Derived;
...
delete b;

Это объясняется здесь: Когда использовать виртуальные деструкторы?

Но неужели теперь в С++ 11 бесполезно объявлять виртуальный деструктор, если у вас есть виртуальные участники?

4b9b3361

Ответ 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, когда мы используем С++, если у нас нет проблемы, где правильный полиморфизм с выделенными кучами объектами является правильным решением.

Ответ 2

Я думаю, что это связано с "правилом нуля", упомянутым в другом месте презентации.

Если у вас есть только автоматические переменные-члены (например, используйте shared_ptr или unique_ptr для членов, которые в противном случае были бы необработанными указателями), вам не нужно писать собственную копию или перемещать конструкторы или операторы присваивания - оптимальные параметры по умолчанию для компилятора. При инициализации в классе вам также не нужен конструктор по умолчанию. И, наконец, вам вообще не нужно писать деструктор, виртуальный или нет.

Ответ 3

Связанная бумага показывает соответствующий код:

std::unique_ptr<Derived> { new Derived };

Сохраненный дебетер std::default_delete<Derived>, для которого не требуется Base::~Base.

Теперь вы можете переместить это на unique_ptr<Base>, и он также переместит std::default_delete<Derived>, не преобразовывая его в std::default_delete<Base>. Забастовкa >