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




Когда виртуальные функции вызываются из производного класса, но не замещаются, то вызывается функция базового класса. Однако во многих случа­ях нет смыслового определения виртуальной функции в базовом классе. Напри­мер, в базовом классе Figure примера 33 для функции show_area() использу­ется просто "заглушка" с сообщением. При создании библиотеки классов для виртуальных функций еще неизвестно, будет ли смысловое содержание в контексте базового класса. Существует два способа справиться с этой пробле­мой.

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

Другим решением этой проблемы в С++ являются чистые виртуальныефункции (pure) – это функции объявленные в базовом классе как виртуальные, но не имеющие описания. Производный класс должен определить свою соб­ственную версию виртуальной функции, так как нельзя просто использовать версию базового класса. Иначе говоря, чистая виртуальнаяфункция – это пу­стое место, ожидающее своего заполнения производным классом. Объявляется чистая виртуальная функция, как и обычная виртуальная, но завершается =0.

Например, в классе:

class AbstractClass

{ public:

virtual void f1 (); // виртуальная функция

virtual void f2 () = 0; // чистая виртуальная функция

};

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

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

Абстрактный класс имет одну важную черту – нельзя создать объектэтого класса. Например, нельзя объявить объект типа AbstractClass, иначе компилятор выдаст ошибку. Причина, по которой абстрактные классы не могут использоваться для объявления объекта та, что одна или более функций не име­ют определения.

Это всего лишь схема, на основе которой можно со­здать производный класс. Он должен использоваться только как интерфейс и в качестве базового класса, от которого наследуются другие классы. Иначе говоря, абстрактный класс представляет собой интерфейс к множеству реализаций общего понятия. Конечно, другие функции-элементы класса AbstractClass могут вызывать чистую вирту­альную функцию. Например, функция f1 может вызвать f2:

void AbstractClass:: f1()

{ f2 (); // вызвать чистую виртуальную функцию

...

}

Чтобы использовать абстрактный класс, следует вывести из него новый класс:

class Myclass: public AbstractClass

{ public:

virtual void f2 (); // бывшая чистая виртуальная функция

...

}

Производный класс Myclass наследует чистую виртуальную функцию, но объявляет ее без =0. В другом месте следует реализовать функцию-элемент f2:

void Myclass:: f2 ()

{... // операторы функции-элемента

}

Обращения к первоначальной чистой виртуальной функции-элементу, к примеру, в данном случае AbstractClass:: f1(), теперь преадресованы Myclass:: f2(). Чистые виртуальные функции подобны крючкам, на которые вы можете цеплять код производных классов. Теперь, когда все виртуальные функции-эле­менты реализованы, компилятор сможет воспринять объекты типа Myclass:

Myclass myobject;

Выполнение оператора myobject.f1 приведет к вызову наследо­ванной функции f1, которая и вызовет f2 из Myclass.

 

Абстрактный класс не может быть использован как тип аргумента и функции show_ar ea(). В главной функции из объявления: Figure f, *p; необходимо уда­ лить объек т f и три строки операторо в с ним связанных, иначе компилятор ука­жет на ошибку создания объекта абстра ктного типа.

Результаты программы:

Треугольник с высотой 3 и основанием 4 имеет площадь = 6

Прямогольник со сторонами 5 и 6 имеет площадь = 30

Круг с радиусом 2 имеет площадь = 12.56

 

Производные классы с конструкторами и деструкторами

 

Каждый класс — базовый и производный может иметь конструктор и де­структонкция

virtual void draw()=0; // чистая виртуальная функция

} shape x; // ошибка: попытка создания объекта

// абстрактного класса

shape f(); // ошибка: абстрактный класс не может быть

// типом возврата

int q(shape s); // ошибка: абстрактный класс не может быть

// типом аргумента функции

shape* sp; // указатель на абстрактный класс допустим

shape& h(shape&); // ссылка на абстрактный класс в качестве типа

// возврата или аргумента функции допустима

 

Предположим, что В является производным классом от абстрактного ба­зового класса А. Тогда для каждой чистой виртуальной функции fun() в А класс В должен обеспечивать определение fun, либо объявлять fun как чисто вирту­альную функцию. Если этого не сделано, то компилятор сообщит об ошибке. Например, описание производного класса от Shape с замещением чисто виртуальных функций:

class Circle: public Shape // производный класс

{ int radius;

public:

virtual void rotate(int) { } // действия отсутствуют

void draw (); // замещение Shape:: draw

};

Circle c;

 

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

Чисто виртуальная функция, которая не определена в производном клас­се, остается чисто виртуальной, поэтому такой производный класс, как и базо­вый, является абстрактным. Это позволяет строить реализацию поэтапно:

 

class Poligon: public Shape // новый абстрактный класс

{ public:

void rotate(int); // замещение Shape:: rotate

// draw не замещен

};

Poligon p; // ошибка: объявление объекта абстрактного класса

 

class Newpoligon: public Poligon // производный класс

{ public:

void rotate(int); // замещение Shape:: rotate

void draw (); // замещение Shape::draw

};

Newpoligon np; // правильно

 

Пример 35.

Рассмотрим модифицированную версию программы примера 33. В классе Figure функцию show_area() определим как чистую виртуальную:

class Figure // базовый класс

{ protected: // защищенные элементы, доступные в производных классах

double x, y;

public:

void set_dim (double i, doublej=0) // 2-й параметр по умолчанию

{ x = i, y = j; } // задание измерений фигур

virtual void show_area() = 0; // чистая виртуальная функция

};

В производных классах остаются переопределенные функции show_area(). В главной функции из объявления: Figure f, *p; необходимо уда­лить объект f и три строки операторов с ним связанных, иначе компилятор ука­жет на ошибку создания объекта абстрактного типа.

Результаты программы:

Треугольник с высотой 3 и основанием 4 имеет площадь = 6

Прямогольник со сторонами 5 и 6 имеет площадь = 30

Круг с радиусом 2 имеет площадь = 12.56

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2018-01-08 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: