Программа 62. абстрактный класс фигур
В программе создан абстрактный класс Figure фигур на плоскости, который включает общие свойства, имеющиеся у любой фигуры: координаты центра, цвет, и общие для всех фигур методы: изображения на экране, стирания с экрана, перемещения по экрану. Функция рисования сделана чистой виртуальной. На базе абстрактного класса Figure определены конкретные классы эллипсов и прямоугольников. Для рисования фигур на экране используется графическая система TC.
// Файл Figure.h
#ifndef FIGUREH
#define FIGUREH
class Figure{ // Класс фигур на плоскости
int x, y; // Координаты центральной точки фигуры
int color; // Цвет фигуры
public:
int& GetX(){return x;} // Доступ к центральной
int& GetY(){return y;} // точке
int GetClr() {return color;} // Доступ к цвету
Figure(int xx, int yy, int clr): x(xx), y(yy) // Конструктор
{color = clr;}
virtual void Show(int clr)=0; // Чистая виртуальная функция
// рисования фигуры
void Hide(); // Скрыть изображение
void Move(int dx, int dy); // Переместить фигуру на dx и dy
void Movement(); // Движение, пока не нажата ESC
};
#endif
// Файл Figure.CPP
#include "Figure.h"
#include<graphics.h>
void Figure::Hide() // Убирает изображение фигуры
{
int bk = getbkcolor(); // Цвет фона
int cc = getcolor(); // Цвет рисования
Show(bk); // Рисуем цветом фона (стираем)
setcolor(cc); // Восстанавливаем цвет рисования
}
void Figure::Move(int dx, int dy) // Перемещение фигуры
{
Hide(); // Удаляем изображение
x+=dx; y+= dy; // Изменяем координаты
Show(color); // Показываем фигуру на новом месте
}
#include <conio.h>
void Figure::Movement() // Движение при нажатии клавиш
{ // со стрелками, пока не нажата Esc
const int UP = 72, DOWN = 80, // Константы
RIGHT = 77, LEFT = 75, ESC = 27; / /для кодов клавиш
char c = 32;
while(c != ESC){ // Пока не нажата клавиша Esc
c = getch();
if(c == 0) { // Возможно нажата одна из клавиш-стрелок
c = getch();
switch(c){
case LEFT: Move(-1, 0); break;
case RIGHT: Move(1, 0); break;
case UP: Move(0, -1); break;
case DOWN: Move(0, 1); break;
} // switch
} // if(c == 0)
} // while
}
// Файл Demoabst.cpp
#include"Figure.h"
#include<conio.h>
#include<graphics.h>
class Ellipse : public Figure{ // Класс эллипсов
unsigned int a, b; // Полуоси эллипса
public:
Ellipse(int x0, int y0, int ai, int bi, int clr): // Конструктор
Figure(x0, y0, clr), // Вызов конструктора базового класса
a(ai), b(bi) // Вызов конструкторов членов класса
{ } // Пустое тело конструктора класса Ellipse
void Show(int clr) // Рисование эллипса
{
int cc = getcolor(); // Запоминаем цвет рисования
setcolor(clr); // Устанавливаем для рисования цвет фигуры
ellipse(GetX(), GetY(), 0, 360, a, b); // Вызов функции для рисования
setcolor(cc); // Восстанавливаем прежний цвет рисования
}
};
class Rect : public Figure{ // Класс прямоугольников со сторонами,
// параллельными осям координат
unsigned int a, b; // Стороны прямоугольника
public:
Rect(int x0, int y0, int ai, int bi, int clr): // Конструктор
Figure(x0, y0, clr), // Вызов конструктора базового класса
a(ai), b(bi) // Вызов конструкторов членов класса
{} // Пустое тело конструктора класса Rect
void Show(int clr) // Рисование прямоугольника
{
int cc = getcolor(); // Запоминаем цвет рисования
setcolor(clr); // Устанавливаем для рисования цвет фигуры
// Вызов функции для рисования
rectangle(GetX() - a / 2, GetY() - b / 2, GetX() + a / 2, GetY() + b / 2);
setcolor(cc); // Восстанавливаем прежний цвет рисования
}
};
#include<iostream.h>
void main()
{
Ellipse E(100, 200, 80, 50, YELLOW); // Создаем желтый эллипс
Rect R(100, 200, 100, 50, BLUE); // Создаем синий прямоугольник
int dr = DETECT, mod;
// Инициализация графики
initgraph(&dr, &mod, "D:\\Programs\\Tc30\\BGI");
E.Show(E.GetClr()); getch(); // Вызов функции Ellipse::Show()
R.Show(R.GetClr()); getch(); // Вызов функции Rect::Show()
E.Movement(); getch(); // Вызов функции Figure::Movement() для
// объекта E производного класса Ellipse
R.Movement(); getch(); // Вызов функции Figure::Movement() для
// объекта R производного класса Rect
closegraph(); // Закрытие графического режима
}
Примерный вид картинки на экране показан на рис.46. Программа после создания изображений эллипса и прямоугольника ждет нажатия клавиш-стрелок и перемешает сначала эллипс. При нажатии клавиши Esc «активным» становится прямоугольник, который также можно подвигать.
Рис.46. Картинка на экране, создаваемая программой 62
Для рисования эллипса на экране использована функция
void ellipse(int x, int y, int stangle, int endangle, int xradius, int yradius);
которая рисует на экране дугу эллипса установленным цветом рисования. Здесь x, y – координаты центра эллипса, xradius, yradius – полуоси, stangle, endangle – начальный и конечный углы, отсчитываемые от оси x против часовой стрелки.
Прямоугольник рисуется функцией
void rectangle(int left, int top, int right, int bottom);
Здесь left, top – координаты левого верхнего угла прямоугольника, right, bottom – координаты правого нижнего угла.
Вызов виртуальных функций
Функции класса можно вызывать для объекта класса, через указатель на объект класса (§20.4) и из другой функции этого класса. Программа 62 иллюстрирует последний способ. В базовом классе Figure есть одна чистая виртуальная функция Show, наличие которой позволяет написать в этом классе обычные функции Hide, Move и Movement. В производных классах Ellipse и Rect есть собственные функции Show. Для объектов производных классов E и R вызывается функция базового класса Movement(), которая в момент написания и компиляции модуля Figure ничего «не знала» о будущих классах-потомках. Тем не менее функция Movement() вызывает при своей работе нужную функцию Show, ориентируясь на тип объекта, для которого вызывается. Это реализуется благодаря тому, что функция Show – виртуальная, и выбор нужной виртуальной функции решается на этапе выполнения программы, когда становится известен тип объекта, для которого вызывается функция.
Совместимость типов
При наследовании производный тип совместим с базовым. При этом совместимость направлена от потомка к предку, т.е. производные классы можно использовать вместо базовых. Это распространяется на экземпляры классов, указатели на объекты, формальные и фактические параметры. Производный класс включает в себя все члены-данные (поля или свойства) и все члены-функции (методы) базового класса и может быть что-то ещё, поэтому гарантируется, что при присваивании объекту базового класса объекта производного класса все поля будут заполнены. Обратное, т.е. присваивание объекту-потомку значения объекта-предка может оставить некоторые поля незаполненными, поэтому запрещено. Например, в программе 59 есть базовый класс
class Date{…};
и производный класс
class Pers: Date {…};
Рассмотрим фрагмент программы, где используются эти классы.
Date dt; // Дата, создаваемая конструктором по умолчанию
Pers VIL(”Ленин В.И.”, Date(22, 4, 1870)); // Переменная класса Pers
dt = VIL; // Допустимо, в dt скопируется дата из VIL
Date bds(21, 12, 1879); // Переменная класса Date
Pers SIV(”Сталин И.В.”, Date(22, 4, 1870)); // Переменная класса Pers
// с ошибочной датой
SIV = bds; // Недопустимо,
Есть ограничения для абстрактных классов. Абстрактные классы нельзя употреблять для задания типа параметра функции или типа значения, возвращаемого из функции, например,
void f(Figure ff); // Запрещено, т.к. ff имеет тип абстрактного класса
Figure g(); // Запрещено, т.к. возвращаемое значение
// абстрактного класса
Ellipse h(Rect r); // Допустимо, в функцию передается, из функции
// возвращается значение конкретного класса
Формальным параметром функции может быть указатель абстрактного класса. В этом случае появляется возможность передавать в качестве фактического аргумента значение указателя на производный объект. Например,
void fgh(Figure *pf) // Функция с аргументом – указателем
{...} // на абстрактный класс
Ellipse elps(50, 50, 20, 10, GREEN); // Объекты конкретных
Rect rct(40, 30, 20, 10, RED); // классов
fgh (&elps); // Вызов функции с аргументами,
fgh (&rct); // указывающими на объекты разных типов
Множественное наследование
Производный класс может иметь не один, а сразу несколько базовых классов. В этом случае говорят о множественном наследовании. Продемонстрируем применение множественного наследования на примере программы для решения систем линейных алгебраических уравнений.
Сначала создаются два класса: алгебраические вектора и матрицы. Система линейных алгебраических уравнений есть совокупность матрицы системы и вектора правой части, поэтому естественно представить класс систем линейных алгебраических уравнений как производный одновременно от класса матриц и класса векторов. .