Virtuální třídy Base
Jelikož třída může být nepřímou základní třídou odvozené třídy více než jednou, jazyk C++ poskytuje způsob, jak práci takové základní třídy optimalizovat.Virtuální základní třídy nabízejí způsob, jak ušetřit místo a vyhnout se nejasnostem v hierarchiích tříd používajících vícenásobnou dědičnost.
Všechny nevirtuální objekty obsahují kopii datových členů definovaných v základní třídě.Tato duplicita zabírá místo a vyžaduje při každém přístupu ke členům základní třídy určit, která kopie členů má být použita.
Je-li základní třída zadaná jako virtuální základ, může více než jednou fungovat jako nepřímý základ bez duplikace svých datových členů.Všechny základní třídy, které tuto třídu používají jako virtuální základ, sdílejí jedinou kopii jejích datových členů.
Při deklarace virtuální základní třídy se v seznamech základů odvozených tříd vyskytuje klíčové slovo virtual.
Uvažte hierarchii tříd na následujícím obrázku, který znázorňuje simulovanou frontu na oběd.
Graf simulované fronty na oběd
Na obrázku je třída Queue základní třídou pro třídu CashierQueue i LunchQueue.Jsou-li však obě třídy kombinovány do třídy LunchCashierQueue, nastane následující problém: nová třída obsahuje dva podobjekty typu Queue, jeden z třídy CashierQueue a druhý z třídy LunchQueue.Následující obrázek ukazuje koncepční rozložení paměti (skutečné rozložení paměti může být optimalizováno).
Objekt simulované fronty na oběd
Povšimněte si, že v objektu LunchCashierQueue existují dva podobjekty Queue.Následující kód deklaruje základní třídu Queue jako virtuální:
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
Klíčové slovo virtual zajišťuje, že je zahrnuta pouze jedna kopie podobjektu Queue (viz následující obrázek).
Objekt simulované fronty na oběd s virtuálními základními třídami
Třída může mít virtuální i nevirtuální komponentu daného typu.K tomu dojde za podmínek znázorněných na následujícím obrázku.
Virtuální a nevirtuální komponenty jedné třídy
Na obrázku používají třídy CashierQueue a LunchQueue třídu Queue jako virtuální základní třídu.Třída TakeoutQueue však třídu Queue určuje jako základní třídu, nikoli jako virtuální základní třídu.Proto má třída LunchTakeoutCashierQueue dva podobjekty typu Queue: jeden z cesty dědičnosti obsahující třídu LunchCashierQueue a jeden z cesty obsahující třídu TakeoutQueue.To je znázorněno na následujícím obrázku.
Rozložení objektů s virtuální a nevirtuální dědičností
[!POZNÁMKA]
Virtuální dědičnost poskytuje v porovnání s nevirtuální dědičností významné výhody ve velikosti.Může však zavést další režii zpracování.
Přepisuje-li odvozená třída virtuální funkci, kterou třída dědí z virtuální základní třídy, a pokud konstruktor nebo destruktor odvozené základní třídy tuto funkci volá pomocí ukazatele na virtuální základní třídu, kompilátor může do tříd s virtuálními základy zavést dodatečná skrytá pole "vtordisp".Možnost kompilátoru /vd0 potlačuje přidání skrytého členu vtordisp pro přesunutí konstruktoru či destruktoru.Možnost kompilátoru /vd1 je ve výchozím nastavení povoluje tam, kde je jich zapotřebí.Členy vtordisp vypněte pouze v případě, že jste si jisti, že všechny konstruktory a destruktory třídy volají virtuální funkce virtuálně.
Možnost kompilátoru /vd ovlivňuje celý modul kompilace.Chcete-li potlačit a poté znovu povolit pole vtordisp pro jednotlivé třídy, použijte direktivu pragma vtordisp:
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )