Расширяющяся иерархия классов




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

 

Пример 42.

На основе примеров 40 и 41 создадим новый проект, в котором определим производный класс Circle (окружность) по иерархии: Figure<-Point<-Circle. Класс Circle наследует от базовых классов элементы-данные X, Y, которые зада­ют центр окружности, Visible (видимость) и имеет свой элемент Radius. Из функций класс включает замещаемые виртуальные методы для работы с окруж­ностью: Show (отображение), Hide (стирание), а также наследует из Point вир­туальный метод Drag (перемещение). Для смещения окружности на новое ме­сто был включен свой метод Moveto, который внешне повторяет метод Point::Moveto, но коды их различны, поскольку отличаются методы Show и Hide. Однако, если объявить метод Point::Moveto виртуальным, то он будет ра­ботать с окружностью как с "большой" точкой и тогда метод Circle::Moveto можно исключить. В класс Circle добавлены также методы увеличения (Expand) и уменьшения (Contract) окружности.

 

Создадим новые файлы для реализации класса Circle и включим их в пап­ку (каталог) с именем CIRCLE.

Дополним описанием класса Circle заголовочный файл figures.h, чем расширим его возможности, и включим в папку CIRCLE:

 

class Circle: public Point // производный класс от Point, Figure

{ protected:

int Radius; // защищенный элемент

public:

Circle (int Initx, int Inity, int Initradius); // конструктор Circle

// виртуальные методы:

void Show (); // отображение окружности

void Hide (); // стирание окружности

// невиртуальные методы:

void Expand (int Expandby); // расширение окружности

void Contract (int Contractby); // сжатие окружности

// используется виртуальный метод Point::Moveto

// void Moveto(int Newx, int Newy); // смещение окружности

};

 

В файл circle.cpp включены методы класса Circle:

 

#include<graphics.h>

#include "figures.h"

Circle::Circle(int Initx, int Inity, int Initradius): // конструктор Circle

Point(Initx, Inity) // вызов конструктора Point

{ Radius=Initradius; // инициализация Radius

}

void Circle::Show() // метод отображения

{ Visible=true; // окружность видима

circle(X, Y, Radius); // отображение окружности

}

void Circle::Hide() // метод стирания окружности

{ int Tempcolor;

Tempcolor=getcolor(); // сохранение текущего цвета

setcolor(getbkcolor()); // установка цвета фона

Visible=false; // окружность невидима

circle(X, Y, Radius); // стирание окружности

setcolor(Tempcolor); // возврат текущего цвета

}

void Circle::Expand(int Expandby) // метод расширения окружности

{ Hide(); // стирание окружности

Radius += Expandby; // увеличение радиуса

if(Radius < 0) Radius=0; // если Radius<0, то Radius=0

Show(); // отображение окружности

}

void Circle::Contract(int Contractby) // метод сжатия окружности

{ Expand(-Contractby); // Radius=(Radius-Contractby)

}

/* свой метод, который заменен виртуальным методом Point::Moveto

void Circle::Moveto(int Newx, int Newy)

{ Hide();

X=Newx;

Y=Newy;

Show();

}

*/

Файл maincirc.cpp содержит главную функцию:

 

#include "figures.h"

#include<iostream.h>

#include<conio.h>

#include<graphics.h>

#include<stdlib.h> // random(), srand(), kbhit()

#include<dos.h> // delay()

const char *path= "c:\\borlandc\\bgi";

void main()

