7. gyakorlat¶
Ismétlés¶
- Öröklődés
- Polimorfizmus
- Virtualizáció
- Override
Pure virtual¶
Az előző órán megismertük a virtualizáció lényegét, most a pure virtual kifejezést nézzük át. Ahogy láttuk a virtualizált metódusoknak a viselkedése polimorfikusan felülírható, azaz az ősosztály-beli metódus X lefutású a gyerek osztály beli felüldefiniált lefutás Y. Ezzel feltételeztük, hogy az ős osztályban biztosan meg tudtuk írni az ahhoz köthető lefutást, az ősosztály is egy teljes egészében használható osztály.
Azonban sokszor a modellezett entitást nem tudjuk teljes egészében meghatározni, egy adott viselkedést meghatározó metódust például nem tudunk implementálni, mert nincs értelme az adott entitáson belül. Vegyünk egy egyetemi polgárt: ez a modell rendelkezik a teendoket_vegez metódussal, azonban ez hallgató esetén a tanulás, PhD-hallgató esetén a tanulás és az oktatás, vezető beosztású egyénnél ez lehet például az adminisztráció. Ha az egyetemi polgárt szeretnénk megvalósítani, nem tudjuk, hogy a teendoket_vegez metódus mit csinál mivel nem tudunk egy általános viselkedést megfogalmazni, ezért az osztályunk nem teljes. Nem teljes osztályból nem célszerű példányosítani, hiszen hasztalan önmagában, csakis az öröklődésben, általánosításban van haszna.
Ezek az osztályok az abstract osztályok, amelyek ismerősek Javaból is. Az absztrakt osztályok nem példányosíthatóak. Javaban erre külön kulcsszó is létezett, azonban C++ esetében egy osztály akkor absztrakt, ha van pure virtual (tisztán virtuális) metódusa, ám ekkor nem példányosítható az adott osztály. A tisztán virtuális metódus egy olyan metódus, aminek nincs implementációja az adott osztályban (nem csak üres az implementációja, azonban ha nem tennénk implementációt, fordítási hibát kapnánk, hiszen egy metódus deklarációhoz nincs definíció), ennek jelölése, hogy a virtuális metódusunkat egyenlővé tesszük 0-val. Erre példa:
1 2 3 4 5 6 7 |
|
Ahogy látszódik, a teendoket_vegez metódus virtuális, de hiányzik a megvalósítás, ezt a törzs helyére írott '=0
' kifejezéssel jelezzük. Ekkor az öröklési láncban lejjebb lévő (leszármazott) minden osztálynak (közvetlen leszármazottnak) kötelező megvalósítani. Ha nem valósítja meg a leszármazott osztály, az is absztrakt osztállyá válik.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Ahogy látszódik, a hallgató egyetemi polgár, és egy olyan típus, amiből lehet példányt gyártani, mivel meg van valósítva a pure virtual metódus.
Ezzel szemben az alkalmazott egy még mindig általános entitás (nem példányosítható), hiszen lehet kutató munkatárs, esetleg gazdasági osztályon dolgozó és még sok egyéb. Mindegyik alkalmazott jár megbeszélésekre, azonban ott más-más módon szerepelhetnek (polimorfikus hívás -> virtual method). Mivel az Alkalmazott szintén absztrakt, kell legyen pure virtual metódusa. Ez az örökölt, és meg NEM valósított teendoket_vegez pure virtual metódus.
1 2 3 4 5 6 7 |
|
A gazdaságis már olyan típus, amiből példányosítani szeretnénk, ezért meg kell valósítani az összes pure virtual metódust, ami jelen esetben a teendoket_vegez metódus. Mivel csak a Gazdaságis és a Hallgató példányosítható típus, csak azokból lehet objektumot előállítani, de ős típusként, paraméterként szerepelhet az összes többi típus is.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Ahogy az látható, a Gazdaságis és Hallgató létrehozható, a többi típus azonban paraméterként és változó típusaként használható. Polimorfikusan Alkalmazott típusú változót létrehozhatunk, de ekkor meg kell oldanunk, hogy egy pl. Gazdaságis objektum kerüljön oda. Ezt pointer vagy referencia használatával megtehetjük.
Nagyon fontos, hogy az Egyetemi polgárként létrehozott gazdaságist nem tudjuk átadni Alkalmazottként, hiszen a rendszer csak annyit tud, hogy ott egy egyetemi polgár típus van (vagy annak egy leszármazottja). A futásidejű típus, és annak viselkedése már nem ismert.
Virtuális destruktor¶
Azt láttuk, hogy ha az ős osztály típusával szeretnénk kezelni a leszármazott osztályt és mégis a leszármazottban megvalósított viselkedést szeretnénk, akkor virtual metódust kell használni. Ez nem különbség a destruktor esetében sem.
Feljebb láthattuk, hogy egy Alkalmazott* típusú változónak adhatunk egy Gazdaságisnak foglalt memóriát. Azonban ekkor ha ki akartuk volna törölni a pointereket az Alkalamazott típust töröltük volna, hiszen:
- a desturktor egy metódus
- nem volt virtual
- a pointer típusa ős típus volt
Ez a fenti példában nem okoz nagyobb hibát, azonban ha az egyik leszármazott osztály erőforrásokat foglal, annak felszabadítása lényeges. Ha nem fut le a desturktora egy rossz polimorfikus hívás miatt, az adott erőforrást elveszítjük, de nem lesz felszabadítva.
Legyen egy Kutató osztály. Ez a kutató egy Alkalmazott. A munkásságát egy dinamikus int tömb jelzi. Egy-egy int a munkásságának fontosságát jelölje!
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Az egyetemben legyen egy alkalmazottakat tároló dinamikus tömb! Ekkor minden alkalmazott ebbe kerül bele. Természetesen Gazdaságis és Kutató is kerülhet bele, hiszen Alkalmazott mind a kettő.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
Látható, hogy az egyetembe Alkalmazottak kerülnek be. Mikor az egyetemet töröljük töröljük az alkalmazottak munkahelyét és az alkalmazottakat is. Egy alkalmazott törlésekor azonban tudnunk kell, hogy Gazdaságis vagy Kutató, hiszen kutató esetén az ő erőforrásait is ki kell törölni. Ez automatikusan a destruktor használatával ment, de ha nem polimorfikus nem kerül meghívásra, akkor nem kerül meghívásra csak az Alkalmazott destruktora, ami a gyerekosztályban foglalt memóriát nem fogja felszabadítani.
Ezt javíthatjuk, ha az Alkalmazott desturktora is virtuális lesz, azaz működhet polimorfikusan. Erre az esetleges problémára a fordító is felhívja a figyelmünket az alábbi figyelmeztetéssel:
warning: deleting object of abstract class type ‘Alkalmazott’ which has non-virtual destructor will cause undefined behavior [-Wdelete-non-virtual-dtor] delete alkalmazottak[i]; // Egy alkalmazott letrehozasa: new -> delete
Feladatok¶
-
Gyakorló feladatsor (a 9. gyakorlat gyakorló feladatsorának bővítése)
-
Hozzunk létre egy eloleny osztályt!
- Legyen egy kromoszomaszam adattagja (unsigned). Írj paraméteres konstruktort az osztályhoz, mely kiírja a standard outputra, hogy "eloleny letrehozva".
- Írj destruktort, mely kiírja: "eloleny torolve"
- Legyen egy void taplalkozik() const metódusa, amely pure virtual
-
Hozzunk létre egy noveny osztályt, ami az elolenybol öröklődik publikusan.
- Legyen egy unsigned klorofill_hetkonysag adattagja, amit a konstruktor állít be a kromoszomaszammal együtt.
- Próbáld példányosítani. Mi történik? Hogy lehetne javítani?
- Legyen egy private void fotoszintetizal() metódusa, amit meghív a taplalkozik(). A fotoszintetizal
kiirja a standard outputra, hogy "a noveny
db energiat allitott elo"
-
Származzon publikusan a meglévő allat osztaly is az eloleny osztalyból.
- Javítsd a felmerülő fordítási hibákat! (Tipp: a macskák kromoszómaszáma 38)
- Melyik virtual kulcsszó vált fölöslegessé?
- Próbáld ki, hogy egy dinamikusan foglalt macska objektumra eloleny pointeren keresztül hivatkozol és azon keresztül törlöd. Mi a tapasztalat? Hogy tudod javítani?
Megoldás
- Kód: Gyakorló feladatsor
- Videó: https://youtu.be/431UBAZjiWs
-