Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекція 16.docx
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
26 Кб
Скачать
☆
  1. Оператор typeid

Отже, оператор dynamic_cast додає програмам, що працюють з ієрархіями віртуальних класів, додаткову гнучкість. Однак про точний тип об'єкта з його допомогою довідатися складно. Оператор dynamic_cast у кращому випадку дозволяє розпізнати базовий клас. Для цього в мові С++ передбачений оператор typeid.

Цей оператор повертає посилання на об'єкт класу type_info, визначеного в заголовку <typeinfo>.

Клас typeinfo, поряд з ім'ям типу, містить визначення операцій порівняння, тому його можна використовувати в логічних вираженнях.

Розглянемо приклад, у якому досліджується описана вище ієрархія класів. Оскільки визначення класів залишаються незмінними, ми приведемо лише функцію main().

int main()

{

TBase objBase("TBase");

TBase* pObj1=&objBase;

TDerived objDerived("TBase", "TDerived");

pObj1 = &objDerived;

if(typeid(*pObj1)==typeid(TDerived)) printf("TDerived\n");

else printf("Something else ...");

return 0;

}

Програма виводить на екран наступні рядки.

Ctor TBase

Ctor TBase

Ctor TDerived

TBase->TDerived

Dtor TDerived

Dtor TBase

Dtor TBase

Четвертий рядок свідчить про те, що вказівник pObj1 дійсно посилається на об'єкт класу TDerived.

Знаючи пристрій класу type_info, можна спробувати вивести на екран назву елементарного типу чи класу.

Застосування оператора typeid

#include <stdio.h>

#include <typeinfo>

int main()

{

double X;

printf("%s",typeid(X).name());

return 0;

}

Ця програма виводить на екран рядок double.

Застосовний тепер оператор typeid не до елементарного типу, а до класу.

int main()

{

TBase objBase("TBase");

printf("%s",typeid(objBase).name());

return 0;

}

На екран виводяться такі рядки.

Ctor TBase

class TBase

Dtor TBase

Перше повідомлення виводиться конструктором класу TBase, другий рядок — результат застосування оператора typeid до об'єкта objBase, а останній рядок являє собою повідомлення деструктора класу TBase.

  1. Основні висновки

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

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

Клас, що лежить в основі ієрархії, називається базовим.

Класи, що успадковують властивості базового класу, називаються похідними.

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

Успадкування буває одиночним і множинним. При одиночному успадкуванні в кожного похідного класу є лише один базовий клас, а при множинному — декілька.

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

Для цього використовується наступна синтаксична конструкція.

class ім'я_похідного_класу:специфікатор_доступу ім'я_базового_класу

{

// тіло класу

};

Хоча всі члени базового класу автоматично стають членами похідного класу, однак доступ до цих членів визначається видом успадкування. У залежності від специфікатора доступу, зазначеного при оголошенні похідного класу, розрізняють відкрите, закрите і захищене успадкування. За замовчуванням використовується закрите успадкування (специфікатор private).

При відкритому успадкуванні всі відкриті і захищені члени базового класу стають відкритими і захищеними членами похідного класу відповідно.

При захищеному успадкуванні всі відкриті і захищені члени базового класу стають захищеними членами похідного класу.

При закритому успадкуванні всі відкриті і захищені члени базового класу стають закритими членами похідного класу відповідно.

При успадкуванні рівень доступу до членів похідного класу може лише зменшитись. “Все можна ще більше закрити, але не відкрити”.

Закриті члени базового класу є доступними лише членам базового класу. Успадкування не може суперечити принципу приховання даних.

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

Деструктори викликаються у порядку, зворотньому відносно до конструкторів.

Множинне успадкування часте приводить до неоднозначностей. Однак для вирішення неоднозначностей можна використати віртуальні базові класи. Для того щоб запобігти дублювання, у списку успадкування перед ім'ям базового класу варто вказати ключовому слову virtual, передбачивши конструктор за замовчуванням.

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

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

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

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

На віртуальні функції накладаються наступні обмеження.

Вони не можуть бути статичними.

Вони не можуть бути дружніми.

Конструктори не можуть бути віртуальними.

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

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

Якщо віртуальна функція зовсім не заміщується у похідних класах — це помилка.

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

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

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

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

Створюючи об'єкт похідного класу, компілятор поєднує його поля і поля базового класу в одне ціле.

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

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

Якщо вказівник базового класу посилається на об'єкт похідного класу, необхідно застосовувати віртуальний деструктор. Розглянемо конкретний приклад.

Оператор dynamic_cast має два операнди: у кутових дужках указується тип, а в круглих дужках — вказівник. Наприклад, в операторі

dynamic_cast<TBase*>(pObj)

TBase* — тип, а pObj — вказівник.

Якщо вказівник pObj посилається на об'єкт базового класу TBase чи похідного, результатом застосування оператора dynamic_cast буде вказівник на базовий клас TBase.

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

Оператор typeid повертає посилання на об'єкт класу type_info, визначеного в заголовку <typeinfo>. Клас typeinfo, поряд з ім'ям типу, містить визначення операцій порівняння, тому його можна використовувати в логічних вираженнях.

Контрольні питання

  1. Опишіть оператор dynamic_cast.

  2. Опишіть три види приведення за допомогою оператора dynamic_cast в ієрархії класів.

  3. Опишіть оператор typeid