Виртуальный деструктор: нужен ли он когда динамически не выделяется память? - PullRequest
20 голосов
/ 14 января 2010

Нужен ли нам виртуальный деструктор, если мои классы не выделяют память динамически?

, например

class A
{
      private: 
      int a;
      int b;

      public:
      A();
      ~A();
};

class B: public A
{     
      private:
      int c;
      int d;

      public:
      B();
      ~B();
};

В этом случае нам нужно пометить деструктор А как виртуальный?

Ответы [ 6 ]

32 голосов
/ 14 января 2010

Проблема не в том, выделяют ли ваши классы память динамически. Это если пользователь классов выделяет объект B через указатель A, а затем удаляет его:

A * a = new B;
delete a;

В этом случае, если для A нет виртуального деструктора, стандарт C ++ говорит, что ваша программа демонстрирует неопределенное поведение. Это не очень хорошая вещь.

Это поведение указано в разделе 5.3.5 / 3 Стандарта (здесь имеется в виду delete):

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

19 голосов
/ 14 января 2010

Назначение деструктора virtual (т.е. создание деструктора virtual ) состоит в том, чтобы облегчить полиморфное удаление объектов с помощью delete-expression . Если ваш дизайн не требует полиморфного удаления объектов, вам не нужны виртуальные деструкторы. Обращаясь к вашему примеру, если вам когда-либо придется удалять объект типа B через указатель типа A * (полиморфное удаление), вам понадобится виртуальный деструктор, который находится на столь высоком уровне в иерархии, как A. Вот так это выглядит с формальной точки зрения.

(Заметьте, кстати, как сказал Нил, важно то, как вы создаете / удаляете свои объекты классов, а не как классы управляют своей внутренней памятью.)

Что касается хорошей практики программирования ... Это зависит от ваших намерений и вашего дизайна в конце концов. Если ваши классы вообще не предназначены для полиморфизации (никаких виртуальных методов), вам не нужны виртуальные деструкторы. Если ваш класс полиморфен (имеет хотя бы один виртуальный метод), то сделать виртуальный деструктор «на всякий случай» может быть очень хорошей идеей, и в этом случае он практически не имеет производительности / потери памяти.

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

Классы, которые не имеют ресурсов, но могут образовывать полиморфные иерархии, должны всегда определять пустые виртуальные деструкторы, за исключением того, что вполне достаточно определить явный пустой (и даже чистый) виртуальный деструктор в самой основе иерархии. Все остальные деструкторы станут виртуальными автоматически, даже если они неявно определены компилятором. То есть вам не нужно явно определять пустой деструктор в каждом классе. Достаточно базы.

4 голосов
/ 14 января 2010

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

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

0 голосов
/ 06 сентября 2012

Если ваша единственная проблема - память, возможно, вам следует начать с защиты деструктора базового класса (и / или, возможно, других). Тогда, если что-то не скомпилируется, вы поймете, почему. Ссылка: повышение :: любые способы.

0 голосов
/ 14 января 2010

Цель объявления деструктора как виртуального состоит в том, чтобы иметь возможность вызывать деструктор производного класса всякий раз, когда вы вызываете delete для указателя типа Base, который указывает на объект типа Derived. Невыполнение этого требования приведет к неопределенному поведению.

Предположение, что вам не нужно помечать деструктор как виртуальный, если вы не выделяете память динамически, подразумевает, что вам не нужно вызывать деструктор производного класса, если вы не выделяете память динамически, что неправильно. Поскольку вы все еще можете выполнять несколько других операций в деструкторе вашего производного класса, кроме просто освобождения динамически распределенной памяти. Например, закрытие открытого файла, регистрация некоторой информации и т. Д.

0 голосов
/ 14 января 2010

Деструктор родительского класса всегда вызывается автоматически, и dtor по умолчанию всегда генерируется, если не объявлен явный dtor. В вашем примере ни A, ни B не должны иметь нетривиальный dtor.

Если в вашем классе есть виртуальные функции, дополнительный виртуальный dtor не повредит, и это хорошая практика. В случае, если ваш класс выделяет память или любой другой ресурс (например, открытие файла), необходим dtor, чтобы снова освободить этот ресурс после уничтожения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...