Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Властивості класів,,,.docx
Скачиваний:
5
Добавлен:
26.10.2018
Размер:
252.91 Кб
Скачать

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

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

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

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

3.5.1 Віртуальні функції

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

У C++ поліморфна функція прив'язується до однієї з можливих одноіменних функцій тільки у момент виконання, коли їй передається конкретний об'єкт класу. Іншими словами, виклик функції в початковому тексті програми лише позначається, без точної вказівки на те, яка саме функція викликається. Такий процес відомий як пізнє скріплення. Лістинг 3.9 показує, до чого може привести не поліморфну поведінку звичайних функцій-членів.

I class Parent { public:

double FI(double x) { return x*x; }

double F2(double x) { return FI(x) /2; }

 

 class Child : public Parent { public:

double FI(double x) { return x*x*x; }};

 

void  main() {

Child child;

cout “ child.F2(3) “ end1;

}

Лістинг 3.9. Невизначене пізнє скріплення.

Клас Parent містить функції-члени F1 і F2, причому, F2 викликає E1. Клас Child, похідний від класу Parent, успадковує функцію F2, проте перевизначає функцію F1. Замість очікуваного результату 13.5 програма видасть значення 4.5. Річ у тому, що компілятор відтранслюватиме вираз child. F2 (3) в звернення до успадкованої функції Parent: :F2, яка у свою чергу викличе Parent: :F1, а не Child: :F1, що підтримало б поліморфну поведінку.

C++ однозначно визначає пізнє скріплення у момент виконання і забезпечує поліморфну поведінку функцій за допомогою їх віртуалізації. Лістинг 3.10 узагальнює синтаксис оголошення віртуальних функцій в базовому і похідному класах.

jclass classNamel {

// Інші функції-члени

virtual returnType functionName(<список параметрів>);

};

 

 class className2 : public classNamel {

// Інші функції-члени

virtual returnType functionName(<cписок параметрів>);

};

Лістинг 3.10. Оголошення віртуальних функції в ієрархії класів.

Щоб забезпечити поліморфну поведінку функції F1 в об'єктах класів Parent і Child, необхідно оголосити її віртуальною. Лістинг 3.11 містить модифікований текст програми.

class Parent {

public:

virtual double F1(double x) { return x*x; }

double F2(double x) { return Fl(x) /2; }

};

 

class  Child : public Parent { public:

virtual double F1(double x) { return x*x*x; }

);

 

void  main() {

Child child;

cout “ child.F2(3) “ endl;

}

Лістинг 3.11. Пізнє скріплення віртуальних функцій.

Тепер програма видасть очікуваний результат 13.5. Компілятор відтранслюватиме вираз child. F2 (3) в звернення до успадкованої функції Parent : : F2, яка у свою чергу викличе перевизначену віртуальну функцію нащадка Child: :F1.

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

Функція, оголошена віртуальною, вважається такою у всіх похідних класах - незалежно від того, чи оголошена вона в похідних класах з ключовим словомvirtual чи ні.

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