Розділ №12. Підсумковий тест

  Юрій  | 

  Оновл. 24 Бер 2021  | 

 104

Отже, наша подорож в спадкування і віртуальні функції в мові C++ підійшла до кінця. Пора закріпити пройдений матеріал.

Теорія

Мова C++ дозволяє створювати вказівники/посилання батьківського класу на об’єкти дочірніх класів. Це корисно при використанні функцій або масивів, які повинні працювати з об’єктами дочірніх класів.

Без віртуальних функцій вказівники/посилання батьківського класу на об’єкт дочірнього класу матимуть доступ лише до членів батьківського класу.

Віртуальна функція — це особливий тип функції, яка, при зверненні до неї, викликає “найдочірніший” метод (перевизначення), який існує між батьківським і дочірніми класами. Щоб вважатися перевизначенням, метод дочірнього класу повинен мати ту ж сигнатуру і тип повернення, що і віртуальна функція батьківського класу. Єдиний виняток — коваріантний тип повернення, який дозволяє перевизначенню повертати вказівник або посилання на дочірній клас, якщо метод батьківського класу повертає вказівник або посилання на себе.

Модифікатор override використовується для позначення методу перевизначенням.

Модифікатор final забороняє перевизначати віртуальну функцію або наслідувати певний клас.

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

Ви можете ігнорувати виклик перевизначень віртуальної функції, використовуючи оператор дозволу області видимості, щоб безпосередньо вказати, яку функцію ви хочете викликати. Наприклад, parent.Parent::GetName().

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

Відносні недоліки віртуальних функцій:

   Виклик віртуальних функцій займає більше часу.

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

Віртуальну функцію можна зробити чистою віртуальною/абстрактною функцією, додавши = 0 в кінець її прототипу. Клас, який містить чисту віртуальну функцію, називається абстрактним класом. Об’єкти абстрактного класу не можуть бути створені. Клас, який наслідує чисті віртуальні функції, повинен надати свої перевизначення цих функцій, або він також буде вважатися абстрактним. Чисті віртуальні функції можуть мати тіло (визначення, записане окремо), але вони як і раніше вважаються абстрактними функціями.

Інтерфейс (або «інтерфейсний клас») — це клас без змінних-членів, всі методи якого є чистими віртуальними функціями. Імена інтерфейсів часто починаються з I.

Віртуальний базовий клас — це батьківський клас, об’єкт якого є загальним для використання всіма дочірніми класами.

При присвоюванні об’єкта дочірнього класу об’єкту батьківського класу, в об’єкт батьківського класу копіюється лише батьківська частина об’єкта, що копіюється, а дочірня частина просто обрізається. Цей процес називається обрізкою об’єктів.

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

Найпростіший спосіб перевантажити оператор виводу << для класів зі спадкуванням — записати перевантаження оператора << в батьківському класі, а виконання операції виводу делегувати віртуальному методу.

Тест

Завдання №1

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

Child

a)

Відповідь №1.a)

Parent::getName() не є віртуальною функцією, тому p.getName() не викличе Child::getName().

b)

Відповідь №1.b)

Хоча метод Child::getName() є константним, метод Parent::getName() не є константним, тому Child::getName() не рахується перевизначенням і, відповідно, не викликається.

c)

Відповідь №1.c)

Об’єкт ch присвоюється об’єкту p по значенню (а не по посиланню), що призводить до обрізки об’єкта ch.

d)

Відповідь №1.d)

Клас Parent був оголошений як final, тому клас Child не може наслідувати клас Parent. Результат — помилка компіляції.

e)

Відповідь №1.e)

Child::getName() є абстрактною функцією, хоча має тіло (записане окремо), тому клас Child є абстрактним, а об’єкти абстрактного класу створювати заборонено.

f)

Відповідь №1.f)

Ця програма виводить вірний результат, але має іншу проблему. В кінці функції main() ми видаляємо p, який є вказівником класу Parent, але у нас немає віртуального деструктора в класі Parent. Отже, видаляється лише частина Parent об’єкта класу ch, а частина Child об’єкта ch залишається у вигляді “витоку пам’яті”.

Завдання №2

a) Створіть абстрактний клас Shape. Цей клас повинен мати три методи:

   чисту віртуальну функцію print() з параметром типу std::ostream;

   перевантаження operator<<;

   пустий віртуальний деструктор.

Відповідь №2.a)

b) Створіть два класи: Triangle і Circle, які наслідують клас Shape.

   Triangle повинен мати 3 точки в якості змінних-членів.

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

Перевантажте функцію print(), щоб наступний код:

Видавав наступний результат:

Circle(Point(1, 2, 3), radius 7)
Triangle(Point(1, 2, 3), Point(4, 5, 6), Point(7, 8, 9))

Ось клас Point, який ви можете використовувати:

Відповідь №2.b)

c) Використовуючи код з попередніх завдань (класи Point, Shape, Circle і Triangle) завершіть наступну програму:

Підказка: Вам потрібно додати метод getRadius() в Circle і виконати понижуюче приведення Shape* в Circle*, щоб отримати доступ до цього методу.

Відповідь №2.c)

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (Немає Оцінок)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *