Віртуальні базові класи
Оскільки клас може кілька разів виступати як непрямий базовий клас до похідному класу, в C ++ є спосіб оптимізувати функціонування таких базових класів. Віртуальні базові класи дозволяють економити простір і виключати неоднозначність в ієрархіях класів, в яких використовується множинне спадкування.
Кожен невіртуальний об'єкт містить копію елементів даних, визначених у базовому класі. Такий повтор даних призводить до непотрібного збільшення їх обсягу. Крім того, при кожній спробі звернення до елементів базового класу доводиться вказувати, яка саме їх копія потрібна.
Якщо базовий клас визначений як віртуальний базовий клас, то він може кілька разів виступати як непрямий базовий клас без дублювання елементів даних. Єдина копія його елементів даних спільно використовується всіма базовими класами, які використовують його як віртуальний базовий клас.
Коли створюється оголошення віртуального базового класу, в базових списках похідних класів вказується ключове слово virtual.
Розглянемо ієрархію класів, представлену на наступному малюнку, на якому показана імітація графа Lunch-Line.
Змодельований граф Lunch-Line
Як видно на малюнку, клас Queue є базовим для двох інших класів: CashierQueue і LunchQueue. Однак коли ці два класи об'єднуються і утворюють клас LunchCashierQueue, виникає наступна проблема: новий клас містить два підлеглих об'єкта типу Queue - один з CashierQueue, а інший з LunchQueue. На наступному малюнку показана концептуальна структура пам'яті (фактична структура пам'яті може бути оптимізована).
Змодельований об'єкт Lunch-Line
Зверніть увагу, що в об'єкті LunchCashierQueue є два підлеглих об'єкта Queue. У наступному коді міститься оголошення Queue як віртуального базового класу:
// deriv_VirtualBaseClasses.cpp // compile with: / LD class Queue {}; class CashierQueue: virtual public Queue {}; class LunchQueue: virtual public Queue {}; class LunchCashierQueue: public LunchQueue, public CashierQueue {};
Завдяки ключовим словом virtual буде включена лише одна копія підлеглого об'єкта Queue (див. Наступний малюнок).
Імітація об'єкта Lunch-Line з віртуальними базовими класами
Клас може мати як віртуальний, так і невіртуальний компонент заданого типу. Це відбувається за умов, які ілюструє наступний малюнок.
Віртуальні і невіртуальні компоненти одного класу
На цьому малюнку показано, що класи CashierQueue і LunchQueue використовують Queue як віртуальний базовий клас. Однак TakeoutQueue визначає Queue в якості базового класу, а не віртуального базового класу. Тому в LunchTakeoutCashierQueue є два підлеглих об'єкта типу Queue: один з шляху успадкування, що включає LunchCashierQueue, а другий з шляху, що включає TakeoutQueue. Це показано на наступному малюнку.
Структура об'єктів з успадкуванням від віртуальних і невіртуальних базових класів
Примітка
Спадкування від віртуальних базових класів дозволяє істотно скоротити обсяг даних в порівнянні з успадкуванням від невіртуальних классов.Однако воно може породити додаткові витрати на обробку.
Якщо похідний клас переопределяет віртуальну функцію, яку він успадковує від віртуального базового класу, і якщо конструктор або деструктор похідного базового класу викликає цю функцію за допомогою покажчика на віртуальний базовий клас, то компілятор може вставити додаткові приховані поля vtordisp в класи з віртуальними базовими класами. Параметр компілятора / vd0 відключає додавання прихованого члена зміщення конструктора або деструктора vtordisp. Параметр компілятора / vd1 (за замовчуванням) дозволяє додавати їх в міру необхідності. Додавання полів vtordisps слід відключати тільки в тому випадку, якщо точно відомо, що всі конструктори і деструктори класів викликають віртуальні функції віртуально.
Параметр компілятора / vd діє на весь модуль компіляції. Використовуйте директиву #pragma vtordisp, щоб відключити, а потім повторно включити додавання полів vtordisp для кожного класу окремо:
#pragma vtordisp (off) class GetReal: virtual public {...}; #pragma vtordisp (on)
посилання
Кілька базових класів