Здавалка
Главная | Обратная связь

Виртуальный деструктор.



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

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

class Base{ private: char *sp1; int size; public: //конструктор Base(const char *S, int s){ size=s; sp1=new char[size]; } //деструктор ~Base(){ cout<<"Base"; delete[]sp1; } };

Итак. Конструктор класса выделяет память для строки путем обращения к конструкции new и сохраняет адрес новой строки в указателе sp1. Деструктор класса освобождает эту память, когда объект класса Base выходит из области видимости. Далее, из базового класса выведем новый класс. Вот такой:

class Derived: public Base{ private: char *sp2; int size2; public: //конструктор Derived(const char *S1,int s1,const char *S2, int s2): Base(S1,s1){ size2=s2; sp2=new char[size2]; } //деструктор ~Derived(){ cout<<"Derived"; delete[]sp2; } };

Этот класс сохраняет вторую строку, на которую ссылается его указатель sp2. Новый конструктор вызывает конструктор базового класса, передавая строку в базовый класс, а также выделяет память под вторую строку и сохраняет адрес новой строки в указателе sp2. Деструктор этого класса освобождает эту память.

Теперь где-то в программе мы можем создать объект такого класса:

Derived MyStrings(“string 1”,9,“string 2”,9);

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

Рассмотрим другой вариант. Предположим, что мы объявили указатель на базовый класс Base, но присвоили ему адрес объекта класса Derived. Это вполне допустимо, мы уже обсуждали этот вопрос ранее. То есть, это будет выглядеть в программе так:

Base *pBase; //указатель на базовый классpBase=new Derived(“string 1”,9,“string 2”,9);

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

delete pBase;

Компилятор "видит", что указатель pBase должен ссылаться на объекты класса Base (откуда бы ему узнать, что именно присвоено этому указателю?). И вполне естественно программа вызовет только деструктор базового класса, и он удалит одну строку, но оставит в памяти другую. Ведь деструктор класса Derived не вызывался. Получается классическая утечка памяти). И, вот здесь, появляется виртуальный деструктор.

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

virtual ~Base(){ cout<<"Base"; delete[]sp1; } virtual ~Derived(){ cout<<"Derived"; delete[]sp2; }

Смысл таков. Поскольку деструкторы объявлены виртуальными, то их вызовы будут компоноваться уже во время выполнения программы. То есть, объекты сами будут определять, какой деструктор нужно вызвать. Поскольку наш указатель pBase на самом деле ссылается на объект класса Derived, то деструктор этого класса будет вызван, так же как и деструктор базового класса. Деструктор базового класса автоматически выполняется после деструктора производного класса.

Предыдущая Оглавление Следующая
Предыдущая Оглавление Следующая






©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.