Урок №177. Віртуальний базовий клас

  Юрій  | 

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

 39

На уроці про множинне спадкування ми говорили про проблему «алмаз смерті». На цьому уроці ми продовжимо цю тему.

Алмаз смерті

Код з того ж уроку, який ілюструє “алмаз смерті” (ми додали ще конструктори):

Хоча ви можете очікувати, що діаграма спадкування буде наступна:

Насправді, це не так. Якщо ви створите об’єкт класу Copier, то отримаєте дві копії класу PoweredDevice: одну від Printer і одну від Scanner.

Діаграму отримаємо наступну:

Розглянемо приклад в коді:

Результат:

PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2

Як ви бачите, PoweredDevice створюється двічі. Іноді так і потрібно, а іноді потрібно, щоб була одна копія PoweredDevice: загальна як для Scanner, так і для Printer.

Віртуальні базові класи

Щоб зробити батьківський (базовий) клас загальним, використовується ключове слово virtual в рядку оголошення дочірнього класу. Віртуальний базовий клас — це клас, об’єкт якого є загальним для використання всіма дочірніми класами. Ось приклад (без конструкторів для простоти) створення загального батьківського класу:

Тепер, при створенні класу Copier, ми отримаємо тільки одну копію PoweredDevice, яка буде спільною як для Scanner, так і для Printer.

Виникає питання: «Якщо Scanner і Printer спільно використовують батьківський клас PoweredDevice, то хто відповідальний за його створення?». Виявляється, що Copier. Конструктор Copier відповідає за створення об’єкта PoweredDevice. Це один з тих випадків, коли дочірньому класу дозволено викликати конструктор батьківського класу, який не є його безпосереднім батьком:

Результат виконання програми:

PoweredDevice: 3
Scanner: 1
Printer: 2

Тут вже PoweredDevice створюється тільки один раз.

Обговоримо кілька деталей.

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

По-друге, конструктори Scanner і Printer як і раніше викликають конструктор PoweredDevice. При створенні об’єкта Copier ці виклики конструктора просто ігноруються, тому що саме Copier відповідає за створення PoweredDevice, а не Scanner чи Printer. Однак, якщо б ми створювали об’єкти Scanner або Printer, то ці конструктори викликалися б і застосовувалися звичайні правила спадкування.

По-третє, якщо клас, стаючи дочірнім, наслідує один або кілька класів, які, в свою чергу, мають віртуальні батьківські класи, то “найдочірніший” клас відповідає за створення віртуального батьківського класу. У вищенаведеній програмі Copier наслідує Printer і Scanner, які обидва мають загальний віртуальний батьківський клас PoweredDevice. Copier, “найдочірніший” клас, відповідає за створення PoweredDevice. Це працює навіть в разі одиночного спадкування: коли Copier наслідує тільки Printer, а Printer віртуально наслідує PoweredDevice, то Copier як і раніше відповідає за створення PoweredDevice.

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

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

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

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