Виртуальный деструктор: требуется ли базовый класс, если базовый класс выделяет память динамически?

Этот вопрос выглядит как обсуждение в Виртуальном деструкторе: требуется ли это, когда динамически не выделяется память?

В вопросе об экзамене меня спросили: - Что должен определять любой базовый класс, поддерживающий указатели для динамически выделяемой памяти?

Я ответил: - Конструктор копирования и оператор присваивания (чтобы убедиться, что НЕ копируются только указатели... см. Глубокое копирование), и деструктор (для освобождения выделенной памяти)

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

6 ответов

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

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

5.3.5

3) В первом альтернативном варианте (удаление объекта), если статический тип операнда отличается от его динамического типа, статический тип должен быть базовым классом динамического типа операнда, а статический тип должен иметь виртуальный деструктор или поведение не определено [...]

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

Base *pointer_to_base_class = new Derived;
delete pointer_to_base_class;

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

Они не на 100% правильные. Виртуальный деструктор является обязательным, если

  1. иерархия классов, используемая с динамическим полиморфизмом И
  2. производные объекты уничтожаются указателем на базу.

В противном случае не виртуальный деструктор в порядке. Но в большинстве случаев, даже если предназначен только № 1, это хороший способ сделать деструктор виртуальным независимо от № 2.

В рамках стандарта большинство иерархий наследования имеют виртуальный деструктор в своей основе; тем не мение, sub_match определяется для публичного наследования от std::pair<BidirectionalIterator, BidirectionalIterator> и как таковой он может владеть динамически распределенной памятью. В смежной области, match_results не требуется, но обычно реализуется для общего наследования от std::vector<...> который определенно выделяет память.

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

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

Например, представьте, что вы выздоравливающий Java-программист и настаиваете, что все Object, Вы можете запустить свою первую программу на C++ следующим образом:

class Object
{
public:
   virtual ~Object() { }
};

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

Если вы также думаете, что Object должен быть абстрактным, вы можете даже сделать деструктор чисто виртуальным:

class Object { public: virtual ~Object() = 0; }; Object::~Object() { }

Чтобы получить ответы на все хорошие вопросы здесь, рекомендуется объявить виртуальный деструктор, чтобы обеспечить надлежащую очистку, когда класс должен быть разделен на подклассы для формирования иерархии, и вы хотите удалить производный объект через указатель на него. Стандарт C++ ясен в этом:

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

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

Другие вопросы по тегам