C++ programozási nyelv Gyakorlat - 8. hét Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. november A C++ programozási nyelv Soós Sándor 1/12
Tartalomjegyzék Miért örököltetünk osztályokat? 1. példa A virtual kulcsszó 2. példa Polimorfizmus, többalakúság Early binding, late binding Binding példa Miért jó a polimorfizmus? Pure virtual, tisztán virtuális metódusok Mire jók az absztrakt osztályok? A C++ programozási nyelv Soós Sándor 2/12
Miért örököltetünk osztályokat? 1. eset: Gyerek = szülő + valami a gyerek mindent tud, amit a szülő + tud még mást is ezzel az esettel foglalkoztunk a múlt héten 2. eset Gyerek = szülő, másképp a gyerek ugyanazokat a dolgokat tudja, mint a szülő, de másképp ezzel foglalkozunk ma! A gyakorlatban a két dolog együtt zajlik. Általában egy gyerek egyrészt kiterjeszti a szülőjét újabb funkciókkal, másrészt egyes örökölt tulajdonságokat (metódusokat) módosít (felüldefiniál). A C++ programozási nyelv Soós Sándor 3/12
1. példa class Szemely { public: virtual void Koszon(); }; void Szemely::Koszon() { printf("jó napot!\n"); }; class Diak : public Szemely { public: virtual void Koszon(); }; void Diak::Koszon() { printf("jó szerencsét!\n"); }; int main() { Szemely sz; Diak d; sz.koszon(); d.koszon(); return 0; }; // Jó napot! // Jó szerencsét! Ebben az esetben nem történt semmi váratlan. Ha most elhagynám a virtual kulcsszót, ugyanez lenne az eredmény. A C++ programozási nyelv Soós Sándor 4/12
A virtual kulcsszó Két helyen írtuk le a virtual kulcsszót: a szülőben a gyerekben Mindkét helyen csak a deklarációban, a definícióban nem szabad kiírni! Ha most elhagynám, akkor ez a példa változatlanul működne, de nem célszerű elhagyni! Amikor egy függvényt felül fogunk definiálni az utódokban, akkor használjuk a virtual-t a szülő osztályban! Erre később még visszatérünk! A gyerek osztályban mindegy, hogy kiírjuk-e, de érdemes kiírni dokumentációs szempontból, a program működését nem befolyásolja. A C++ programozási nyelv Soós Sándor 5/12
2. példa void Diak::Koszon() { Szemely::Koszon(); printf("jó szerencsét!\n"); }; int main() { Diak d; d.koszon(); return 0; }; // Jó napot! // Jó szerencsét! A leszármazott egy felüldefiniált metódusban meghívhatja az ősének ugyanilyen nevű metódusát Mi történik, ha a Diak::Koszon-ben kihagyom a Szemely:: -t? Ezt is leírhatom a main-ben: d.szemely::koszon(); szintaktikailag helyes, de kerülendő! Az objektum belső dolgait ne használjuk fel kívül. A C++ programozási nyelv Soós Sándor 6/12
Polimorfizmus, többalakúság Nézzük meg a pelda3.cpp fájlt! Mi történik, ha nem virtualnak deklarálom a Koszon függvényt? Vizsgáljuk meg az f1() f4() függvényeket! A C++ programozási nyelv Soós Sándor 7/12
Egy kis háttér: Early binding, late binding Mit befolyásol a virtual kulcsszó? Amikor a fordító találkozik egy metódus meghívásával, akkor kétféleképpen járhat el: ha a metódus nem virtuális, akkor a hívás helyére beírja az ott szereplő osztály hívott metódusának kezdőcímét, illetve az annak megfelelő jelzést. Ez fordítási időben megtörténik (early binding korai kötés) ha a metódus virtuális, akkor fordításkor csak egy hivatkozás kerül ide az osztályhoz tartozó Virtuális Metódus Tábla (VMT) megfelelő bejegyzésére A VMT futás időben kerül kitöltésre, csak ekkor dől el, hogy az egyes virtuális metódusok ténylegesen milyen memóriacímre mutatnak (late binding késői kötés) Nézzük meg az előző példa f1() függvényét! A C++ programozási nyelv Soós Sándor 8/12
Binding példa void f1( Szemely &sz) { printf("f1: "); sz.koszon(); }; Ha a Koszon() metódus nem virtuális, akkor az f1() lefordításakor sz.koszon() helyére a Szemely osztály Koszon metódusának címe kerül. Ha viszont Koszon() virtuális, akkor ide csak az a jelzés kerül, hogy az f1()-nek átadott paraméter VMT táblájának 1. helyén szereplő címre kell ugrani. Majd futás időben derül, ki, hogy az f1() függvénynek milyen típusú objektumot adtunk át. A polimorfizmus miatt ez valóban csak futásidőben derül ki. A C++ programozási nyelv Soós Sándor 9/12
Miért jó a polimorfizmus? Nézzük meg a Jegypenztar.cpp fájlt! Egy programozó megírta a Jegypenztar() függvényt. Később bármikor létrehozhatunk egy újabb leszármazottat a Szemelyből, arra is működni fog a Jegypenztar, még csak újra sem kell fordítani. Konténerben kezelt objektumok (geometria hf) Ha egy konténerben (például egy tömbben) szeretnénk tárolni különböző típusú objektumokat (például a síkidomokat), akkor a közös ősből hozzuk létre a konténert. (Tömb esetén csak pointerekkel működik mindez!) Ezek után egy ciklusban sorra lefuttathatjuk például a Kirajzoló, virtuális metódust. Minden síkidom esetén a megfelelő metódus fog lefutni. A C++ programozási nyelv Soós Sándor 10/12
Pure virtual, tisztán virtuális metódusok class Szemely { public: virtual void Koszon() = 0; }; Ezzel a szintaktikával jelöljük a tisztán virtuális metódusokat. Absztrakt osztály: van legalább egy tisztán virtuális tagfüggvénye nem példányosítható Nézzük meg az absztrakt.cpp fájlt! A C++ programozási nyelv Soós Sándor 11/12
Mire jók az absztrakt osztályok? Nyelvi eszközökkel segítik a tervezést. Az absztrakt osztályban deklaráljuk, hogy milyen metódusokat kell mindenképpen megvalósítani egy osztályban ahhoz, hogy példányosítani lehessen azt. Amíg nem írtuk meg az összes pure virtual metódust, addig nem lehet példányt létrehozni belőle. Miért fontos ez? ez azt jelenti, hogy ezekre a tulajdonságokra (metódusokra) biztosan számíthatunk minden leszármazott osztályban Tervezzük tovább a múlt órai geometriai oktatóprogramot az új fogalmak felhasználásával! A C++ programozási nyelv Soós Sándor 12/12