Обобщенный пример наследования




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

 

Постановка задачи.

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

Вместо ограниченного по своим возможностям класса Location в каче­стве базового класса опишем свойства и функции "некоторой фигуры" (Figure). Ме­стоположение ее определяется двумя координатами экрана (X, Y), инициализа­ция которых задается встроенным конструктором, а для получения значений те­кущих координат используются встроенные методы класса GetX(), GetY(). Па­раметры X, Y объявлены защищенными элементами базового класса, чтобы производные классы могли их наследовать и использовать.

В качестве основных действий, которые можно совершать с этой фигу­рой, могут быть: показ на экране (Show), удаление (Hide), перемещение (Drag). Описание этих действий представлено чистыми виртуальными функциями без их тел, что делает класс абстрактным. Для этого класса нельзя создать реаль­ный объект на экране, но можно определить указатель или массив указателей на базовый тип и использовать их при создании объектов производных классов. При этом могут применяться только наследуемые виртуальные функции, которые видимы через базовый указатель из производного класса.

Базовому классу наследует производный класс (Figure <- Point), который описывает простейший реальный объект "точка", координаты которой наследу­ются из базового класса (X, Y), а также задается свой параметр видимости точ­ки на экране как защищенный элемент Visible с булевыми значениями. Объяв­лены обычные методы: функция Isvisible(), определяющая видимость точки, и функция Moveto(), задающая новые координаты точки. Как невиртуальные ме­тоды они могут наследоваться производными классами. Их можно сделать вир­туальными, объявив их как virtual, и тогда в производных от Point классах необходимо замещать их "своими" виртуальными методами.

Чистые виртуальные функции класса Figure замещаются конкретными виртуальными функциями (Show(), Hide(), Drag()) в классе Point. Виртуальная функция Drag() использует обычный метод своего класса Moveto(), который определяет новые координаты объекта, а также обычную функцию общего на­значения (т.е. не метод класса) Getdelta(). Последняя определяет направление смеще­ния точки (или другой реальной фигуры) на экране по нажатию пользователем клавиш-стрелок клавиатуры (вправо, влево, вверх, вниз) и окончание процесса смещения объекта на экране при нажатии клавиши <Enter>.

В главной функции программы демонстрируется создание и перемещение точки по экрану, создание "звездного" неба с использованием стандартной функции получения случайных чисел X, Y для координат точки random(max), где max – максимальное число для координаты экрана с установленной разре­шающей способностью. Обработка программы выполняется в среде компилято­ра Borlandc 3.1.

 

Пример 40.

Рассмотрим программу для демонстрации описанной выше постановки задачи.

 

#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"; // путь к библиотеке bgi-файлов

enum Boolean { false, true }; // булевы значения

// прототип обычной функции (не метод), определенной ниже:

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

 

class Figure // абстрактный базовый класс "фигура"

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

int X, Y; // координаты объекта на экране

public:

Figure (int Initx, int Inity) // конструктор базового класса Figure

{ X = Initx, Y = Inity; } // инициализация координат

// встроенные функции-методы получения текущих координат объекта:

int Getx () { return X; }

int Gety () { return Y; }

// чистые виртуальные функции-методы базового класса:

virtual void Show() = 0; // изображение объекта

virtual void Hide() = 0; // стирание объекта с экрана

virtual void Drag (int Dragby) = 0; // перемещение объекта

};

// производный класс Point с открытым доступом к базовому классу Figure:

class Point: public Figure // класс "точка"

{ protected:

Boolean Visible; // параметр видимости точки

public:

Point (int Initx, int Inity); // конструктор класса Point

// виртуальные функции-методы класса Point:

void Show(); // изображение точки на экране

void Hide(); // стирание точки с экрана

void Drag (int Dragby); // перемещение точки по экрану

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

Boolean Isvisible() { return Visible;} // проверка точки на видимость

void Moveto (int Newx, int Newy); // смещение точки на новое место

};

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

Point:: Point (int Initx, int Inity): // конструктор класса Point

Figure (Initx, Inity) // передача аргументов базовому классу

{ Visible = false; // инициализация параметра Point

}

void Point:: Show() // метод изображения точки на экране

