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

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

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

25 комментариев:

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

    "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-ить Все Конструкторы...

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

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

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

    ОтветитьУдалить
  5. Анонимный10/1/07 11:57

    ---
    А Зачем Же Классу Вообще Быть Абстрактным, Если У Него 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);
    ...
    };

    ОтветитьУдалить
  6. Анонимный11/7/08 16:21

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

    ОтветитьУдалить
  7. Анонимный9/3/09 08:10

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

    ОтветитьУдалить
  8. Анонимный9/3/09 08:18

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

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


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

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

    };


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

    CBase::~CBase()
    {}

    ОтветитьУдалить
  10. Анонимный11/3/09 09:47

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

    ОтветитьУдалить
  11. 2Анонимный:

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

    Да.

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

    Да.

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

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

    ОтветитьУдалить
  12. Вот эту мысль я не поняла


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

    ОтветитьУдалить
  13. Анонимный18/3/09 20:52

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

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

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

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

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

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

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

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

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

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

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

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

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

    ;-)

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

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

    ОтветитьУдалить
  21. 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 для плохой диагностики компилятора

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

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

    ОтветитьУдалить
  23. Анонимный2/4/12 15:40

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

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

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

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

    ОтветитьУдалить
  24. Анонимный7/12/12 01:47

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

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

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

    ОтветитьУдалить