Программа 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 «активным» становится прямоугольник, который также можно подвигать.

программа 62. абстрактный класс фигур - student2.ru

Рис.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); // указывающими на объекты разных типов

Множественное наследование

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

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

Наши рекомендации