Classes de base virtuelles
Comme une classe peut être une classe de base indirecte à une classe dérivée plusieurs fois, C++ offre un moyen d'optimiser la façon dont fonctionnent de telles classes de base. Les classes de base virtuelles permettent d'économiser de l'espace et d'éviter les ambiguïtés dans les hiérarchies de classes qui utilisent un héritage multiple.
Chaque objet non virtuel contient une copie des données membres définies dans la classe de base. Cette duplication gaspille de l'espace et vous oblige à spécifier quelle copie des membres de classe de base vous souhaitez chaque fois que vous accédez à eux.
Lorsqu'une classe de base est spécifiée comme base virtuelle, elle peut agir comme base indirecte plusieurs fois sans duplication de ses données membres. Une copie unique de ses données membres est partagée par toutes les classes de base qui l'utilisent comme base virtuelle.
Lorsque vous déclarez une classe de base virtuelle, le mot clé virtual apparaît dans les listes de base des classes dérivées.
Dans la figure ci-dessous, la hiérarchie de classes illustre un objet Lunch-Line simulé.
Graphique de Lunch-Line simulé
Dans la figure, Queue représente la classe de base de CashierQueue et LunchQueue. Toutefois, lorsque les deux classes sont combinées pour former LunchCashierQueue, le problème suivant survient : la nouvelle classe contient deux sous-objets de type Queue, l'un provenant de CashierQueue et l'autre de LunchQueue. La figure suivante montre la disposition de mémoire conceptuelle (la disposition de mémoire réelle peut être optimisée).
Objet Lunch-Line simulé
Notez que deux sous-objets Queue figurent dans l'objet LunchCashierQueue. Le code suivant déclare Queue en tant que classe de base virtuelle :
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
Le mot clé virtual garantit qu'une seule copie du sous-objet Queue est incluse (voir la figure suivante).
Objet Lunch-Line simulé avec classes de base virtuelles
Une classe peut avoir à la fois un composant virtuel et un composant non virtuel d'un type donné. Cela se produit dans les conditions illustrées à la figure suivante.
Composants virtuel et non virtuel de la même classe
Dans cette figure, CashierQueue et LunchQueue utilisent Queue comme classe de base virtuelle. Toutefois, TakeoutQueue spécifie Queue en tant que classe de base, et non pas comme classe de base virtuelle. Par conséquent, LunchTakeoutCashierQueue a deux sous-objets de type Queue : l'un provenant du chemin d'héritage qui inclut LunchCashierQueue et l'autre provenant du chemin qui inclut TakeoutQueue. La figure ci-dessous illustre cela.
Disposition d'objets avec héritage virtuel et non virtuel
Notes
L'héritage virtuel fournit des avantages de taille significatifs par rapport à l'héritage non virtuel.Toutefois, il peut introduire une certaine surcharge de traitement.
Si une classe dérivée remplace une fonction virtuelle qu'elle hérite d'une classe de base virtuelle, et si un constructeur ou un destructeur pour la classe de base dérivée appelle cette fonction à l'aide d'un pointeur désignant la classe de base virtuelle, le compilateur peut introduire des champs « vtordisp » masqués supplémentaires dans les classes dotées de bases virtuelles. L'option /vd0 du compilateur supprime l'ajout du membre de déplacement de constructeur/destructeur vtordisp masqué. L'option /vd1 du compilateur, utilisée par défaut, les active là où ils sont nécessaires. Désactivez les paramètres vtordisp seulement si vous êtes certain que tous les constructeurs et destructeurs de classe appellent virtuellement les fonctions virtuelles.
L'option /vd du compilateur affecte un module de compilation entier. Utilisez le pragma vtordisp pour supprimer puis réactiver les champs vtordisp classe par classe :
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )