Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Не підтверджено.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
3.08 Mб
Скачать

Void Show()

{

cout« Суг("Трикутник з висотою ")« x; cout« Cyr(" і основою") « у; cout« Cyr(" мас площу ") « x * 0.5 * у ; cout« Суг(" кв. од. ")« endl;

}

};

class rectangle: public figure { public:

Void Show()

{

cout« Суг("Прямокутник розмірами") « x «" x" « y; cout« Cyr(" мас площу ") «х* у; cout« Cyr(" кв. од. ")« endl;

}

};

Int mainO

// Створення покажчика на об'єкт базового типу // Створення об'єктів похідних типів // Створення об'єкта похідного типу

{

figure *p; triangle ObjT; rectangle ObjR;

р = &ObjT; // Присвоєння покажчику адреси об'єкта похідного класу

p->Set(10.3,5.5);

p->Show();

р = &ObjR; // Присвоєння покажчику адреси об'єкта похідного класу

p->Set(10.3,5.5); p->Show(); getchO; return 0;

}

Ось ж виглядають результати виконання цієї програми:

Трикутник з висотою 10.3 і основою 5.5 має площу 28.325 кв. од.

Прямокутник розмірами 10.3 х 5.5 має площу 56.65 кв. од.

У цьому коді програми зверніть увагу на те, що під час роботи з класами rec­tangle і triangle використано однаковий інтерфейс, хоча у кожному з них реалізовано власні методики обчислення площі відповідних об'єктів.

Як Ви думаєте, використовуючи оголошення базового класу figure, можна ви­вести похідний клас сігсіе для обчислення площі круга за заданим значенням раді­уса? Відповідь: так. Для цього достатньо створити новий похідний тип, який би обчислював площу круга. Потужність віртуальних функцій опирається на той факт, що програміст може легко вивести новий тип, який розділятиме загальний інтерфейс з іншими "спорідненими" об'єктами. Ось, наприклад, як це можна зро­бити в нашому випадку: class сігсіе : public figure { public:

void ShowO {

cout « CyrfKpyr з радіусом ") « x;

cout « Cyr(" має площу ") « 3.14 * x * x « endl;

}

};

Перш ніж випробувати клас сігсіе в роботі, розглянемо уважно визначення функції ShowO- Зверніть увагу на те, що в ній використовується тільки одне зна­чення змінної х, яка повинна містити радіус круга1. Проте, згідно з визначенням функції Set(), у базовому класі figure їй передається два значення, а не одне. Оскіль­ки похідному класу сігсіе не потрібне друге значення, то що ми можемо зробити? Є два способи вирішити цю проблему. Перший (і одночасно найгірший) полягає у тому, що ми могли б, працюючи з об'єктом класу сігсіе, просто викликати функцію SetO, передаючи їй як другий параметр фіктивне значення. Основний недолік цьо­го методу - відсутність чіткості у задаванні параметрів і потрібно пам'ятати про спеціальні винятки, які порушують дію механізму - один інтерфейс, багато мето­дів.

Є вдаліший спосіб вирішення цього питання, який полягає у наданні пара­метру функції SetO значення, яке діє за замовчуванням. У цьому випадку під час виклику функції SetO Для круга потрібно задавати тільки радіус. Під час виклику ж функції SetO Для трикутника або прямокутника задають обидва значення. Нижче показано програму, у якій реалізовано цей підхід.

Код програми 6.6. Демонстрація механізму надання параметру віртуальної функції значення, що діє за замовчуванням

#include <vcl>

#include <conio>

#include <iostream> // Для потокового введення-виведення using namespace std; // Використання стандартного простору імен

class figure { protected:

double x, у; public:

void Set(double _x, double _y=0) {x = _x; у = _y;} virtual void Show()

{

cout« Суг("У цьому класі вираз для обчислення площі не визначено.") « endl;

}

};

class triangle: public figure { public:

void Show()

{

cout« Суг("Трикутник з висотою ")« x; cout« Cyr(" і основою") « у; cout« Cyr(" має площу ") « x * 0.5 * у ; cout« Суг(" кв. од. ")« endl;

}

};

class rectangle: public figure { public:

void Show()

{

cout« Суг("Прямокутник розмірами") « x «" x" « y; cout« Cyr(" має площу ") «х* у; cout« Cyr(" кв. од. ")« endl;

}

};

class circle: public figure { public:

void Show()

{

cout« Cyr("Kpyr з радіусом") « x;

cout« Cyr(" має площу ") «З .14159 * x * x; cout« Cyr(" кв. од.") << endl;

}

};

int mainO

// Створення покажчика на об'єкт базового типу // Створення об'єкта похідного типу // Створення об'єкта похідного типу // Створення об'єкта похідного типу

// Присвоєння покажчику адреси об'єкта похідного класу

{

figure *p; triangle ObjT; rectangle ObjR; circle ObjC;

// Присвоєння покажчику адреси об'єкта похідного класу

p = &ObjT; p->Set(10.3,5.5); p->ShowO;

p = &ObjR;

p->Set(10.3, 5.5); p->ShowO;

р = &ObjC; // Присвоєння покажчику адреси об'єкта похідного класу

p->Set(9.67);

p->ShowO;

getchO; return 0;

}

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

Трикутник з висотою 10.3 і основою 5.5 має площу 28.325 кв. од.

Прямокутник розмірами 10.3 х 5.5 має площу 56.65 кв. од.

Круг з радіусом 9.67 має площу 293.767 кв. од.

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

  1. Поняття про суто віртуальні функції та абстрактні класи

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

Існує два способи оброблення таких ситуацій. Перший (він показаний у наве­деному вище прикладі коду програми) полягає у виведенні функцією застережно­го повідомлення. Можливо, такий підхід і буде корисний у певних ситуаціях, але здебільшого він просто неприйнятний. Наприклад, можна уявити собі віртуальні функції, без визначення яких у існуванні похідного класу взагалі немає ніякого сенсу. Розглянемо клас triangle. Він абсолютно зайвий, якщо у ньому не визначити функцію Show(). У цьому випадку варто створити метод, який би гарантував, що похідний клас дійсно містить усі необхідні функції. У мові програмування C++ для вирішення цього питання і передбачено суто віртуальні функції.

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

virtual тип ім'я_фуищї(перелік_парететрів) = 0;

У цьому записі під елементом тип маємо на увазі тип значення, що повер­тається функцією, а елемент ім'я_функції- використовуване у програмі її ім'я. Поз­начення = 0 є ознакою того, що функція тут оголошується як суто віртуальна. Нап­риклад, в наступній версії визначення базового класу figure функція Show() вже представлена як суто віртуальна: class figure { double х, у; public:

void Set(double _x, double _y=0) {x = _x; у = _y;} virtual void Show() = 0; // Суто віртуальна функція

};

Оголосивши функцію суто віртуальною, програміст створює умови, при яких похідний клас просто вимушений мати визначення власної її реалізації. Без цього компілятор видасть повідомлення про помилку. Наприклад, спробуйте скомпілю­вати цю модифіковану версію програми обчислення площ геометричних фігур, у якій з класу circle видалено визначення функції Show().

Код програми 6.7. Демонстрація не коректної програми, яка у класі circle немає перевизначення функції Show()

#include <vcl>

#include <conio>

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

class figure { protected:

double x, у; public:

void Set(double _x, double _y) {x = _x; у = _y;} virtual void Show() = 0; // Суто віртуальна функція

};

class triangle: public figure { public: