A C++ öröklés (Előfeltétel: 12. tétel ismerete) Az öröklés fogalma:
olyan alapvető programozási technika, amely lehetővé teszi, hogy a már meglévő osztályainkból újakat tudunk származtatni, valamint az egymással kapcsolatban álló osztályokat hierarchiába tudjuk rendezni.
Egy meglévő osztályból újat származtathatunk azáltal, hogy a meglévő osztály nevét feltüntetjük az új osztály deklarációjában. A már meglévő osztályt ősosztálynak, az új osztályt származtatott osztálynak nevezzük. A származtatott osztály örökli az ősosztály minden tagját, a származtatott osztályt új tagokkal láthatjuk el (változók, függvények). A származtatott osztály konstruktora explicit módon inicializálhatja az ősosztályát azáltal, hogy paramétereket ad át az ősosztály konstruktorának. Ha a származtatott osztály konstruktora expliciten nem inicializálja az ősosztály konstruktorát, akkor a fordító automatikusan az ősosztály konstruktorát hívja meg. Példa: #include #include <string> using namespace std; //Õsosztály class CEmberek { private: int iGondolat; public: CEmberek() { } CEmberek(int iparam) { iGondolat = iparam; } ~CEmberek() { } void Gondolkodik() { iGondolat = 1; } }; //Származtatott osztály class CProgramozo : public CEmberek { private: int iSorokSzama; void SorInit(int iSzam) { iSorokSzama = iSzam; } public: CProgramozo() { } CProgramozo(int iparam) : CEmberek(iparam) { SorInit(iparam); } ~CProgramozo() { } void SokSortIszik() { iSorokSzama++; } int GetSorokSzama() { return iSorokSzama; } }; void main() { CProgramozo Geza(25); cout << "Geza " << Geza.GetSorokSzama() << " sort iszik\n"; Geza.SokSortIszik(); cout << "Geza " << Geza.GetSorokSzama() << " sort iszik"; getchar(); }
1
Látható, hogy a két osztály nagyon hasonló, a programozó is az emberek halmazába tartozik. Tehát a programozó rendelkezik az emberekre jellemző tulajdonságokkal, ezen felül iszik sört. A : public CEmberek kifejezés hatására a CProgramozo a CEmberek osztályból származó osztály lesz. Ezért örökli a CEmberek osztály adattagjait és tagfüggvényeit. Ha a CProgramozo osztályt teljesen üresen deklaráltuk volna, akkor is rendelkezne az ősosztálya adattagjaival és metódusaival(tagfüggvényeivel). A „class CProgramozo : public CEmberek” public kulcsszavának hatására az ősosztály minden nyilvános tagja nyilvános marad a származtatott osztályban is. Nagyon ritka az az eset, amikor nem így származtatunk, csak speciális esetekben fordul elő. A CProgramozo osztály void SokSortIszik() taggfüggvénye már a CProgramozo osztály egyedi jellemző függvénye. A konstruktorok és destruktorok meghívásának a sorrendje: Egy származtatott osztály példányosításakor a fordító a következő sorrendben hívja meg a konstruktorokat: 1. Ősosztály konstruktora. 2. Származtatott osztály tagobjektumainak (azon adattagjainak, amelyek objektumok(példányosított osztályok)) konstruktorai. Ezeket a konstruktorokat olyan sorrendben hívja meg, amilyen sorrendben azok az adott osztályban definiálva lettek. 3. Az osztály saját konstruktora. A destruktorok – amennyiben definiálva vannak – pontosan az ellenkező sorrendben futnak le. Így, amikor egy adott konstruktor hajtódik végre, tudni lehet, hogy az ősosztály és a tagobjektumok már inicializálásra kerültek, és így biztonságosan használhatóak. Hasonlóan, amikor egy adott destruktor fut le, tudjuk, hogy sem az ősosztály sem bármelyik tagobjektum nem lett még megsemmisítve, és így azok még mindig használhatóak. Ha az ősosztály egy adattagja védett, azaz deklarációjában szerepel a protected hozzáférés-módosító, akkor a tag elérhető az osztályból származtatott osztályban, de a program más függvényei által nem. Az int iGondolat változót nem tudjuk elérni az származtatott osztályból, vannak esetek, amikor erre szükség van. Ezt a következőképpen tehetjük meg. Példa: class CEmberek { protected: int iGondolat; public: CEmberek() { } CEmberek(int iparam) { iGondolat = iparam; } ~CEmberek() { } void Gondolkodik() { iGondolat = 1; } }; Mivel az iGondolat változó protected lett, így már a CProgramozo származtatott osztályból is elérhető lesz. Egy származtatott osztály szolgálhat egy másik osztály őseként. Ilyen módon az egymással kapcsolatban álló osztályok többszintű hierarchiája valósítható meg. Az öröklődés segítségével lehetőség nyílik egy osztály korábban megírt adatstruktúráinak és kódjának újrafelhasználására. Ezáltal könnyebben kezelhető a program és lehetőség van a program által kezelt objektumok kapcsolatrendszerének modellezésére. #include #include <string> using namespace std; //Õsosztály, a CApolo közvetett(indirekt) őse class CEmberek { protected: int iGondolat; public: CEmberek() { } CEmberek(int iparam) { iGondolat = iparam; } ~CEmberek() { } void Gondolkodik() { iGondolat = 1; } };
2
//Származtatott osztály, CApolo közvetlen(direkt) őse class CProgramozo : public CEmberek { private: int iSorokSzama; void SorInit(int iSzam) { iSorokSzama = iSzam; } public: CProgramozo() { } CProgramozo(int iparam) : CEmberek(iparam) { SorInit(iparam); } ~CProgramozo() { } void SokSortIszik() { iSorokSzama++; } int GetSorokSzama() { return iSorokSzama; } }; //Származtatott osztály class CApolo : public CProgramozo { private: int iSorokSzama; void SorInit(int iSzam) { iSorokSzama = iSzam; } public: CApolo() { } CApolo(int iparam) : CProgramozo(iparam) { SorInit(iparam); } ~CApolo() { } void SokSortIszik() { iSorokSzama++; } int GetSorokSzama() { return iSorokSzama; } }; void main() { CProgramozo Geza(25); cout << "Geza " << Geza.GetSorokSzama() << " sort iszik\n"; Geza.SokSortIszik(); cout << "Geza " << Geza.GetSorokSzama() << " sort iszik"; getchar(); } Az int iGondolat bármelyik CEmberek-ből származtatott osztályból elérhető, de nem érhető el közvetlenül az osztályhierarchián kívüli kódrészben. A származtatott osztályok hierarchiájában minden osztály adott tagfüggvényének saját változatával rendelkezhet. Ha ez a függvény virtuális is egyben, akkor ennek meghívásakor mindig az aktuális objektum típusának megfelelő változat fog lefutni, még akkor, ha a hívás egy ősosztályra mutató mutatón keresztül történik. A virtuális függvények a polimorfizmus is támogatják, azaz egyetlen utasítás segítségével több, művelet futhat le, és hogy éppen melyik, az a szóban forgó objektum típusától függ.
különféle
A virtuális függvények segítségével egyszerű, általános célú rutinok írhatóak, amelyek különböző, de egymással kapcsolatban álló objektumok széles választékát tudják kezelni.
3
A virtuális függvények segítségével továbbá lehetőség van az ősosztály viselkedésének módosítására anélkül, hogy a forráskódon módosítanánk. Amikor a származtatunk, akkor a kód megkettőzése és a lehetséges egyéb redundanciák elkerülése cél. Példa: #include #include <string> using namespace std; //Õsosztály class CEmberek { protected: int iGondolat; public: CEmberek() { } CEmberek(int iparam) { iGondolat = iparam; } ~CEmberek() { } virtual void Gondolkodik() { iGondolat = 1; } }; //Származtatott osztály class CProgramozo : public CEmberek { private: int iSorokSzama; void SorInit(int iSzam) { iSorokSzama = iSzam; } public: CProgramozo() { } CProgramozo(int iparam) : CEmberek(iparam) { SorInit(iparam); } ~CProgramozo() { } void SokSortIszik() { iSorokSzama++; } int GetSorokSzama() { return iSorokSzama; } virtual void Gondolkodik() { iGondolat = 10; iSorokSzama += iGondolat; } }; //Származtatott osztály class CApolo : public CProgramozo { private: int iSorokSzama; void SorInit(int iSzam) { iSorokSzama = iSzam; } public: CApolo() { } CApolo(int iparam) : CProgramozo(iparam) { SorInit(iparam); } ~CApolo() { }
4
void SokSortIszik() { iSorokSzama++; } int GetSorokSzama() { return iSorokSzama; } virtual void Gondolkodik() { iGondolat = 100; iSorokSzama += iGondolat; } }; void main() { CProgramozo Geza(25); CApolo Anett(25); cout << "Geza " << Geza.GetSorokSzama() << " sort iszik\n"; Geza.SokSortIszik(); cout << "Geza " << Geza.GetSorokSzama() << " sort iszik\n"; Geza.Gondolkodik(); cout << "Geza " << Geza.GetSorokSzama() << " -ra gondolt.\n"; Anett.Gondolkodik(); cout << "Geza " << Anett.GetSorokSzama() << " -re gondolt.\n"; getchar(); } A program futásának az eredménye: Geza 25 sort iszik. Geza 26 sort iszik. Geza 36 -ra gondolt. Anett 125 -re gondolt. A virtuális függvények az objektum orientált programozás egy fontos vonását támogatják, a polimorfizmust(többalakúság). Akkor beszélünk többalakúságról, ha egyetlen utasítással többfajta különböző művelet hajtható végre, és az, hogy éppen melyik, attól függ, hogy éppen melyik objektumról van szó. Fenti példa jól mutatja ki hogyan gondolkodik és a gondolkodás milyen hatással van a megivott sörök számára. Az öröklés előnyei: Az öröklés segítségével újra felhasználhatóvá válnak a már megírt kódok és a már megtervezett adatstruktúrák. Ezáltal elkerülhető az adat és kód megkettőződése. A programokat könnyebben lehet karbantartani, mivel egy adott feladatot megvalósító kód és adatok általában a programban egyetlen, könnyen elérhető osztály definíciójában szerepelnek ahelyett, hogy szétszórva helyezkednének el a forráskódban. Számos, valós világból származó kapcsolatot lehet hatékonyan modellezni a C++ - ban definiált osztályhierarchiával. Például az emberek osztályából származtatott programozónak is szüksége lehet ápolóra, aki szintén programozó, hogy hatékony és gyors segítséget tudjon nyújtani a programozónak.
5