Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кетков.doc
Скачиваний:
17
Добавлен:
27.09.2019
Размер:
2.22 Mб
Скачать

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

Когда существует иерархия производных классов, и мы создаем массив динамических указателей на объекты разных производных классов, то при уничтожении такого рода объектов могут возникнуть проблемы. Продемонстрируем это на примере иерархии геометрических фигур: Shape (фигура базового класса), Circle (окружность, производная от Shape) и Rectangle (прямоугольник, производный от Shape):

#include <iostream.h>

class Shape {

public:

Shape(); //конструктор по умолчанию

~Shape(); //стандартный деструктор

virtual void show() {cout <<"Shape"<<endl;

};

class Circle: public Shape {

int xc,yc,r; //координаты центра и радиус

public:

Circle(int x,int y,int R):xc(x),yc(y),r(R) {} //конструктор

~Circle(); //стандартный деструктор

void show() {cout<<"x="<<xc<<" y="<<yc<<" r="<<r<<endl;

};

class Rectangle: public Shape {

int x1,y1,x2,y2; //координаты противоположных вершин

public:

Rectangle(int ix1,int iy1,int ix2,int iy2):

x1(ix1),y1(iy1),x2(ux2),y2(iy2) {} //конструктор

~Rectangle(); //стандартный деструктор

Создаем массив указателей на объекты базового класса и присваиваем им адреса динамически создаваемых объектов:

Shape *ptr_s[2];

ptr_s[0]=new Circle(20,20,10)

ptr_s[1]=new Rectangle(20,40,50,50);

Поработали с временно созданными объектами, и пришла пора их удалить. Попытка сделать это следующим способом ни к чему хорошему не приведет:

for(int i=0; i<2; i++) delete ptr_s[i];

Причина заключается в том, что для удаления этих фигур будет вызван деструктор класса Shape (именно на объекты этого класса был объявлен массив указателей ptr_s). А ресурсы, занятые окружностью и прямоугольником, при этом не будут освобождены. Выход из создавшегося положения довольно простой – надо объявить деструктор базового класса виртуальным (virtual ~Shape();). Тогда автоматически виртуальными станут и деструкторы производных классов (хотя деструкторы и не наследуются). И все проблемы, связанные с утечкой памяти, будут решены.

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

16.1.6. Чистые виртуальные функции и абстрактные классы

Чистая виртуальная функция не совершает никаких действий, и ее описание выглядит следующим образом:

virtual тип name_f(тип1 a1,тип2 a2,...)=0;

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

Объявим абстрактным класс Shape (Геометрическая Фигура), в состав которого включим две чистые виртуальные функции – определение площади фигуры (Get_Area) и определение периметра фигуры (Get_Perim).

class Shape {

public:

Shape(){} //конструктор

virtual double Get_Area()=0;

virtual double Get_Perim()=0;

};

class Rectangle: public Shape {

double w,h; //ширина и высота

public:

Rectangle(double w1,double h1):w(w1),h(h1) {}

double Get_Area() {return w*h;}

double Get_Perim() {return 2*w+2*h);}

};

class Circle: public Shape {

double r; //радиус

public:

Circle(double r1):r(r1) {}

double Get_Area() {return M_PI*r*r;}

double Get_Perim() {return 2*M_PI*r;}

};

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