Herència virtual

Herència virtual

Característiques tècniques
PlataformaC standard library i altres

L'herència virtual és una tècnica C++ que garanteix que només una còpia de les variables membres ' una classe base sigui heretada per les classes derivades dels néts. Sense herència virtual, si dues classes B i C hereten d'una classe A i una classe D hereta tant de B com de C, aleshores D contindrà dues còpies de les variables membres ' A : una a través de B i una altra a través C Aquests seran accessibles de manera independent, utilitzant la resolució d'abast.[1]

Diagrama de l'herència del diamant, un problema que l'herència virtual està intentant resoldre.

En canvi, si les classes B i C hereten virtualment de la classe A, aleshores els objectes de la classe D només contindran un conjunt de variables membres de la classe A. [2]

Aquesta característica és molt útil per a l'herència múltiple, ja que fa que la base virtual sigui un subobjecte comú per a la classe derivada i totes les classes que se'n deriven. Això es pot utilitzar per evitar el problema del diamant aclarint l'ambigüitat sobre quina classe ancestra s'ha d'utilitzar, ja que des de la perspectiva de la classe derivada (D a l'exemple anterior) la base virtual (A ) actua com si fos la classe base directa de D, no una classe derivada indirectament a través d'una base (B o C ).[3][4]

UML virtual inheritance.svg

S'utilitza quan l'herència representa la restricció d'un conjunt en lloc de la composició de parts. En C++, una classe base destinada a ser comuna a tota la jerarquia es denota com a virtual amb la paraula clau virtual.

Considereu la següent jerarquia de classes.

struct Animal {
 virtual ~Animal() = default; // Explicitly show that the default class destructor will be made.
 virtual void Eat() {}
};

struct Mammal: Animal {
 virtual void Breathe() {}
};

struct WingedAnimal: Animal {
 virtual void Flap() {}
};

// A bat is a winged mammal
struct Bat: Mammal, WingedAnimal {};

Com s'ha dit més amunt, una crida al bat. Eat és ambigu perquè hi ha dues classes base Animal (indirectes) a Bat, de manera que qualsevol objecte Bat té dos subobjectes diferents de classe base Animal. Per tant, un intent d'enllaçar directament una referència al subobjecte Animal d'un objecte Bat fallaria, ja que la vinculació és inherentment ambigua:

Bat bat;
Animal& animal = bat; // error: which Animal subobject should a Bat cast into, 
 // a Mammal::Animal or a WingedAnimal::Animal?

Per desambiguar, caldria convertir explícitament bat a qualsevol subobjecte de classe base:

Bat bat;
Animal& mammal = static_cast<Mammal&>(bat); 
Animal& winged = static_cast<WingedAnimal&>(bat);

Per trucar a Eat, cal la mateixa desambiguació o qualificació explícita: static_cast<Mammal&>(bat). Eat() o static_cast<WingedAnimal&>(bat). Eat() o alternativament bat. Mammal::Eat() i bat. WingedAnimal::Eat(). La qualificació explícita no només utilitza una sintaxi més fàcil i uniforme tant per als punters com per als objectes, sinó que també permet l'enviament estàtic, de manera que podria ser el mètode preferible.

Referències

  1. «What is virtual inheritance in C++ and when should you use it?» (en anglès americà), 23-12-2020. [Consulta: 18 desembre 2023].
  2. «What is Virtual Inheritance?» (en anglès), 23-02-2023. [Consulta: 18 desembre 2023].
  3. Milea, Andrei. «Solving the Diamond Problem with Virtual Inheritance» (en anglès). Cprogramming.com. [Consulta: 8 març 2010].
  4. McArdell, Ralph. «C++/What is virtual inheritance?» (en anglès). All Experts, 14-02-2004. Arxivat de l'original el 2010-01-10. [Consulta: 8 març 2010].