среда, марта 02, 2005

Чисто виртуальный деструктор

При попытке объявить деструктор чисто виртуальным, компилятор будет справедливо ругаться на то, что у деструктора не объявлено тело. Правильно, ведь наследники вызвают деструктор родителя, а если тела у родительского деструктора нет, то и вызывать нечего. Поэтому деструктору нужно написать тело. Да, у чисто виртуальных функций могут быть объявлены тела. Обычно это никто не делает, потому что это не нужно. Итак, в итоге получается базовый класс, деструктор которого наследникам надо обязательно переопределить. Итак, в итоге получается абстрактный базовый класс, при наследовании от которого деструкторы будут виртуальными. Это удобно, если в базовом классе нет других чисто виртуальных функций.
Здесь можно почитать о том, в каких еще случаях удобно использовать чисто виртуальную функцию с телом.

25 коммент.:

Ivan Sagalaev комментирует...

Меня Заинтересовало, А Зачем Вообще Делать Чисто Виртуальный Деструктор, И Я Пошел Почитать По Ссылке:

"If the class should be abstract (you want to prevent instantiating it) but it doesn't happen to have any other pure virtual functions..."

А Зачем Же Классу Вообще Быть Абстрактным, Если У Него Doesn't Happen To Have Обычных Абстрактных Функций? Собственно, Это И Есть Показатель Абстрактности Класса - Наличие Виртуальных Функций, Которые Наследники _Должны_ Реализовать. Если Их Нет По Сути, То Зачем Приплетать Для Этой Цели Деструктор? Не Могу Придумать Реальной Задачи...

P.S. И Если Уж На То Пошло, То Чтобы Просто Запретить Создание Класса, Достаточно За-Protect-ить Все Конструкторы...

Alena комментирует...

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

Ivan Sagalaev комментирует...

Но Это Не Решает Проблемы Для Наследников Уровня Ниже Первого. Получается, Что Для Первого Наследника У Тебя Есть Механизм "Не Забыть", А Для Его Наследников - Уже Нет.

Alena комментирует...

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

Анонимный комментирует...

---
А Зачем Же Классу Вообще Быть Абстрактным, Если У Него Doesn't Happen To Have Обычных Абстрактных Функций? Собственно, Это И Есть Показатель Абстрактности Класса - Наличие Виртуальных Функций, Которые Наследники
---
Если кратко, то, например, для корректной работы оператора присваивания.


---
Не Могу Придумать Реальной Задачи...
---
У Мейерса в "More effective C++" много на эту тему.
Наверно, вам лучше просто прочитать
"More effective C++" Item 33: Make non-leaf classes abstract.
Я лишь копи-пейстну итоговый пример.

class AbstractAnimal {
protected:
AbstractAnimal& operator=(const AbstractAnimal& rhs);

public:
virtual ~AbstractAnimal() = 0; // see below
...

};

class Animal: public AbstractAnimal {
public:
Animal& operator=(const Animal& rhs);
...
};

class Lizard: public AbstractAnimal {
public:
Lizard& operator=(const Lizard& rhs);
...
};

class Chicken: public AbstractAnimal {
public:
Chicken& operator=(const Chicken& rhs);
...
};

Анонимный комментирует...

Виртуальные методы нужны для посылки сообщений от предка потомкам. Если предок никаких сообщений потомкам не передает (читай - у него отсутствуют виртуальные методы) то его ценность в иерархии нулевая. Зачем впихивать сущность только ради виртуального деструктора я не понимаю. Пример приведенный выше умным не назовешь. Именно по причине отсутствия связи между предком и потомками. Оператор присваивания, в данном случае, вообще непонятно зачем написан, это разные методы. Оператор "=" предка никак с операторами "=" потомков не связан. Так что пример фтопку, тема класса с единственным виртуальным методом - деструктором, да еще и чисто виртуальным не раскрыта.

Анонимный комментирует...

g++(версии 4.2.4) вообще не даёт переопределить деструктор базового класса в наследнике...

Анонимный комментирует...

Для gcc (версия 4.2.4) не получилось переопределить чисто виртуальный деструктор в наследнике.Подскажите синтаксис...

Alena комментирует...

Для gcc (версия 4.2.4) не получилось переопределить чисто виртуальный деструктор в наследнике.Подскажите синтаксис...


struct CBase
{
virtual ~CBase() = 0;
};

struct CDerived : public CBase
{
~CDerived() { }

};


Ну и тело где-нибудь объявить.

CBase::~CBase()
{}

Анонимный комментирует...

