Virtuel arv

For virtuel metodearv, se virtuel metode .

Virtuel arv ( eng.  virtual inheritance ) i programmeringssproget C++  er en af ​​nedarvningsmulighederne , som er nødvendig for at løse nogle af de problemer, der genereres af tilstedeværelsen af ​​muligheden for multipel arv (især " rudeformet arv "). , ved at løse tvetydigheden af ​​hvilke metoder der er fra superklasser (direkte klasser -ancestor) skal bruges. Det bruges i tilfælde, hvor multipel nedarvning, i stedet for den tilsigtede fulde sammensætning af egenskaber for forfædreklasser, resulterer i en begrænsning af tilgængelige nedarvede egenskaber på grund af tvetydighed. En basisklasse, der er arvet flere gange, defineres som virtuel ved hjælp af nøgleordet virtual.

Essensen af ​​problemet

Overvej følgende klassehierarki:

klasse dyr { offentligt : virtuel void spise (); // Metoden er defineret for den givne klasse ... }; klasse Pattedyr : offentligt dyr { offentligt : Farve getHairColor (); ... }; klasse WingedAnimal : offentligt dyr { offentligt : tomrumsklap ( ); ... }; // En flagermus er en bevinget pattedyrklasse Flagermus : offentligt pattedyr , offentligt bevinget dyr {}; //<--- bemærk at eat() metoden ikke er tilsidesat i Bat Batbat ; _

For koden ovenfor er opkaldet bat.eat()tvetydigt. Det kan henvise til Bat::WingedAnimal::Animal::eat()både og til Bat::Mammal::Animal::eat(). For hver mellemliggende efterfølger ( WingedAnimal, Mammal) kan metoden eat()tilsidesættes (dette ændrer ikke ved problemets essens set fra sprogets synspunkt). Problemet er, at semantikken i traditionel multipel arv ikke stemmer overens med den virkelighed, den modellerer. I en vis forstand er essensen Animalunik i essensen; Bat - dette er Mammalog WingedAnimal, men en flagermus's egenskab ( Animalhed ) Bat, det er også et pattedyrs dyrs egenskab ( Mammal) og det er den samme dyriske egenskab WingedAnimal - faktisk er dette en og samme egenskab .

Denne situation omtales almindeligvis som " diamantarv " og er et problem, som virtuel arv er designet til at løse.

Klasserepræsentation

Før du fortsætter, er det nyttigt at gennemgå, hvordan klasser er repræsenteret i C++. Især under arv placeres klasserne for forfaderen og efterfølgeren simpelthen i hukommelsen efter hinanden. Således er et Bat-klasseobjekt faktisk en sekvens af klasseobjekter (Animal, Pattedyr, Animal, WingedAnimal, Bat) placeret sekventielt i hukommelsen, mens Animal gentages to gange, hvilket fører til tvetydighed.

Løsning

Vi kan tilsidesætte vores klasser på denne måde:

klasse dyr { offentligt : virtuel void spise (); ... }; // To klasser arver praktisk talt Dyr: klasse Pattedyr : offentlig virtuelt Dyr // <--- bemærk søgeordet virtuel { offentligt : Farve getHairColor (); ... }; class WingedAnimal : public virtual Animal // <--- bemærk søgeordet virtual { offentligt : tomrumsklap ( ); ... }; // En flagermus er stadig en bevinget pattedyrklasse Flagermus : offentligt pattedyr , offentligt bevinget dyr {};

Nu er den del Animalaf klasseobjektet Bat::WingedAnimal den samme som den del Animal, der bruges i Bat::Mammal, og kan siges kun at Bathave én del i sin repræsentation Animal, og opkaldet Bat::eat()bliver entydigt.

Virtuel arv implementeres ved at tilføje pointere til Mammalog WingedAnimal. Således Batfremstår det som (ptr, Pattedyr, ptr, WingedAnimal, Bat, Animal). *ptr indeholder information om forskydningen i hukommelsen mellem begyndelsen af Mammal​​/ WingedAnimalog dens Animal. På grund af dette er ikke kun elimineringen af ​​duplikering af den fælles Animaldel for Mammalog WingedAnimal, men også en simpel mekanisme til at konvertere en pointer (reference) til et objekt fra arvingklassen til en pointer (reference) til et objekt af enhver basisklasse. stillet til rådighed. Det er klart, at overdreven brug af virtuel arv vil give en vis ydeevneforringelse (svarende til virtuelle funktioner - på grund af en ekstra læseoperation).

Eksempel

For at forstå essensen af ​​virtuel arv uden for meget "støj", overvej følgende eksempel:

#include <iostream> klasse A { offentligt : virtual int foo () { retur 1 ; } }; klasse B : offentlig virtuel A {}; klasse C : offentlig virtuel A {}; klasse D : offentlig B , offentlig C {}; int main () { D d ; std :: cout << d . foo (); returnere 0 ; }

Hvis det virtuelle nøgleord fjernes , så kan foo()- metoden ikke defineres entydigt og vil derfor ikke være tilgængelig, ligesom et objekt i klasse D - koden vil ikke kompilere.

Se også

Litteratur

  • Podbelsky VV Kapitel 10.2 Multipel nedarvning og virtuelle basisklasser // Sprog C++ / rec. Dadaev Yu. G. - 4. - M . : Finansiering og statistik , 2003. - S. 336-359. — 560 s. - ISBN 5-279-02204-7 , UDC 004.438Si (075.8) LBC 32.973.26-018 1ya173.