Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C _Учебник_МОНУ

.pdf
Скачиваний:
206
Добавлен:
12.05.2015
Размер:
11.12 Mб
Скачать

Об’єктно-орієнтоване програмування

519

 

 

paral d(StrToFloat(Edit1->Text),StrToFloat(Edit2->Text), StrToFloat(Edit5->Text));

// Виведення результатів обчислень об‟єктів

Memo1->Lines->Add("Прямокутник = "+FloatToStr(b.arear())); Memo1->Lines->Add("Циліндр = "+FloatToStr(c.areac())); Memo1->Lines->Add("Паралелепіпед = "+FloatToStr(d.areap()));

}

Форма з результатами обчислень має вигляд:

14.8 Поліморфізм

При успадкуванні деякі методи класу мають можливість бути замінені на інші. Так, батьківський клас способів зв‟язку (див. рис. 14.1) матиме узагальнений метод – спосіб передавання інформації. У похідних класах цей метод буде уточнено: радіозв‟язок – це передавання радіосигналів від радіостанції до радіоприймачів, поштовий зв‟язок – це перевезення транспортом поштових відправлень (посилок, бандеролей, листів тощо), супутниковий зв‟язок – це передавання сигналів на супутники та прийом цих сигналів супутниковими антенами. Отже, одне ім‟я методу використовується для розв‟язання декількох схожих, але технічно різних задач. Таке змінювання змісту методу називається поліморфізмом. Взагалі поліморфізм (polymorphism) (від грецької – polymorphos) – це здатність об‟єкта змінювати форму (poly означає багато, а morphism має відношення до змінювання форми). Отже поліморфний об‟єкт, являє собою об‟єкт, який може набувати різноманітних форм.

Мета поліморфізму в об‟єктно-орієнтованому програмуванні – використання одного імені для схожих дій (методів) класу. Виконання кожної конкретної дії буде визначено типом даних. У різних мовах програмування поліморфізм реалізовано різноманітними засобами. Наприклад, у Pascal та C++ його реалізовано за допомогою механізму віртуальних функцій. Разом з тим, у мові С++ поліморфізм підтримується недостатньо, наприклад, обчислення абсолютного значення змінної можна виконати трьома функціями: abs(), labs() та fabs(). Ці функції обчислюють та повертають абсолютне значення змінних цілого, довгого цілого та дійсного типів відповідно. У Pascal така задача виконується однією функцією abs(). У мові С++ вибір конкретної функції для цієї задачі здійснює програміст відповідно до типу даних.

520

Розділ 14

 

 

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

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

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

virtual <тип> <ім‟я_функції> (<список_параметрів>);

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

Базовий клас здебільшого буває абстрактним класом, об‟єкти якого ніколи не буде реалізовано. Такий клас існує з єдиною метою – бути батьківським відносно похідних класів, об‟єкти яких буде реалізовано, для створювання ієрархічної структури. Наприклад, клас “Способи зв‟язку” має метод “Засоби передавання інформації”, який не може бути реалізовано для об‟єктів цього класу, а в похідних класах цей метод має конкретне значення: для класу “Радіозв‟язок”

– це радіосигнал, для класу “Поштовий зв‟язок” – це транспорт для перевезення поштових відправлень тощо. Але, якщо об‟єкти батьківського (абстрактного) класу не призначені для реалізації, то в який спосіб захистити базовий клас від використання не за призначенням? – Захистити його треба програмно. Для цього достатньо ввести у клас хоча б одну суто віртуальну функцію (pure virtual function).

Об’єктно-орієнтоване програмування

521

 

 

Для суто віртуальної функції використовується формат: virtual <тип> <ім‟я_функції> (<список_параметрі в >) = 0;

Ключовою частиною цього оголошення є присвоєння суто віртуальній функції значення нуль. Це повідомляє компіляторові, що в батьківському класі немає тіла функції. Якщо функцію задано як суто віртуальну, то це означає, що вона обов‟язково повинна бути заміненою в кожному похідному класі, інакше при компіляції виникне помилка. Отже, створення суто віртуальних функцій – це гарантія того, що похідні класи забезпечать їхнє перевизначення. Якщо клас містить хоча б одну суто віртуальну функцію, то його називають абстрактним класом. Оскільки в абстрактному класі є хоча б одна функція, в якої відсутнє тіло функції, технічно такий клас не є повністю визначений, і для нього неможливо створити жодного об‟єкта. Тому абстрактні класи можуть бути лише похідними.

Основні концепції поліморфізму в мові програмування С++:

поліморфізм – це властивість об‟єкта змінювати форму під час виконання програми;

для створення методів, які є поліморфними, у програмі необхідно ви-

користовувати віртуальні (virtual) функції;

якщо об‟єкти батьківського (абстрактного) класу не призначені для реалізації, необхідно ввести у клас хоча б одну суто віртуальну функцію;

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

для створення поліморфного об‟єкта в С++ слід використовувати вказівник на об‟єкт батьківського класу;

поліморфізм спрощує програмування та створення поліморфних об‟єктів і зменшує складність програм.

Розглянемо механізм поліморфізму на прикладах.

Приклад 14.6 Створити поліморфний клас – телефон, який відображує (імітує) телефонні операції: набирання номера, дзвінок, роз‟єднання та індикацію зайнятості лінії зв‟язку. Передбачити у програмі можливість імітувати, за бажанням, роботу дискового, кнопкового чи платного телефону (25 копійок, щоб подзвонити). Тобто об‟єкти класу мають бути поліморфними – від одного дзвінка до іншого об‟єкт-телефон має змінювати форму (своє призначення).

Розв‟язок. Створимо батьківський клас phone – дисковий телефон з полем number – номером телефону і методами: dial – набирання номера, ring – дзвінок, answer – очікування відповіді, hangup – роз‟єднання. Далі створимо два похідні класи: touch_tone кнопковий телефон та pay_phone – платний телефон, причому в цих класах будуть визначені свої власні методи dial.

Код програми з виведенням результатів у консольному режимі має такий вигляд:

#include <iostream.h> #include <string.h> #include <conio.h>

522

Розділ 14

 

 

class phone // Оголошення батьківського класу – дисковий телефон

{ protected:

char number[13] ; public:

virtual void dial (char *number) // Віртуальна функція

{cout<<"Набирання номера "<<number<< endl;; } void answer(void)

{cout << "Очікування відповіді" << endl; } void hangup(void)

{cout << "Дзвінок виконано – покладіть слухавку " << endl; } void ring(void)

{cout << " Дзвінок, дзвінок, дзвінок " <<endl; }

phone(char *number)

 

{ strcpy(phone::number, number); };

// Конструктор

}; // Оголошення похідного класу – кнопковий телефон

class touch_tone : phone { public:

void dial(char * number)

{ cout << "Пік, пік. Набирання номера" << number << endl; } touch_tone(char *number):phone(number){ } // Конструктор

}; // Оголошення похідного класу – платний телефон

class pay_phone: phone { private:

int amount; public:

void dial(char *number)

{ cout<<"Будь ласка, сплатіть "<<amount<<" копійок"<<endl; cout<<"Набирання номера "<<number<<endl;

}

pay_phone(char *number, int amount): // Конструктор phone(number) { pay_phone::amount = amount; }

};

// Головна програма – створення та робота з об‟єктами класу void main(void)

{// Створення об‟єкта rotary – дисковий телефон phone rotary("201-555-1212"); rotary.dial("602-555-1212");

//Створення об‟єкта home_phone – кнопковий телефон touch_tone home_phone("555-1212"); home_phone.dial("555-1212");

//Створення об‟єкта city_phone – платний телефон pay_phone city_phone("702-555-1212", 25); city_phone.dial("212-555-1212");

cout << " Поліморфні об’єкти: " << endl;

//Створення об‟єкта-вказівника на батьківський клас – дисковий телефон phone *poly_phone = &rotary;

Об’єктно-орієнтоване програмування

523

 

 

poly_phone->dial("818-555-1212");

// Змінення форми об‟єкта на кнопковий телефон poly_phone = (phone *) &home_phone; poly_phone->dial("303-555-1212");

// Змінювання форми об‟єкта на платний телефон poly_phone = (phone *) &city_phone; poly_phone->dial("212-555-1212"); getch();

return ;

}

Результати виконання програми:

Набирання номера 602-555-1212 Пік, пік. Набирання номера 212-555-1212 Будь ласка, сплатіть 25 копійок Набирання номера 212-555-1212

Поліморфні об’єкти: Набирання номера 818-555-1212

Пік, пік. Набирання номера 303-555-1212 Будь ласка, сплатіть 25 копійок Набирання номера 212-555-1212

Наведені класи телефону мають однакову назву, але різну за виконанням функцію dial(). Цю функцію було визначено як віртуальну (з ключовим словом virtual) у батьківському класі phone.

Для створення поліморфного об‟єкта у програмі використано вказівник на об‟єкт батьківського класу (phone *poly_phone). Для змінювання форми об‟єкта цьому вказівникові надано адресу об‟єкта похідного класу:

poly_phone = (phone *) &home_phone;

Вираз у круглих дужках (phone *), записаний за оператором присвоєння, є операцією зведення типів, яка сповіщає компілятору C++, що вказівнику на змінну одного типу (phone) треба надати адресу змінної іншого типу (home_phone). Оскільки програма присвоює вказівнику об‟єкта poly_phone адреси різних об‟єктів, то цей об‟єкт може змінювати свою форму, а, отже, він є поліморфним. Згідно з програмою, об‟єкт poly_phone змінює форму з дискового телефону на кнопковий, а після цього – на платний.

Приклад 14.7 Побудувати батьківський клас Tvirob з полями: назва виробу, кількість випробувань виробу на якість, кількість виробів, які успішно пройшли випробування, кількість таких з них, які виявилися нестандартними. Метод класу визначає якість за формулою:

Q = кількість випробувань виробу / кількість успішних випробувань.

Побудувати похідний клас TPvirob, який крім батьківських полів має додаткове поле (P) – кількість виробів, які не відповідають стандарту (за розміром, кольором тощо) та визначає якість виробу за новою формулою:

Qр = Q – 2* P / кількість випробувань виробу на якість.

524

Розділ 14

 

 

Оголосити та визначити елементи класів в окремих файлах. У головному модулі створити об‟єкти з вказівником та без нього і вивести інформацію у вікно форми. При написанні програми застосувати поняття поліморфізму класів.

Розв‟язок. У батьківському класі Tvirob оголосимо поля: NAME – назва виробу, kolp – кількість випробувань виробу на якість (у розділі protected), kolpr – кількість виробів, які успішно пройшли випробування. Методи батьківського класу – конструктор, rachest() – віртуальна функція розрахунку якості (у розділі protected), Info() – функція формування рядка інформації (не віртуальна).

Клас-нащадок TPvirob має одне, додаткове до батьківських, поле kolV – кількість таких виробів, які успішно пройшли випробування, але виявилися нестандартними. Методи класу-нащадка – конструктор та віртуальна функція rachest(void) для розрахунку якості за новою формулою, яка перекриває функцію якості батьківського класу.

Текст програми:

Файл Unit2.h – оголошення батьківського класу

#ifndef Unit2H #define Unit2H #include <vcl.h>

//------------------------------------------------------------

class Tvirob

{ private: int kolpr;

AnsiString NAME; protected:

virtual float rachest(void); // Віртуальна функція int kolp;

public:

Tvirob (AnsiString iNAME,int ikolp, int ikolpr); // Конструктор

AnsiString Info(void);

// Без перекриття

};

 

#endif

 

Файл Unit2.cpp – визначення методів батьківського класу

#pragma hdrstop #include "Unit2.h"

//------------------------------------------------------------

#pragma package(smart_init)

Tvirob::Tvirob(AnsiString iNAME,int ikolp,int ikolpr)//Конструктор { NAME=iNAME;kolp=ikolp; kolpr=ikolpr;}

float Tvirob::rachest()

// Функція розрахунку якості Q

{ return 1.0*kolpr/kolp; };

 

AnsiString Tvirob::Info()

// Функція формування рядка-інформації s