{ int gdriver = DETECT, gmode; // параметры графики

initgraph (&gdriver, &gmode, path); // инициализация графики

int x, y, n, i; // координаты центра окружности

cout<<"Input x (<" << getmaxx() << "): ";

cin >> x;

cout<<"Input y (<" << getmaxy() << "): ";

cin >> y;

cout << "Move point and circle using key-arrow\n";

Point Mypoint (x,y); // создание объекта точка

Mypoint.Show(); // отображение точки

Mypoint.Drag(20); // перемещение точки

Mypoint.Hide(); // стирание точки

Point *ppoint; // базовый указатель

Circle Mycircle(x, y, 50); // создание объекта окружность

ppoint=&Mycircle; // указатель на окружность

// Mycircle.Show(); // отображение своим методом

ppoint->Show(); // отображение базовым указателем

ppoint->Drag(40); // перемещение окружности

ppoint->Hide(); // стирание точки

getch ();

ppoint->Moveto(200, 250); // смещение окружности

ppoint->Drag(20); // перемещение окружности

Mycircle.Expand(75); // увеличение окружности

ppoint->Drag(40); // перемещение окружности

Mycircle.Contract(50); // уменьшение окружности

ppoint->Drag(40); // перемещение окружности

cout<<"Input circles quantity: ";

cin >> n; // ввод количества окружностей

x=y=100; // координаты х, у

while(! kbhit()) // цикл до нажатия любой клавиши

{ x=random(getmaxx()); // случайное значение х

if(x<50) x=100; // проверка пределов значения х

if(x>600) x=500;

y=random(getmaxy()); // случайное значение у

if(y<50) y=100; // проверка пределов значения у

if(y>400) y=300;

for(i=1; i<=n; i++) // цикл расширения окружностей

{ setcolor(random(15)); // случайный цвет

Mycircle.Expand(i); // увеличение окружности

Mycircle.Moveto(x,y); // смещение окружности

delay(100); // задержка времени

}

for(i=n; i>=1; i--) // цикл уменьшения окружностей

{ setcolor(random(15)); // случайный цвет

Mycircle.Contract(i); // уменьшение окружности

delay(100); // задержка времени

}

}

cout<<"Input circles quantity <=5: ";

cin >> n; // ввод количества окружностей

x=y=100; // координаты х, у

Circle *pcircle[5]; // массив указателей на окружности

for(i=0;i<n;i++) // цикл по окружностям

{ pcircle[i]=new Circle(x+i*5,y+i*5,20); // инициализация окружности

setcolor(random(15)); // случайный цвет

pcircle[i]->Expand(i*10); // увеличение окружности

pcircle[i]->Contract(i*10); // уменьшение окружности

pcircle[i]->Drag(20); // перемещение окружности

}

closegraph(); // закрытие графики

}

В папку CIRCLE необходимо скопировать также из папки FIGPOINT файл функций базовых классов figpoint.cpp. Таким образом, в папке CIRCLE должны находится файлы: figures.h, figpoint.cpp, circle.cpp, maincirc.cpp, на основе которых разрабатывется проект с именем, например, circle.prj.

 

Узловые классы

 

Класс в иерахии представляет общее понятие, для которого производные классы могут считаться специализациями. Типичный класс, спроектированный как составная часть иерархии, узловой класс, опирается на услуги базовых классов, чтобы обеспечить свои собственные услуги. То есть он вызывает функции-элементы базового класса. Типичный базовый класс обеспечивает не просто реализацию интерфейса, описанного его базовым классом (как это делает класс реализации для абстрактного типа). Он также сам добавляет новые функции, обеспечивая, таким образом, широкий интерфейс. Это часто называют " программированием отличий" или "программированием по расширению". Узловому классу обычно нужны конструкторы, и часто не простые. Этим узловой класс отличается от абстрактных типов, которые редко имеют конструкторы.

Рассмотренный выше пример 42 пригоден для создания наследования для объектов с одной опорной точкой (центр окружности, сектора и дуги окружности, линии, заданной исходной точкой и т.п.). Для создания линии наследования объектов с двумя опорными точками (например, прямоугольник) можно начать ее с класса Point, используя его как узел разветвления (базовый класс) для новой ветви на­следования в виде производного класса Rectangle (прямоугольник).

 

Пример 43.

Разработаем новый проект для работы с объектом "прямоугольник" с це­лью реализации расширяющегося наследования классов по иерархии Figure<-Point<-(Circle, Rectanglе). При этом сохраняются все возможности ранее раз­работанных классов.

Класс Rectanglе наследует от базового класса Figure элементы-данные X, Y, которые задают координаты левого верхнего угла прямоугольника, и вклю­чает элементы X1, Y1, как координаты правого нижнего угла прямоугольника, а также наследует элемент Visible (видимость) класса Point. Из функций класс включает замещаемые виртуальные методы для работы с прямоугольником: Show (отображение), Hide (стирание), а также наследует из Point виртуаль­ный метод Drag (перемещение). Используются невиртуальные методы Getx1, Gety1 для получения координат X1, Y1 и свой метод Moveto для смещения прямоугольника на новое место. Конструктор класса инициирует свои элементы и передает аргументы своим базовым классам.

Заголовочный файл figures.h дополним описанием класса Rectangle:

 

class Rectangle: public Point

{ protected:

int X1, Y1; // координаты правого нижнего угла

public:

Rectangle (int Initx, int Inity, int Initx1,int Inity1): // конструктор Rectangle

Point(Initx, Inity) // вызов конструктора Point

{ X1=Initx1; Y1=Inity1; // инициализация данных Rectangle

}

// прототипы виртуальных методов:

void Show (); // изображение прямоугольника

void Hide (); // стирание прямоугольника

void Drag (int Dragby); // перемещение прямоугольника

// невиртуальные методы:

int Getx1() { return X1;} // получение координат угла

int Gety1() { return Y1;}

void Moveto(int Newx, int Newy,

int Newx1, int Newy1); // смещение объекта на новое место

};

 

Создадим файл rectangl.cpp с методами класса Rectangle:

#include<graphics.h>

#include "figures.h"

Boolean Getdelta (int& Deltax, int& Deltay);

// методы класса Rectangle:

void Rectangle::Show() // метод показа прямоугольника

{ if (! Visible) // если прямоугольник не видим, то

Visible=true; // теперь он видим

rectangle(X, Y, X1, Y1); // вывод прямоугольника на экран

}

void Rectangle::Hide() // метод стирания прямоугольника

{ int Tempcolor; // переменная для цвета

Tempcolor=getcolor(); // сохранение текущего цвета

setcolor(getbkcolor()); // установка цвета фона

if (Visible) // если прямоугольник видим, то

Visible=false; // теперь он невидим

rectangle(X, Y, X1, Y1); // стирание фоном

setcolor(Tempcolor); // возврат текущего цвета

}

// виртуальный метод перемещения прямоугольника по экрану:

void Rectangle::Drag(int Dragby) // Dragby — шаг смещения

{ int Deltax, Deltay; // смещение по Х, Y

int Fx, Fy, Fx1, Fy1; // координаты прямоугольника

Show(); // показ прямоугольника

Fx=Getx(); // текущие координаты объекта

Fy=Gety();

Fx1=Getx1();

Fy1=Gety1();

while (Getdelta(Deltax, Deltay)) // цикл перемещения прямоугольника

{ Fx += (Deltax * Dragby); // новые координаты прямоугольника

Fy += (Deltay * Dragby);

Fx1+= (Deltax * Dragby);

Fy1+= (Deltay * Dragby);

Moveto (Fx, Fy, Fx1, Fy1); // прямоугольник на новом месте

}

}

// метод смещения прямоугольника:

void Rectangle::Moveto(int Newx, int Newy, int Newx1, int Newy1)

{ Hide(); // стирание прямоугольника

X=Newx; // новые координаты

Y=Newy;

X1=Newx1;

Y1=Newy1;

Show(); // объект на новом месте

}

 

Создадим файл главной программы mainrec.cpp для демонстрации дей­ствий с прямоугольником:

 

#include "figures.h"

#include<iostream.h>

#include<conio.h>

#include<graphics.h>

#include<stdlib.h> // random(), srand(), kbhit()

#include<dos.h> // delay()

const char *path= "c:\\borlandc\\bgi";

void main() // главная функция

{ int gdriver = DETECT, gmode; // установка режимов графики

initgraph (&gdriver, &gmode, path); // инициализация графики

int x, y, x1, y1, n, i,maxx, maxy, maxcol,col; // рабочие переменные

// создание одного прямоугольника:

cout<<"Input coordinates of rectangle:\n"; // ввод координат:

cout<<"left up x="; cin >> x; // левый верхний угол

cout<<"left up y="; cin >> y;

cout<<"right down x1="; cin >> x1; // правый нижний угол

cout<<"right down y1="; cin >> y1;

Rectangle Myrect(x, y, x1, y1); // создание прямоугольника

Myrect.Show(); // показ прямоугольника

Myrect.Drag(20); // перемещение прямоугольника

// создание серии прямоугольников с помощью массива указателей:

Rectangle *prect[20]; // массив указателей

cout<< "Input rectangle quantity (<=10): "; // задать число прямоугольников

cin >> n;

for (i=0; i<n; i++) // цикл по прямоугольникам

{ setcolor(i+1); // изменение цвета

prect[i]=new Rectangle(x+i*30, y+i*30,

x1+i*30, y1+i*30); // новый прямоугольник

prect[i]->Show(); // показ прямоугольника

prect[i]->Drag(20); // перемещение прямоугольника

}

getch();

// создание пульсирующей серии прямоугольников:

cout<< "Input rectangles quantity (<=20): "; // задать число объектов

cin >> n;

clearviewport(); // чистка экрана

maxx=getmaxx(); // max пикселов по Х

maxy=getmaxy(); // max пикселов по Y

maxcol=getmaxcolor(); // max цветов

col=1; // начальный цвет

while(! kbhit()) // цикл до нажатия клавиши

{ for (i=0; i<n; i++) // цикл по прямоугольникам

{ prect[i]=new Rectangle(maxx/2-i*10, maxy/2-i*5,

maxx/2+i*10, maxy/2+i*5); // новый прямоугольник

setcolor(col++); // изменение цвета

if(col==maxcol) col=1; // если max, то начальный цвет

prect[i]->Show(); // показ прямоугольника

delay(500); // задержка показа

}

for (i=n-1; i>=0; i--) // обратный цикл

{ prect[i]->Hide(); // стирание прямоугольника

delay(500); // задержка показа

}

 

}

for (i=0; i<n; i++) //цикл

delete prect[i]; // уничтожения прямоугольников

closegraph(); // закрытие режима графики

}

 

Для разработки проекта создадим папку с именем RECTANGL, в которую включим ранее разработанные и новые файлы: figures.h, figpoint.cpp, circle.cpp, rectangl.cpp, mainrect.cpp. На основе этих файлов создадим проект программы с именем файла rect.prj для демонстрации действий с прямоуголь­никами.

Для демонстрации возможностей работы с объектами всех разработанных классов можно разработать новый проект c именем папки, например, FIGURES на основе проекта RECTANGL. При этом нужно создать новую главную функ­цию с именем, например, mainfig.cpp, которая включала бы возможные дей­ствия с различными объектами (точка, окружность, прямоугольник).

 

Подводя итог, можно сказать, что узловой класс:

1. Опирается на свои базовые классы, как для своей реализации, так и для предоставления услуг своим пользователям.

2. Предоставляет более широкий интерфейс (то есть с большим числом открытых методов) по сравнению с базовыми классами.

3. В своем открытом интерфейсе опирается в первую очередь (но не обязательно только) на виртуальные функции.

4. Зависит от своих (прямых и не прямых) базовых классов.

5. Понятен только в контексте своих базовых классов.

6. Может служить базовым классом для дальнейшего порождения классов.

7. Может быть использован для создания объектов.

Не все узловые классы будут удовлетворять пунктам 1, 2, 6 и 7, но большинство – удовлетворяют. Класс, не удовлетворящий пункту 6, напоминает конкретный тип, и его можно назвать конкретным узловым классом. Такие классы иногда называют классами-листьями. Класс, не удовлетворяющий пункту 7, напоминает абстрактный тип, и его можно назвать абстрактным узловым классом. Пункт 4 подразумевает, что для компиляции узлового класса программист должен включить объявления всех его прямых и непрямых базовых классов, а также все объявления, от которых в свою очередь зависят последние. Этим узловой класс отличается от абстрактного типа. Пользователь абстрактного типа не зависит от классов, использующихся для его реализации, и может не включать их в текст для компиляции.

 



Поделиться:




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

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


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