Virtuaalinen periytyminen ( eng. virtual inheritance ) C ++ -ohjelmointikielessä on yksi niistä periytymisvaihtoehdoista , joita tarvitaan ratkaisemaan joitakin ongelmia, jotka aiheutuvat moniperinnön mahdollisuudesta (erityisesti " timantin muotoinen perintö "), ratkaisemalla moniselitteisyyden, mitkä menetelmät ovat superluokista(välittömät esivanhemmat) on käytettävä. Sitä käytetään tapauksissa, joissa moninkertainen periytyminen esivanhempainluokkien ominaisuuksien tarkoitetun täyden koostumuksen sijaan johtaa käytettävissä olevien perittyjen ominaisuuksien rajoitukseen epäselvyyden vuoksi. Perusluokka, joka on useita peritty, määritellään virtuaaliseksi avainsanalla virtual.
Harkitse seuraavaa luokkahierarkiaa:
luokka eläin { julkinen : virtuaalinen void syödä (); // Metodi on määritelty annetulle luokalle ... }; luokka Nisäkäs : julkinen eläin { julkinen : Väri getHairColor (); ... }; luokka WingedAnimal : public Animal { julkinen : tyhjä läppä (); ... }; // Lepakko on siivekäs luokka Lepakko : public Mammal , public WingedAnimal {}; //<--- Huomaa, että eat()-metodia ei ohiteta Batissa Batbat ; _Yllä olevan koodin kohdalla puhelu bat.eat()on epäselvä. Se voi viitata Bat::WingedAnimal::Animal::eat()sekä ja Bat::Mammal::Animal::eat(). Jokaisen väliseuraajan ( WingedAnimal, Mammal) kohdalla menetelmä eat()voidaan ohittaa (tämä ei muuta ongelman olemusta kielen näkökulmasta). Ongelmana on, että perinteisen moniperinnön semantiikka ei vastaa sen mallintamaa todellisuutta. Eräässä mielessä olemus Animalon pohjimmiltaan ainutlaatuinen; Bat - tämä on Mammalja WingedAnimal, mutta lepakkon ( ) eläimellisyyden ( Animalness) ominaisuus Bat, se on myös nisäkkään ( Mammal) eläimellisyyden ominaisuus ja se on sama eläimellisyyden ominaisuus WingedAnimal - itse asiassa tämä on yksi ja sama ominaisuus .
Tätä tilannetta kutsutaan yleisesti " timanttiperinnöksi ", ja se on ongelma, jonka virtuaaliperinnöllä on tarkoitus ratkaista.
Ennen kuin jatkat, on hyödyllistä tarkistaa, kuinka luokat esitetään C++:ssa. Erityisesti perinnön aikana esi- ja seuraajan luokat yksinkertaisesti sijoitetaan muistiin peräkkäin. Siten Bat-luokan objekti on itse asiassa sarja luokkaobjekteja (Animal, Mammal, Animal, Winged Animal, Bat), jotka on sijoitettu peräkkäin muistiin, kun taas Animal toistetaan kahdesti, mikä johtaa epäselvyyteen.
Voimme ohittaa luokkamme seuraavasti:
luokka eläin { julkinen : virtuaalinen void syödä (); ... }; // Kaksi luokkaa käytännössä perii Animal: class Mammal : public virtual Animal // <--- huomaa avainsanan virtual { julkinen : Väri getHairColor (); ... }; class WingedAnimal : public virtual Animal // <--- huomaa avainsanan virtual { julkinen : tyhjä läppä (); ... }; // Lepakko on edelleen siivekäs luokka Lepakko : public Mammal , public WingedAnimal {};Nyt luokkaobjektin osa Animalon Bat::WingedAnimal sama kuin se osa Animal, jota käytetään kohdassa Bat::Mammal, ja voidaan sanoa, että Batsen esityksessä on vain yksi osa Animal, ja kutsu Bat::eat()tulee yksiselitteiseksi.
Virtuaalinen periytyminen toteutetaan lisäämällä osoittimia kohtaan Mammalja WingedAnimal. Siten se Batnäkyy muodossa (ptr, Mammal, ptr, Winged Animal, Bat, Animal). *ptr sisältää tiedot muistissa olevasta offsetista Mammal/ alun WingedAnimalja sen välillä Animal. Tästä johtuen ei ainoastaan poisteta yhteisen Animalosan Mammalja -osien päällekkäisyyttä WingedAnimal, vaan myös yksinkertainen mekanismi, jolla osoitin (viittaus) muunnetaan perillisen luokan objektiin osoittimeksi (viittaukseksi) minkä tahansa perusluokan objektiin. tarjotaan. On selvää, että virtuaalisen perinnön liiallinen käyttö heikentää suorituskykyä (samanlainen kuin virtuaalisten toimintojen kohdalla - lisälukutoiminnon vuoksi).
Ymmärtääksesi virtuaalisen perinnön olemuksen ilman liikaa "kohinaa", harkitse seuraavaa esimerkkiä:
#include <iostream> luokka A { julkinen : virtuaalinen int foo () { paluu 1 ; } }; luokka B : julkinen virtuaalinen A {}; luokka C : julkinen virtuaalinen A {}; luokka D : julkinen B , julkinen C {}; int main () { D d ; std :: cout << d . foo (); paluu 0 ; }Jos virtuaalinen avainsana poistetaan , foo() -metodia ei voida määrittää yksiselitteisesti, eikä se siksi ole käytettävissä, kuten luokan D objekti - koodia ei käännetä.
C++ | |
---|---|
Erikoisuudet | |
Jotkut kirjastot | |
Kääntäjät | |
vaikutti | |
|