Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка2011(12 лаб)_декабрь.doc
Скачиваний:
4
Добавлен:
26.08.2019
Размер:
1.1 Mб
Скачать

Завдання 2.

Розробити метод з використанням принципів простого та множинного спадкування та об’єктів одного базового та декількох похідних класів з віртуальними функціями для моделювання заданих графічних об’єктів, таких як:

  1. Олімпійські кільця.

  2. Концентричні кола.

  3. Концентричні прямокутники.

  4. Будь-яка фігура з різнокольоровими квадратами.

  5. Група трикутників за допомогою ліній.

  6. Група квадратів за допомогою ліній.

  7. Група 6-кутників за допомогою ліній.

  8. Фігура з кіл різного радіусу та ліній.

  9. Кола, вписані в прямокутники.

Варіант 10. Трикутники, вписані в прямокутники.

Основні вимоги до програми:

  1. Базовий клас Shape має включати параметри фігури, конструктор, віртуальну функцію virtual Draw, яка повинна змінюватися для кожного похідного класу в залежності від класу геометричної фігури. В базовому класі використати функцію завдання кольору. Похідні класи можуть бути побудовані по принципу простого і множинного спадкування.

  2. В головній програмі необхідно створити масив геометричних фігур, який буде включати об’єкти різних класів.

  3. Колір фігури задавати за допомогою генератору випадкових чисел.

Теоретичні відомості

Віртуальна функція (virtualfunction) є компонентною функцією базового класу, яка перевизначається в похідному класі. Використовування віртуальних функцій, на відміну від перевантаження функцій,

забезпечує динамічний поліморфізм, реалізовуваний на етапі виконання програми.

При оголошенні віртуальної функції в базовому класі перед її ім'ям указується ключове слово virtual. У похідному класі віртуальна функція перевизначається. Кожне таке перевизначення (overriding) віртуальної функції в похідному класі означає створення конкретного методу. При перевизначенні віртуальної функції в похідному класі ключове слово virtual не указується.

Віртуальну функцію можна викликати як будь-яку іншу компонентну функцію. Проте для підтримки динамічного поліморфізму віртуальні функції викликають через покажчик базового класу, використовуваний як посилання на об'єкт похідного класу. Якщо об'єкт похідного класу, що адресується таким чином, містить віртуальну функцію і віртуальна функція викликається за допомогою цього покажчика, то при компіляції визначається версія віртуальної функції, що викликається, з урахуванням типу об'єкту, на який посилається покажчик. Визначення конкретної версії віртуальної функції здійснюється в процесі виконання програми.

Якщо є декілька похідних класів від базового класу, що містить віртуальну функцію, то при посиланні покажчика базового класу на різні об'єкти цих похідних класів виконуватимуться різні версії віртуальної функції.

Перевизначення віртуальної функції в похідному класі має істотні відмінності від механізму перевантаження функцій: Перш за все, віртуальна функція, що перевизначається, повинна мати ті ж тип, число параметрів і тип значення, що повертається, тоді як перенавантажувана функція повинна мати відмінності в типі і числі параметрів. По-друге, віртуальна функція повинна бути компонентом класу, що не відноситься до перенавантажуваних функцій.

Щоб прояснити значення використовування віртуальних функцій, приведемо приклад, що ілюструє різницю між звичними і віртуальними функціями.

Приклад. Віртуальні і звичні функції.

#include <iostream.h>

class parent

//оголошення базового класу

public:int k;

parent(int а)

//конструктор класу

k=a;

virtual void fv()

// віртуальна функція

cout «"call fv() of base class: cout « k « endl;

void f()

//звична функція

cout «"call f() of base class:cout «k*k «endl;

};

class childl:public parent

public: childl(int x): parent(x)(}

void fv() {

cout «"call fv() of derived class childl:";

cout «(k+1) «endl;

}

void f() //звичайна функція

{

cout «"call f() of derived class childl: ";

cout «(k-1) «endl;

}

};

int main () {

parent *p;// покажчик на базовий клас

parent ez(5);// оголошення об'єкту базового класу

childl chezl(15); // оголошення об'єкту похідного класу

р=&ez;// покажчик посилається на об'єкт базового класу

p->fv(); // виклик функції fv() базового класу

p->f(); // виклик функції f() базового класу

р=Schezl;// покажчик посилається на об'єкт похідного класу

p->fV(); // виклик віртуальної функції fv() похідного класу

p->f(); // виклик звичної функції f() похідного класу

return 0;

}

При виконанні приведеної програми на екран будуть видані наступні результати:

call fv() of base class:5

call f() of base class:25

call fv() of derived class childl:16

call f() of base class:225

Одержані результати показують, що при зверненні до віртуальної функції fv() похідного класу відбувається виклик функції похідного класу. Звернення до звичної функції f() похідного класу приводить до виклику однойменної функції базового класу, а не похідного.

У мові С++ існує поняття чистих (клуні) віртуальних функцій. Такі функції використовуються у разі, коли у віртуальній функції базового класу відсутня значуща дія. При цьому в кожному похідному класі від заданого класу така функція повинна бути обов'язково перевизначена.

Чисті віртуальні функції в базовому класі не визначаються, натомість поміщаються їх прототипи з наступною формою запису:

virtual тип ім’я_функції (список параметрів) = 0;

Привласнення імені функції значення 0 указує на відсутність тіла функції. При такому оголошенні віртуальної функції в базовому класі в кожному похідному класі повинне виконуватися її перевизначення, інакше при компіляції програми буде виявлена помилка.

Розглянуті нами статичний і динамічний варіанти поліморфізму (перевантаження функцій і використовування віртуальних функції відповідно) співвідносять з двома наступними поняттями: раннє зв’язування і пізнє зв’язування.

Раннє зв’язування торкається подій етапу компіляції програми, таких як: настройка при виклику звичних функцій, перенавантажених функцій, невіртуальних компонентних функцій і дружніх функцій. При виклику перерахованих функцій вся необхідна адресна інформація відома при компіляції. Гідністю раннього зв’язування є висока швидкодія одержуваних здійснимих програм. Недоліком раннього зв’язування є зниження гнучкості програм.

Пізнє зв’язування торкається подій, що відбуваються в процесі виконання програми. При виклику функцій з використанням пізнього зв’язування адреса функції, що викликається, до початку виконання програми невідома. Зокрема, об'єктом пізнього зв’язування є віртуальні функції. При доступі до віртуальної функції через покажчик базового класу при виконанні програми визначається тип засланого об'єкту і вибирається версія віртуальної функції для виклику. Перевагою пізнього зв’язування є висока гнучкість виконуваної програми, можливість реакції на події. Недоліком є відносно низька швидкодія програми.

Приклад виконання завдання

Потрібно розробити загальний клас геометричної фігури за допомогою якого можна легко будувати будь – які геометричні фігури на екрані комп’ютера. Розробимо фігури точки, трикутника, чотирикутника та кола з використанням наслідування базового класу.

Структура наслідування класів

СShape

СTriangle

СQuadrangle

СCircle

СPoint

Обґрунтування вибору методу

Дану задачу можливо вирішити декількома способами

а) За допомогою структурного програмування.

Розробляється структура яка описую фігуру, потім ця структура використовується різними функціями.

б) За допомогою модульного програмування

розробляється модуль (набір функцій) для роботи з фігурами на екрані комп’ютера.

в) За допомогою класів.

Розробляється клас фігури, потім він використовується для роботи

розробленою фігурою.

Обираємо третій метод для вирішення поставленої задачі. Перерахуємо основні переваги використання класів для вирішення задач даного типу:

а) Найбільш економічне використання коду.

б) Можливість легко змінити і розширити клас за допомогою таких можливостей класу як наслідування, використання віртуальних функцій, множинне наслідування, використання шаблонів в класах та ін.

в) За допомогою класів можна легко вирішити задачі великої складності, які важко вирішити іншими способами а в деяких випадках і неможливо.

Постановка задачі

Розробити базовий клас для опису фігур на екрані.

Створимо клас який буде представляти будь – яку геометричну фігуру на екрані, та який передбачає можливість її руху. В клас входить вказівник на масив точок фігури. Функції які добавляють точку, забезпечують рух фігури та вивід і виділення її на екрані. Також передбачена структура, яка задає тип руху геометричної фігури на екрані і функції, які обслуговують її.

Типи руху геометричної фігури на екрані.