Получается, что деструктор базового класса можно переопределить(вернее будет сказать определить) только один раз. Если наследников несколько, они все будут использовать одно и тоже определение тела деструктора базового класса. Так не проще ли тогда написать тело деструктора базового класса, при написании самого базового класса, не откладывая это дело до момента написания наследника?

Alena комментирует...

2Анонимный:

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

Да.

Если наследников несколько, они все будут использовать одно и тоже определение тела деструктора базового класса.

Да.

Так не проще ли тогда написать тело деструктора базового класса, при написании самого базового класса, не откладывая это дело до момента написания наследника?

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

Unknown комментирует...

Вот эту мысль я не поняла


Разобрался. Я просто понял фразу переопределить деструктор базового класса слишком буквально - как замену кода деструктора именно базового класса. По аналогии с переопределением метода, когда базовая реализация скрывается и подменяется наследником. Вы же под переопределением деструктора базового класса видимо имеете ввиду определение деструктора дочернего класса. Вобщем, я немного неправильно Вас понял :))

Анонимный комментирует...

Почитал ребята я тут ваши комментарии и понял, что плохо у нас преподается ООП. Что такое "чисто виртуальный метод" ? Это который = 0 в c++. Так давно уже сложились термины для такого случая - абстрактный метод. Классы, имеющие все абстрактные методы являют собой интерфейсы. В CSharp, в Java да и что там говорить - в самой нотации UML эти термины давно "узаконены". Огромная просьба не вводить новичков в дебри, а изъясняться на профессиональном языке. Мне то все равно, а вот начинающим разработчикам голову заморочите, они вообще ООП понимать не будут.

elwood комментирует...

Интересный вопрос. Кстати, объявить тело для такого деструктора можно только в cpp-файле. Inline-декларация прямо в хеадере не прокатывает :

virtual ~Foo() = 0 { } // compile-time error

Проверял на gcc 4.2

Unknown комментирует...

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

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

Alena комментирует...

2Darth Vader:
Т.е. объявление деструктора чисто-виртуальным не обязывает потомков к написанию деструктора, а всего лишь не дает возможности создать объект базового класа как абстрактного.

То, как ведет себя один компилятор в одной конкретной ситуации не есть истина в последней инстанции. Надо стандарт смотреть.

Alena комментирует...

То, как ведет себя один компилятор в одной конкретной ситуации не есть истина в последней инстанции. Надо стандарт смотреть.

Посмотрела, поправила, спасибо!

NightmareZ комментирует...

А зачем нужна в языке возможность определять тело для чисто виртуальной функции?

Alena комментирует...

NightmareZ
А зачем нужна в языке возможность определять тело для чисто виртуальной функции?

http://www.gotw.ca/gotw/031.htm

;-)

NightmareZ комментирует...

Спасибо, конечно. Но, толи мне "повылазило" по новый год, толи что... я что-то в упор не вижу, зачем это НУЖНО :-)

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

Alena комментирует...

NightmareZ
Буду весьма благодарен, если тыкните меня носом в нужный фрагмент текста.

Вот это:
2. Why might you declare a pure virtual function and also write a definition (body)? Give as many reasons or situations as you can.

Там дается три примера:
Для определения чисто виртуального деструктора.
Для определения поведения по умолчанию.
Workaround для плохой диагностики компилятора

Там же приведены примеры.

NightmareZ комментирует...

А зачем определение чисто виртульному деструктору? И зачем чисто виртуальному деструктору поведение по-умолчанию? И, если оно ему таки нужно, зачем его делать чисто виртуальным, а не просто виртуальным?

Анонимный комментирует...

Определение деструктора виртуальным - понятно нужно для предотвращения утечек памяти.

Определение чисто виртуальному деструктору можно и не давать (если Вам специально это не требуется), потому как компилятор автоматически создает определение деструктора для каждого класса у которого нет деструктора.

Так чем же обычный отличается от чистого?
Единственно отличие в классах в которых нет чисто виртуальных функций, а это почему то нужно. Тогда можно деструктор сделать чистым и все будет ок.

Итого наличие виртуального деструктора важно, а вот чистота его не так существенна.

Анонимный комментирует...

Итак, в итоге получается абстрактный базовый класс, при наследовании от которого деструкторы будут виртуальными.
************
странній вівод - если деструктор будет просто виртуальній, то у наследников деструкторі же будут тоже виртуальнімі, разве нет? Для єтого не нужен чисто виртуальній деструктор.
PS: извините, я ставил бі себе раскладку дольше, чем єто писал

Alena комментирует...

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

Это не вывод, просто комментарий. Достаточно, если деструктор будет просто виртуальным, да.