{ if (! Visible) // если точка невидима, то

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

putpixel (X, Y, getcolor()); // изображение цветной точки

}

}

void Point:: Hide() // метод стирания точки с экрана

{ if (Visible) // если точка видима, то

{ Visible = false; // установка невидимости точки

putpixel (X, Y, getbkcolor()); // закрашивание точки цветом фона

}

}

void Point:: Moveto (int Newx, int Newy) // метод смещения точки

{ if (Visible) Hide (); // стирание точки

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

Y = Newy;

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

}

void Point:: Drag(int Dragby) // метод смещения фигуры на величину Dragby

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

int Figurex, Figurey; // координаты по X, Y

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

while (Getdelta (Deltax, Deltay)) // цикл перемещения точки:

{ Figurex = Getx()+(Deltax * Dragby); // смещение по X

Figurey = Gety()+(Deltay * Dragby); // смещение по Y

Moveto (Figurex, Figurey); // перемещение точки на новое место

}

}

/* обычная функция (вне классов) определения направления смещения объекта,

которая анализирует код нажатой управляющей клавиши и возвращает

разрешение на смещение (true) в заданном направлении через параметры

ссылки (&), либо отмену смещения (false) при нажатии клавиши <Enter>;

используется в методе Point:: Drag():

*/

Boolean Getdelta (int& Deltax, int& Deltay) // & - ссылки на смещение

{ char Keychar; // переменная символа клавиши

Boolean Quit; // переменная смещения

Deltax = 0; // начальные значения смещений

Deltay = 0;

do { Keychar = getch (); // цикл анализа кода нажатой клавиши:

if (Keychar == 13) // если код = 13 (<Enter>), то

return (false); // конец смещения объекта;

if (Keychar == 0) //если код = 0, то это скэн-код клавиши

{ Quit = true; // подтверждение смещения

Keychar = getch (); // чтение 2- го байта кода

switch (Keychar) // анализ кода управления:

{ case 72: Deltay = -1; break; // стрелка вниз

case 80: Deltay = 1; break; // стрелка вверх

case 75: Deltax = -1; break; // стрелка влево

case 77: Deltax = 1; break; // стрелка вправо

default: Quit = false; // неправильная клавиша

}

}

} while (! Quit); // продолжение цикла чтения кода клавиш

return (true); // разрешение смещения объекта

}

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