enum TYPE {MT_LINE=0,/*рух по прямій лінії */

MT_OBERT, /* поворот фігури */

MT_RESHAPE,/* зміна її розмірів */

MT_USER};/* тип руху що задає користувач */

class СShape{

protected:

POINT*p;/*вказівник на масив точок які зберігають координати точок на екрані*/

int count;/*кількість точок в фігури*/

int current; /*поточна точка фігури*/

PMOVE m_move;/*структура яка зберігає в собі тип руху фігури на екрані комп’ютера*/

public:

CShape(int); /* Конструктор вхідний параметр якого задає кількість точок на екрані комп’ютера */

virtual~shape();/* деструктор */

void set_point(int,int); // встановлює координати точки фігури

void set_move(PMOVE); /* задає тип руху фігури на екрані */

virtual void show() {};/* Віртуальна функція показує фігуру на екрані монітора, вона в кожному класі насліднику повинна переоприділена*/

virtual void move();/* Рух фігури на екрані монітора */

virtual void hide();/* стирає фігуру на екрані комп’ютера */

PMOVE get_move(){ return m_move;} // повертає структуру move

};

CShape::CShape(int count){

//cout <<"constructor shape" <<count <<endl;

if (count<1) count =1;

current=0;

this->count=count;

p=new POINT[this->count];

}

void CShape::hide(){

setwritemode(XOR_PUT);

show();

for(int i=0;i<count;i++) putpixel(p[i].x,p[i].y,0);

}

void CShape::set_move(PMOVE move) {

m_move=move;

}

void CShape::set_point(int x,int y) {

if (current<count) {

p[current].x=x;

p[current].y=y;

current++;

}

//cout<<current<<endl;

}

CShape::~CShape() {

if(p!=NULL) {

delete[] p;

}

}

void CShape::move() {

if(m_move->erase) hide();

if(m_move->type=MT_LINE) {

for(int i=0;i<count;i++) {

p[i].x+=m_move->stepx;

p[i].y+=m_move->stepy;

}

}

if (m_move->type=MT_RESHAPE) {

for (int i=0;i<count;i++) {

p[i].x+=m_move->stepx;

p[i].y+=m_move->stepy;

}

}

if(m_move->type=MT_OBERT) {

for(int i=0;i<count;i++) {

p[i].x+=m_move->stepx;

p[i].y+=m_move->stepy;

}

}

show();

}

Розробити клас трикутника, чотирикутника, кола, точки

Клас точки

Даний клас добавлено один член класу який задає колір точки та переоприділений метод show базового класу. Також в конструкторі викликається конструктор базового класу в якому параметр конструктора задає кількість точок в класі. В даному класі використовується одна точка.

class CPoint:public shape {

private:

int color; /*колір точки*/

public:

CPoint();/*конструктор*/

virtual~point();/*деструктор*/

virtual void show();//переоприділення функції показу фігури

};

/*реалізація конструктора*/

CPoint::CPoint():shape(1) {

}

/*реалізація деструктора*/

CPoint::~CPoint(){

}

реалізація функції показу(show)

void CPoint::show(){

setcolor(2);

line(p[0].x,p[0].y,p[0].x,p[0].y);

}

Клас трикутника

Реалізація подібна попередньому класові за винятком кількості точок які зберігає в собі клас (3 точки)

class CTriangle:public CShape {

public:

CTriangle();

~CTriangle();

virtual void show();

};

CTriangle::CTriangle():shape(3) {

}

CTriangle::~CTriangle() {

}

void Сtriangle::show() {

moveto(p[0].x,p[0].y);

lineto(p[1].x,p[1].y);

lineto(p[2].x,p[2].y);

lineto(p[0].x,p[0].y);

}

Клас чотирикутника

Аналогічно попередньому класові за винятком кількості точок для даного класу кількість точок рівняється 4

class CQuadrorange: public CShape {

public:

CQuadrorange();

~CQuadrorange();

virtual void show();

};

CQuadrorange::CQuadrorange():shape(4){

}

CQuadrorange::~CQuadrorange() {

}

void CQuadrorange::show() {

moveto(p[0].x,p[0].y);

lineto(p[1].x,p[1].y);

lineto(p[2].x,p[2].y);

lineto(p[3].x,p[3].y);

lineto(p[0].x,p[0].y);

}

Клас кола

В даному класі використовується одна точка яка задає координати центра кола. Добавлено один член класу, який зберігає в собі радіусом кола, і одну функцію, яка задає радіус кола. Переоприділена віртуальна функція show, яка відповідає за відображення геометричної фігури на екрані.

class CColo: public CShape {

private:

int radius; /* зберігає радіус кола */

public:

CColo();

~CColo();

void set_radius(int); /*функція що встановлює радіус кола*/

virtual void show();

};

/* задає тільки одну точку – центр кола на екрані */

CColo::CColo():shape(1) {

radius =0 ;

}

CColo::~CColo() {

}

void CColo::set_radius(int r) {

radius=r;

}

void CColo::show() {

circle(p[0].x,p[0].y,radius);

}

Розробити контейнер класів фігур для управління групою фігур на екрані.

Створимо клас за допомогою якого можна керувати групою геометричних фігурами на екрані. В класі створено вказівник на групу фігур. Добавлені функції руху всіх геометричних фігур та добавлення геометричних фігур в контейнер.

class CConteiner {

private:

CShape *CShapes[MAX_SHAPE];/*масив фігур якими потрібно керувати на екарані монітора*/

int count;/*кількість заданих фігур*/

int exit;

public:

CConteiner();/*конструктор */

~CConteiner();/*деструктор*/

void action();/*виконує рух групи фігур на екрані*/

void add_shape(shape*);/*добавляє фігуру до контейнера */

};

CConteiner::CConteiner() {

count=0;

}

CConteiner::~CConteiner() {

}

void CConteiner::add_shape(shape* fig) {

if (count<MAX_SHAPE) {

shapes[count]=fig;

count++;

}

}

void CConteiner::action() {

for (int i=0;i<count;i++) {

shapes[i]->get_move()->current_movex=0;

shapes[i]->get_move()->current_movey=0;

}

while (1) {

exit=1;

for (int i=0;i<count;i++) {

if((shapes[i]->get_move()->current_movex<shapes[i]->get_move()->endx)

||(shapes[i]->get_move()->current_movey<shapes[i]->get_move()->endy)){

shapes[i]->move();

shapes[i]->get_move()->current_movex+=shapes[i]->get_move()->stepx;

shapes[i]->get_move()->current_movey+=shapes[i]->get_move()->stepy;

exit=0;

delay(10);

}

}

if (exit) break;

}

}

Літинг програми

#include <iostream.h>

#include <conio.h>

#include <graphics.h>

#include <stdio.h>

#include <math.h>

#include <dos.h>

структура точки яка зберігає в собі координати по х и по y

typedef struct _point {

int x;

int y;

} POINT, *PPOINT;

/* об’явлення типу функції для вирахування координат при русі фігури на екрані /*

typedef double (func)(double);

/* об’явлення типів руху фігури */

enum TYPE {MT_LINE=0, /* рух по прямій лінії */

MT_OBERT, /*поворот фігури */

MT_RESHAPE, /* зміна розмірів фігури */

MT_USER} ; /* рухи користувачів, тип руху може задавати користувач за допомогою функції */

/*Структура, яка зберігає в собі дані про рух фігури */

typedef struct _move {

TYPE type; /* тип руху */

int stepx; /* крок по х */

int stepy; /* крок по y */

int endx; /* кінцева точка по х */

int endy; /* кінцева точка по y */

int centr_x; /* центр координат */

int centr_y; /* центр координат */

func*func_x; /* функції руху по х */

func*func_y; /* функції руху по y */

void*tag; /* можна зберігати різні дані */

int erase; /* визначає стирати фігури при русі чи ні */

int current_movex; /* зарезервовано програмою */

int current_movey; /* зарезервовано програмою */

} MOVE,*PMOVE;

class shape {

protected:

POINT* p; /* вказівник на масив точок */

int count; /* кількість точок */

int current; /* поточна точка */

PMOVE m_move; /* структура типу руху фігури */

public:

shape(int); /* конструктор вхідний параметр кількість точок у фігури */

virtual~shape();/* деструктор */

void set_point(int,int); /* додає точку */

void set_move(PMOVE); /* встановлює тип руху */