Wirtualne klasy podstawowe
Ponieważ klasa może być pośrednią klasą podstawową do klasy pochodnej więcej niż raz, w języku C++ dostarczono sposobu optymalizacji sposobu działania klas podstawowych.Wirtualne klasy podstawowe oferują sposób, aby zaoszczędzić miejsce i uniknąć niejasności w hierarchii klas, które używają wielokrotnego dziedziczenia.
Każdy obiekt niewirtualny zawiera kopię elementów członkowskich danych zdefiniowanych w klasie podstawowej.Ta duplikacja powoduje utratę miejsca i wymaga od użytkownika wybrania kopii elementów członkowskich klasy podstawowej przy każdym uzyskiwaniu dostępu do nich.
Gdy klasa podstawowa jest określona jako wirtualna baza, może ona działać jako baza pośrednia więcej niż raz, bez duplikacji jej elementów członkowskich danych.Pojedyncza kopia elementów członkowskich danych jest współużytkowana przez wszystkie klasy podstawowe, które używają jej jako bazy wirtualnej.
Podczas deklarowania wirtualnej klasy podstawowej, słowo kluczowe virtual pojawia się na liście bazowej klas pochodnych.
Należy wziąć pod uwagę hierarchię klas na poniższym rysunku, który ilustruje symulowaną linię obiadu.
Wykres symulowanej linii obiadu
Na rysunku Queue jest klasą podstawową dla CashierQueue i LunchQueue.Jednakże, kiedy obie klasy są połączone, tworząc LunchCashierQueue, pojawia się następujący problem: nowa klasa zawiera dwa podobiekty typu Queue, jeden CashierQueue , a drugi LunchQueue.Na poniższej ilustracji pokazano koncepcyjny układ pamięci (rzeczywisty układ pamięci może być zoptymalizowany).
Obiekt symulowanej linii obiadu
Należy zauważyć, że istnieją dwa podobiekty Queue w obiekcie LunchCashierQueue.W poniższym kodzie zadeklarowano Queue jako wirtualną klasę podstawową:
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
Słowo kluczowe virtual zapewnia, że zawarta jest tylko jedna kopia podobiektu Queue (patrz poniższy rysunek).
Obiekt symulowanej linii obiadu z wirtualnymi klasami podstawowymi
Klasa może mieć zarówno wirtualny jak i niewirtualny składnik danego typu.Dzieje się tak przy warunkach przedstawionych na poniższej ilustracji.
Wirtualne i niewirtualne składniki tej samej klasy
Na rysunku CashierQueue i LunchQueue używają Queue jako wirtualnej klasy podstawowej.Jednakże TakeoutQueue określa Queue jako klasę podstawową, a nie wirtualną klasę podstawową.W związku z tym LunchTakeoutCashierQueue posiada dwa podobiekty typu Queue: jeden ze ścieżki dziedziczenia, która zawiera LunchCashierQueue i jeden ze ścieżki, która zawiera TakeoutQueue.Jest to zilustrowane na poniższym rysunku.
Układ obiektu z wirtualnym i niewirtualnym dziedziczeniem
[!UWAGA]
Dziedziczenie wirtualne zapewnia znaczące korzyści dotyczące rozmiaru w porównaniu do dziedziczenia niewirtualnego.To jednak może wprowadzić dodatkowe obciążenie dotyczące przetwarzania.
Jeśli w klasie pochodnej przeciążono funkcję wirtualną, która jest dziedziczona z wirtualnej klasy podstawowej, oraz jeśli konstruktor lub destruktor klasy pochodnej wywołuje tą funkcję za pomocą wskaźnika do wirtualnej klasy podstawowej, kompilator może wprowadzić dodatkowe ukryte pola "vtordisp" do klas z bazami wirtualnymi.Opcja kompilatora /vd0 powoduje pominięcie dodania ukrytego członka przemieszczenia konstruktora/destruktora vtordisp.Domyślna opcja kompilatora /vd1 włącza je tam, gdzie są niezbędne.Wyłącz vtordisps tylko wtedy, gdy masz pewność, że wszystkie konstruktory i destruktory klas wywołują funkcje wirtualne w sposób wirtualny.
Opcja kompilatora /vd wpływa na cały moduł kompilacji.Użycie pragmy vtordisp do pominięcia i ponownego włączenia pól vtordisp działające na zasadzie klasy przez klasy:
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )