
- •12.1. Класи й об’єкти. Основні поняття
- •12.2.Опис класів і об’єктів
- •12.3. Складові класів
- •12.4. Поняття успадкування
- •12.4. Поліморфізм. Віртуальні та динамічні методи
- •12.5. Структура опису класу
- •13. Класи загального призначення
- •13.1. Клас exception –- обробки виключень
- •13.2. Клас tlist – списки
- •13.3. Класи tstrings, tstringlist – набори рядків й об’єктів
12.4. Поліморфізм. Віртуальні та динамічні методи
Третім фундаментальним принципом класів є поліморфізм. Поліморфізм - це властивість класів вирішувати схожі за змістом проблеми різними способами. Змінюючи алгоритм того або іншого методу в нащадках класу, програміст може надавати цим нащадкам відсутні у батька специфічні властивості. Для зміни методу його необхідно перекрити у нащадку, тобто оголосити в нащадку однойменний метод і реалізувати в ньому потрібні дії. У результаті в об'єкті-батька й об'єкті-нащадка будуть діяти два однойменних методи, що мають різну алгоритмічну основу й, отже, різні властивості. Це й називається поліморфізмом об'єктів.
Усі методи, які розглядалися є статичними. При зверненні до статичного методу компілятор точно знає клас, до якого належить даний метод. Тому, наприклад, звернення до статичного методу GetPercentFree у методі CheckStatus компілюється у виклик TResourceGauge.GetPercentFree
Procedure TResourceGauge.CheckStatus;
begin
if GetPercentFree<=FPercentCritical then
Beep;
(TResourceGauge.GetPercentFree)
end;
Метод CheckStatus буде неправильно працювати у спадкоємцях TDiskGauge та TMemoryGauge, так як у цих класах метод GetPercentFree перекритий тому він не буде виконуватися. Звичайно, у класах TDiskGauge та TMemoryGauge метод CheckStatus можна було б продублювати, але при цьому втрачаються всі переваги успадкування. Ця проблема вирішується по іншому – метод GetPercentFree оголошується віртуальним за допомогою ключового слова virtual
Type
TResourceGauge=class
. . . . . .
Function GetPercentFree:integer; virtual;
. . . . . .
end;
У породжених класах віртуальний метод перекривається за допомогою ключового слова override
Type
TDiskGauge=class(ResourceGauge)
. . . . . .
Function GetPercentFree:integer;override;
. . . . . .
end;
TMemoryGauge=class(ResourceGauge)
. . . . . .
Function GetPercentFree:integer;override;
. . . . . .
end;
Метод, що перекривається повинен мати точно такий же формат (список параметрів, а для функцій ще і тип значення, що повертається), що й перекритий.
Віртуальний метод викликається по фактичному типу екземпляру а не по формальному записаному у програмі
Procedure TResourceGauge.CheckStatus;
begin
if GetPercentFree<=FPercentCritical then
Beep;
(<Фактичний клас>.GetPercentFree)
end;
Тому після зроблених змін метод CheckStatus у спадкоємцях TDiskGauge та TMemoryGauge буде працювати вірно.
Різновидністю віртуальних методів є динамічні методи, при їх оголошенні замість слова virtual використовується ключове слово dynamic
Type
TResourceGauge=class
. . . . . .
Function GetPercentFree:integer; dynamic;
. . . . . .
end;
У спадкоємцях динамічні методи перекриваються як і віртуальні за допомогою ключового слова override. Семантично віртуальні і динамічні методи ідентичні.
Зустрівши оголошення динамічного або віртуального методу компілятор створить дві таблиці - DMT {Dynamic Method Table) і VMT {Virtual Method Table) і помістить у них адреси точок входу відповідно динамічних і віртуальних методів. При кожному звертанні до перекритого методу компілятор вставляє у програму код, що дозволяє вибрати адресу точки входу в підпрограму з тієї або іншої таблиці.
Різниця між динамічними й віртуальними методами полягає у тому, що таблиця динамічних методів DMT містить адреси тільки тих методів, які оголошені як dynamic у даному класі, а таблиця VMT містить адреси метолів оголошених як virtual не тільки даного класу, але й всіх його батьків. Вона значно більша за розміром ніж таблиця DMT і забезпечує більш швидкий пошук, у той час як при звертанні до динамічного методу програма спочатку переглядає таблицю DMT об'єкту, а потім - у його батьківського класу й так далі, поки не буде знайдена потрібна точка входу.
Методи, що перекриваються динамічно, часто можуть взагалі нічого не робити. Такі методи називаються абстрактними, вони оголошуються директивою abstract і зобов'язані перекриватися в нащадках. Наприклад,
Type TResourceGauge=class
. . . . . .
Function GetPercentFree:integer;dynamic; abstract;
. . . . . .
end;
У класі TResourceGauge метод GetPercentFree повертає значення Result:=0; Звернення до таких метолів неможливе, вони виступають, як заготовки для створення інших кпасів.