Moștenirea virtuală ( ing. moștenirea virtuală ) în limbajul de programare C++ este una dintre opțiunile de moștenire care este necesară pentru a rezolva unele dintre problemele generate de prezența posibilității de moștenire multiplă (în special „ moștenirea în formă de diamant ”), prin rezolvarea ambiguității a căror metode sunt din superclase(clase de strămoși imediati) trebuie utilizate. Este utilizat în cazurile în care moștenirea multiplă, în loc de compoziția completă intenționată a proprietăților claselor de strămoși, are ca rezultat o limitare a proprietăților moștenite disponibile din cauza ambiguității. O clasă de bază care este moștenită multiplu este definită ca virtuală folosind cuvântul cheie virtual.
Luați în considerare următoarea ierarhie a claselor:
clasa Animal { public : virtual vid eat (); // Metoda este definită pentru clasa dată ... }; clasa Mamifer : animal public { public : Culoare getHairColor (); ... }; clasa WingedAnimal : animal public { public : clapeta de gol (); ... }; // Un liliac este o clasă de mamifer înaripat Bat : public Mammal , public WingedAnimal {}; //<--- rețineți că metoda eat() nu este suprascrisă în Bat Batbat ; _Pentru codul de mai sus, apelul bat.eat()este ambiguu. Se poate referi la Bat::WingedAnimal::Animal::eat()ambele și la Bat::Mammal::Animal::eat(). Pentru fiecare succesor intermediar ( WingedAnimal, Mammal) metoda eat()poate fi suprascrisă (acest lucru nu schimbă esența problemei din punctul de vedere al limbajului). Problema este că semantica moștenirii multiple tradiționale nu se potrivește cu realitatea pe care o modelează. Într-un fel, esența Animaleste unică în esență; Bat - aceasta este Mammalși WingedAnimal, dar proprietatea de animalitate ( Animalness) a unui liliac ( Bat), este și proprietatea de animalitate a unui mamifer ( Mammal) și este aceeași proprietate a animalității WingedAnimal - de fapt, aceasta este una și aceeași proprietate .
Această situație este denumită în mod obișnuit „ moștenirea diamantelor ” și este o problemă pe care moștenirea virtuală este concepută să o rezolve.
Înainte de a continua, este util să revizuiți modul în care sunt reprezentate clasele în C++. În special, în timpul moștenirii, clasele strămoșului și succesorului sunt pur și simplu plasate în memorie una după alta. Astfel, un obiect de clasă Bat este de fapt o succesiune de obiecte de clasă (Animal, Mammal, Animal, WingedAnimal, Bat) plasate secvenţial în memorie, în timp ce Animal se repetă de două ori, ceea ce duce la ambiguitate.
Ne putem suprascrie clasele astfel:
clasa Animal { public : virtual vid eat (); ... }; // Două clase care moștenesc virtual Animal: clasa Mamifer : public virtual Animal // <--- observați cuvântul cheie virtual { public : Culoare getHairColor (); ... }; class WingedAnimal : animal virtual public // <--- observați cuvântul cheie virtual { public : clapeta de gol (); ... }; // Un liliac este încă o clasă de mamifer înaripat Bat : public Mammal , public WingedAnimal {};Acum, partea Animalobiectului de clasă Bat::WingedAnimal este aceeași cu partea Animalcare este utilizată în Bat::Mammalși se poate spune că Batare o singură parte în reprezentarea sa Animal, iar apelul Bat::eat()devine fără ambiguitate.
Moștenirea virtuală este implementată prin adăugarea de pointeri la Mammalși WingedAnimal. Astfel Batapare ca (ptr, Mammal, ptr, WingedAnimal, Liliac, Animal). *ptr conține informații despre decalajul din memorie între începutul lui Mammal/ WingedAnimalși Animal. Din acest motiv, nu numai eliminarea duplicării părții comune Animalpentru Mammalși WingedAnimal, ci și un mecanism simplu de conversie a unui pointer (referință) la un obiect al clasei moștenitoare într-un pointer (referință) la un obiect al oricărei clase de bază este furnizate. Evident, utilizarea excesivă a moștenirii virtuale va da o oarecare degradare a performanței (asemănătoare cu cea a funcțiilor virtuale - datorită unei operații suplimentare de citire).
Pentru a înțelege esența moștenirii virtuale fără prea mult „zgomot”, luați în considerare următorul exemplu:
#include <iostream> clasa A { public : virtual int foo () { întoarcere 1 ; } }; clasa B : public virtual A {}; clasa C : public virtual A {}; clasa D : public B , public C {}; int main () { D d ; std :: cout << d . foo (); returnează 0 ; }Dacă cuvântul cheie virtual este eliminat , atunci metoda foo() nu poate fi definită în mod unic și, ca urmare, nu va fi disponibilă, la fel ca un obiect din clasa D - codul nu se va compila.
C++ | |
---|---|
Particularități | |
Unele biblioteci | |
Compilatoare | |
influențat | |
|