{ int graphdriver = DETECT, graphmode; // данные режима графики

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

int n, i, x, y, maxx, maxy, maxcolor, seed, color; // рабочие переменные

int DelayTime; // временная задержка

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

y=200;

Point Apoint (x, y); // создание и инициализация точки

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

Apoint.Drag(20); // перемещение точки по экрану

getch (); // задержка образа экрана

/*

// изображение статического "звездного неба":

cout<< "Enter number of points (<32000): ";

cin >> n; // ввод количества точек

for (i=0; i<n; i++) // цикл создания точек

{ x = random(639); // случайные коодинаты точки (х, у)

y = random(479);

Point Apoint (x,y); // создание и инициализация точки

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

}

*/

/*

// изображение мерцающих цветных точек

maxx = getmaxx(); // количество пикселей по оси Х экрана

maxy = getmaxy(); // количество пикселей по оси Y экрана

maxcolor = getmaxcolor(); // количество цветов

cout<< "Enter number of points (<2000): ";

cin >> n; // ввод количества точек

cout<<"Enter DelayTime (millisecond) = ";

cin >> DelayTime; // ввод временной задержки

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

{ seed = random (RAND_MAX); // начальное случайное число

srand (seed); // запуск генератора случайных чисел

for (i=0; i<n; i++) // цикл создания точек

{ x = random(maxx); // случайные координаты по x

y = random(maxy); // и по y

color = random(maxcolor); // случайное значение цвета

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

Point Apoint (x, y); // создание и инициализация точки

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

}

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

srand (seed); // запуск генератора случайных чисел

for (i=0; i<n; i++) // цикл создания точек

{ x = random (maxx); // случайные коодинаты точки (х, у)

y = random (maxy);

color = random (maxcolor); // случайное значение цвета

if (color == getpixel (x, y)) // если цвет точки = случайному, то

{ Apoint.Moveto (x, y); // смещение точки и

Apoint.Hide(); // стирание

}

}

}

getch (); // задержка экрана

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

}

 

Модульность классов

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

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

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

 

Размещение нескольких классов в одном модуле (файле)

Пример 41.

Рассмотрим разработку независимо компилируемого модуля, в состав ко­торого входят классы Figure и Point на основе программы примера 40, то есть тела классов и функций должны быть скопированы из примера 40. Объявления этих двух классов поместим в файл "figures.h":

 

enum Boolean { false, true }; // булевы значения

class Figure // абстрактный базовый класс "фигура"

{ // тело класса Figure

};

// производный класс Point с открытым доступом к базовому классу Figure:

class Point: public Figure // класс "точка"

{ // тело класса Point

};

Этот процесс может продолжаться неопределенно долго – допускается определять другие производные классы от Figure, Point и т. д. Более того, как известно, допускается множественное наследование от более чем одного базо­вого класса.

Определения всех невстроенных методов указанных двух классов обра­зуют файл figpoint.cpp:

 

#include "figures.h" // подключение файла объявления классов

#include<graphics.h> // подключение библиотеки графики

#include<conio.h> // для функции getch()

// прототип обычной функции (не метод):

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

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

Point:: Point (int Initx, int Init): // конструктор класса Point

Figure (Initx, Inity) // передача аргументов базовому классу

{ Visible = false; // инициализация параметра Point

}

void Point:: Show() // метод изображения точки на экране

{ // тело метода Show

}

void Point:: Hide() // метод стирания точки с экрана

{ // тело метода Hide

}

void Point:: Moveto (int Newx, int Newy) // метод смещения точки

{ // тело метода Moveto

}

void Point::Drag(int Dragby) //метод перемещения точки с дискретом Dragby

{ // тело метода Drag

}

// обычная функция Getdelta:

Boolean Getdelta (int& Deltax, int& Deltay) // & - ссылки на смещение

{ // тело функции Getdelta

}

Рассмотрим головную программу, демонстрирующую возможности классов Figure и Point, которая сохраняется в файле, например, mainpoin.cpp:

 

#include "figures.h" // подключение файла объявления классов

#include<graphics.h> // подключение библиотеки графики

#include<conio.h> // для функции getch()

#include<iostream.h> // для ввода-вывода данных

#include<stdlib.h> // для функций random(), srand(), kbhit()

#include<dos.h> // для функции delay()

const char* path= "c:\\borlandc\\bgi"; // путь к bgi-библиотеке

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

{ // тело функции main

}

 

Создание проекта программы.

Для того чтобы разработать единую программу из нескольких разнотип­ных файлов, необходимо создать проект программы и откомпилировать его в среде BORLANDC3.1. Последовательность разработки проекта программы включает следующие этапы.

1. Создать папку (каталог), где будут размещаться все файлы проекта, например, под именем FIGPOINT.

2. Записать в папку проекта разработанные текстовые файлы (срр-файлы, h-файлы): figpoint.cpp, mainpoin.cpp, figures.h.

3. Выполнить команду меню Project-Open project. В окне Open-Project File перейти в поле Files и, начиная с нужного диска, найти свою папку. Перейти в поле Open-Project File, где вместо символа * набрать имя файла проекта, например, figpoint.prj и щелкнуть кнопку ОК. Внизу экрана появится окно Project:Figpoint с высвеченной строкой.

4. Выполнить команду меню Project-Add item. В окне Add item to Pro­ject List перейти в поле Files. Выделяя срр-файлы в любом порядке, кнопкой Add включить их в файл проекта и закрыть окно кнопкой Done.

5. Запустить файл проекта на компиляцию и выполнение командой Run-Run или клавишами <Ctrl-Enter>.

6. Выполнить отладку исходных файлов, выделяя в окне Project строку файла и нажав <Enter>.

7. После отладки проекта программы получить результаты работы exe-файла.

Проект программы может включать следующие файлы: 1) исходные срр-файлы; 2) файлы заголовков разработчика программы (h-файлы); 3) объектные obj-файлы; 4) файл проекта prj-файл; 5) исполняемый ехе-файл; 6) файл на­стройки среды dsk-файл, 7) копии файлов (bak-файлы).



Поделиться:




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

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


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