{ AnsiString s=NAME+" Q = "+FloatToStrF(rachest(),ffFixed,6,2); return s; }

Об’єктно-орієнтоване програмування

525

 

 

Файл Unit3.h – оголошення похідного класу

#ifndef Unit3H #define Unit3H #include "Unit2.h"

//------------------------------------------------------------

class TPvirob : public Tvirob { private:

int kolV; // Додаткове поле нащадка protected:

float rachest(void); public:

TPvirob(AnsiString iNAME,int ikolp, int ikolpr, int ikolV);

};

#endif

Файл Unit3.cpp – визначення методів похідного класу

#pragma hdrstop #include "Unit3.h"

//------------------------------------------------------------

#pragma package(smart_init) // Конструктор нащадка

TPvirob::TPvirob(AnsiString iNAME,int ikolp,int ikolpr,int ikolV)

:Tvirob (iNAME,ikolp,ikolpr)

{kolV=ikolV; }

float TPvirob::rachest()

{ float Q=Tvirob::rachest(); return Q-2.0*kolV/kolp; }

Файл Unit1.cpp – головний модуль проекту

#include <vcl.h> #pragma hdrstop #include "Unit1.h" #include "Unit2.h" #include "Unit3.h"

//------------------------------------------------------------

#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1;

//------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)

{

}

//------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)

{AnsiString NAME = Edit1->Text; int kp = StrToInt(Edit2->Text);

526

Розділ 14

 

 

int kpr = StrToInt(Edit3->Text);

Tvirob vr(NAME,kp,kpr); // Об‟єкт батьківського класу

Memo1->Lines->Add("базовий об'єкт: " + vr.Info()); int kol = StrToInt(Edit4->Text);

TPvirob vg(NAME,kp,kpr,kol); //Об‟єкт класу-нащадка Memo1->Lines->Add("об'єкт-нащадок: "+ vg.Info()); Memo1->Lines->Add(" об'єкт з вказівником ");

Tvirob *p0,*p1; //Оголошення змінних об‟єктів з вказівником

p0 = new Tvirob(NAME,kp,kpr); // Об‟єкт батьківського класу

Memo1->Lines->Add("базовий об'єкт: "+p0->Info());

p1 = new TPvirob(NAME,kp,kpr,kol); //Об‟єкт класу-нащадка Memo1->Lines->Add("об'єкт-нащадок : "+ p1->Info());

}

//-----------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender) { Memo1->Clear();

}

У головному модулі проекту вводитимемо дані через компоненти Edit та виводити інформацію до Memo. Створимо об‟єкти батьківського класу як змінні без вказівника та з вказівником. Форма проекту з результатами обчислень для двох варіантів введених даних:

14.9 Класи та об’єкти бібліотеки компонентів

14.9.1Ієрархія класів бібліотеки візуальних компонентів

Усередовищі С++ Builder компоненти (наприклад: Button, StringGrid, Edit тощо) є спеціальними класами, які організовано у бібліотеці Visual Components Library (VCL). Класи компонентів створюються розробниками програмного забезпечення і використовуються програмістами при роботі в інтегрованому середовищі програмування С++ Builder. Кожен клас компонентів складається з таких елементів: властивості (properties), методи (methods) та події

(events). Властивості компонентів (наприклад: Caption, Top, Left, Font тощо) –

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

Об’єктно-орієнтоване програмування

527

 

 

приклад: Show(), Hide(), SetFocus(), GetParentComponent() тощо) реалізують певну стандартну поведінку компонентів. Події – це спеціальні методи класу, за допомогою яких компонент повідомляє користувача про те, що над ним виконали якусь дію (наприклад: Click, MouseDown, KeyPress тощо) і буде виконано програмний код, передбачений програмістом. Перелік усіх елементів компонентів можна переглянути за допомогою довідкової системи Help.

Класи компонентів мають ієрархічну структуру. Наведемо фрагмент дерева класів бібліотеки візуальних компонентів C++ Builder (рис. 14.3).

TObject

TPersistent

TComponent

TControl TMainMenu TTime

TGraphicControl

 

TWinControl

 

TLabel

 

 

 

 

 

 

TImage

TEdit

 

TMemo

 

TStringGrid

 

TButton

 

 

 

 

 

 

 

 

 

 

Рис. 14.3. Ієрархія класів бібліотеки VCL

(курсивом позначено класи компонентів)

Клас TObject – це базовий клас, який є спільним предком усіх класів. Він забезпечує такі методи: здібність конструктора створювати, а деструктора знищувати об‟єкт класу в динамічній пам‟яті, опрацювання повідомлень Windows тощо. До методів цього класу не слід звертатися зі своїх програм, оскільки вони можуть бути перевизначеними у похідних класах.

Клас TPersistent – похідний клас від TObject. Це абстрактний клас, який визначає низку методів копіювання об‟єктів подібних класів, повернення власника даного об‟єкта, завантаження та збереження спеціальної інформації.

Клас TComponent – похідний клас від TPersistent та батьківський клас для усіх компонентів. Цей клас забезпечує відображення компонентів та маніпуляцію з ними в редакторі форм. Не слід створювати об‟єкти цього класу у своїх програмах, але можна використовувати цей клас для створювання похідних класів. Похідними від цього класу є класи невізуальних компонентів (наприклад: TMainMenu, TTimer, OpenDialog тощо), які є видимі як піктограми на формі під час проектування, але їх не видно під час виконання.

528

Розділ 14

 

 

Клас TControl – базовий клас усіх візуальних компонентів. Цей клас встановлює властивості компонента: розташування, розміри, видимість, доступність, колір, шрифт тощо. У табл. 14.1 наведено заголовки та призначення деяких методів, притаманних об‟єктам класу TControl, а отже, усім нащадкам цього класу.

 

Таблиця 14.1

Деякі методи класу TControl

 

 

Прототип методу

Призначення

 

 

DYNAMIC void __fastcall

Викликає функцію відгуку на подію OnClick, яка

Click(void);

відбувається при клацанні лівою кнопкою миші

DYNAMIC void __fastcall

Викликає функцію відгуку на подію OnDblClick,

DblClick(void);

яка відбувається у разі подвійного клацання

 

лівою кнопкою миші

DYNAMIC void __fastcall

Викликає функцію відгуку на подію OnMouseDown,

MouseDown(TMouseButton

яка відбувається при клацанні кнопки миші.

Button,

Параметр Button визначає кнопку: ліва, середня

Classes::TShiftState

чи права. Параметр Shift зазначає, чи було

Shift, int X, int Y);

натиснуто якусь з кнопок клавіатури: Shift,

 

 

Ctrl, Alt. Параметри X, Y – координати миші

DYNAMIC void __fastcall

Викликає функцію відгуку на подію

MouseMove

OnMouseMove, яка відбувається при переміщуван-

(Classes::TShiftState

ні курсора

Shift, int X, int Y);

миші. Параметри є аналогічні до наведених вище

 

DYNAMIC void __fastcall

Повідомляє про те, що розміри об‟єкта змінилися

Resize(void);

 

 

 

DYNAMIC void __fastcall

Викликає функцію відгуку на подію OnMouseUp,

MouseUp

яка відбувається при відпусканні кнопки миші.

(TMouseButton Button,

Параметри є аналогічні до параметрів методу

Classes::TShiftState

MouseDown

Shift, int X, int Y);

 

Усі ці методи у класі TControl вміщено до секції protected. Здебільшого наведені методи не викликаються безпосередньо, тобто за явними звертаннями у програмах. Іноді їхні прямі виклики навіть заборонено (наприклад для методу Resize()). Проте всі вони є доступні для перекриття, на що вказує ключове слово DYNAMIC – аналог слова virtual для компонентів.

Клас TWinControl – базовий клас усіх віконних компонентів. У цьому класі введено віконний дескриптор (window handle), реалізовано здібність приймати фокус введення інформації та обслуговувати події клавіатури. У табл. 14.2 наведено деякі з методів класу TWinControl. Слід звернути увагу на використання слова virtual в оголошенні функції SetFocus замість слова DYNAMIC у переважної більшості компонентів.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]