На этом шаге мы рассмотрим использование абстрактных классов.
Абстрактным классом называется класс, в котором есть хотя бы одна чистая (пустая) виртуальная функция. Чистой виртуальной называется компонентная функция, которая имеет следующее определение:
virtual тип имя_функции (список_формальных_параметров) = 0;
В этой записи конструкция "= 0" называется "чистый спецификатор". Пример описания чистой виртуальной функции:
virtual void fpure(void) = 0;
Чистая виртуальная функция "ничего не делает" и недоступна для вызовов. Ее назначение - служить основой для подменяющих ее функций в производных классах. Исходя из этого становится понятной невозможность создания самостоятельных объектов абстрактного класса. Абстрактный класс может использоваться только в качестве базового для производных классов. При создании объектов такого производного класса в качестве подобъектов создаются объекты базового абстрактного класса. Предположим, что имеется абстрактный класс:
class В { protected: virtual void func(char) = 0; void sos(int); };
На основе класса B можно по-разному построить производные классы:
class D: public В { . . . void func(char); }; class E: public В { void sos(int); };
В классе D чистая виртуальная функция func() заменена конкретной виртуальной функцией того же типа. Функция B::sos() наследуется классом D и доступна в нем и в его методах. Класс D не абстрактный. В классе Е переопределена функция B::sos(), а виртуальная функция B::func() унаследована. Тем самым класс Е становится абстрактным и может использоваться только как базовый.
Как всякий класс, абстрактный класс может иметь явно определенный конструктор. Из конструктора возможен вызов методов класса, но любые прямые или опосредованные обращения из конструктора к чистым виртуальным функциям приведут к ошибкам во время выполнения программы.
Механизм абстрактных классов разработан для представления общих понятий, которые в дальнейшем предполагается конкретизировать. Эти общие понятия обычно невозможно использовать непосредственно, но на их основе можно, как на базе, построить частные производные классы, пригодные для описания конкретных объектов. Например, из абстрактного класса "фигура" можно сформировать класс "треугольник", "окружность" и т.д.
В качестве примера рассмотрим программу, в которой на основе базового класса point построен абстрактный класс figure. В классе figure определены:
Кроме того, в класс входят методы:
Функции hide() и move() обращаются к чистой виртуальной функции show(). Однако реальное выполнение show() возможно только после создания производного класса, в котором чистая виртуальная функция show() будет подменена компонентной функцией для изображения конкретной фигуры.
Определение абстрактного класса figure (в файле figure.срр):
//FIGURE.CPP - абстрактный класс на базе класса POINT. #include "point.cpp" class figure: public point { public: // Конструктор абстрактного класса figure: figure (point p) : point(p.givex(), p.givey()) { } // Чистая виртуальная функция для будущего изображения фигур: virtual void show(char s) = 0; // Функция для удаления изображения фигуры: void hide() { show(' '); // Обращение к чистой виртуальной функции. } void move(point p) // Перемещение фигуры в точку "р". { hide(); x = p.givex(); y = p.givey(); show('*'); } };
На базе класса figure определим неабстрактные классы:
// SQUARE.CPP - класс "квадрат", наследник класса FIGURE. #include <conio.h> //Для функции gotoxy(); class square : public figure { int lq; //Длина стороны квадрата. public: // Конструктор класса SQUARE: square (point d, int li): figure(d) { lq = li;} ~square() // Деструктор класса SQUARE. { hide(); // Убрать с экрана изображение квадрата. } void show(char s='*');// Изобразить квадрат на экране дисплея. }; //Конец определения класса. void square::show(char s) { int d=lq/2; for (int i=-d; i<=d; i++) { gotoxy(x+i,y+d); cout << s; gotoxy(x+i,y-d); cout << s; } for (int j=-d; j<=d; j++) { gotoxy(x-d,y+j); cout << s; gotoxy(x+d,y+j); cout << s; } }
// STAR.CPP - класс "звезда", наследник класса FIGURE. #include <conio.h> //Для функции gotoxy(); class star: public figure { int rad; // Длина лучей звезды. public: // Конструктор класса STAR: star (point e, int ri): figure(e) { rad = ri; } ~star() // Деструктор класса STAR. { hide(); // Убрать с экрана изображение звезды. } void show(char s='*'); // Изобразить звезду на экране дисплея. }; void star::show(char s) // Изобразить звезду на экране дисплея. { gotoxy(x,y); cout << s; for (int i=1;i<=rad;i++) { gotoxy(x+i,y); cout << s; gotoxy(x-i,y); cout << s; gotoxy(x,y+i); cout << s; gotoxy(x,y-i); cout << s; } for (i=-rad;i<=rad;i++) { gotoxy(x+i,y+i); cout << s; gotoxy(x-i,y+i); cout << s; } }
В следующей программе используются все три класса:
//OOР32_1.СРР - абстрактные классы и чистые виртуальные функции. #include "figure.cpp" #include "square.cpp" #include "star.cpp" #include <conio.h> // Для функции getch(); void main(void) { point A(10,8), B(15,20); square C(A,6); star E(B,3); // Изобразить точку - point::show(): A.show(); getch(); // Изобразить точку - point::show(): B.show(); getch(); // Показать квадрат - square::show(): C.show(); getch(); // Показать звезду - star::show(): E.show(); getch() ; // Совместить фигуры - square::figure::move(): C.move(B); getch(); // Убрать звезду - star::figure::hide(): E.hide(); getch() ; // Убрать квадрат - square::figure::hide(): C.hide(); getch(); }
В программе на базе класса figure определены два производных класса: square (квадрат) и star (звезда). Для обоих классов унаследованный класс point определяет центры фигур. В обоих классах определены конкретные методы show() и из абстрактного класса figure унаследованы функции move() и hide(). Комментарии к операторам основной программы содержат полные (квалифицированные) имена исполняемых функций.
На следующем шаге мы закончим рассмотрение абстрактных классов.