Bevezetés a programozásba 2 5. előadás: Öröklődés
emlékeztető Tagfüggvény struct koord { double x,y,r; void set(double ux, double uy) { x=ux; y=uy; r=sqrt(x*x+y*y); } Használat: koord k; k.set(4,5); Egységbezárás
emlékeztető Dinamikus memóriakezelés T *m; m = new T; *m... delete m; A new operátorral kérünk memóriát, T típus méretével megegyező mennyiségben, az OS az m mutatónkba adja a címét Ha már nem kell, delete operátorral szólunk A delete nem nullázza m-et!
emlékeztető Láthatóság struct S { // ez látszik kívülről private: // ez nem látszik // amíg más módosító nem érkezik // mezők, tagfüggvények... public: // innentől megint látszik // mezők, tagfüggvények...
emlékeztető Interface és implementáció struct Vektor { Vektor(int M); ~Vektor(); int osszeg(); private: int *v; int m; Vektor::Vektor(int M) { v=new int[m]; m=m; for (int i=0;i<m;i++) v[i]=i+1; } Vektor::~Vektor() {delete v;} int Vektor::osszeg() { int sum=0; for (int i=0;i<m;i++) sum+=v[i]; return sum; } int main() { int M=10; Vektor v(m); cout << v.osszeg(); } Interface (felület) Implementáció (megvalósítás)
emlékeztető vektor.hpp #ifndef VEKTOR_HPP #define VEKTOR_HPP Implementáció elrejtése vektor.cpp #include vektor.hpp struct Vektor { Vektor(int M); ~Vektor(); int osszeg(); private: int *v; int m; #endif main.cpp #include vektor.hpp Vektor::Vektor(int M) { v=new int[m]; m=m; for (int i=0;i<m;i++) v[i]=i+1; } Vektor::~Vektor() { delete v; } int Vektor::osszeg() { int sum=0; for (int i=0;i<m;i++) sum+=v[i]; return sum; } int main() { int M=10; Vektor v(m); cout << v.osszeg(); }
emlékeztető Implementáció elrejtése Három szintet láttunk: Láthatóság módosítása A fordítóprogram segítsége a különböző szerepű komponensek független fejlesztésében Több fájlba bontott program, fejlécfájlok Fordítási egységek, személyekre bontható illetékesség, a kód ismerete nem szükséges Könyvtárak Újrafelhasználható típusok gyűjteménye, előre lefordítva, a kód megismerése sem lehetséges
Redundancia Alapvető elv: a programban minden csak egyszer szerepeljen. A redundáns kód változtatása az ismétlődések és összefüggések minden pontján változtatást jelent Következmény: amit lehet, ki kell emelni Eddig Ciklust használtunk ismétlődő utasítások helyett Konstansokat használtunk számok helyett Függvényeket használtunk gyakori kódrészletek helyett
Öröklődés, bevezető Az öröklődés lényege, hogy egy meglevő osztályból kiindulva hozunk létre új osztályt, és csak a változásokat írjuk meg Ősosztály: az az osztály, amiből kiindultunk Leszármazott: az az osztály, amiben felhasználtuk az ősosztályt Minden class és struct lehet ősosztály
Öröklődés class A { protected: int x; public: A() {x=0;} void kiir() {cout<<x;} class B : public A { public: B() {x=1;} Ősosztály Leszármazott int main() { B b; b.kiir(); }
Öröklődés Lehetséges változások, amiket a leszármazott tartalmazhat: Új metódusok, amik az ősosztályban nem léteztek Új mezők Felüldefiniálás: egy már meglevő metódus mást csinál A felüldefiniált metódus haszna, hogy más-más leszármazottak más-más viselkedésre képesek
Öröklődés class A { protected: int x; public: A() {x=0;} void kiir() {cout<<x;} class B : public A { public: B() {x=1;} void ujfuggveny(..) {..} T ujmezo; új tagfüggvény új mező void kiir() { //mást csinálunk } felüldefiniált tagfüggvény
Példa öröklődés használatára Szeretnénk két gombot csinálni, amik mást csinálnak, ha megnyomják őket, de mindkettő jelezze a színével, ha felette van az egér Megoldás: olyan ősosztályt csinálni, amiben a szín kezelése meg van oldva majd a két leszármazottban a két funkciót megírjuk Így minden kód csak egyszer van a programban
Példa öröklődés használatára Következmény: létrehozunk egy Gomb osztályt, ami konkrét funkció nélkül, minden gomb közös vonásait viseli megjelenés eseménykezelés felület a program többi része felé... Amit később úgy használunk, hogy öröklődünk belőle, és a konkrét funkciót az örökösbe tesszük
Polimorfizmus Többalakúság Ilyet már láttunk: istream és ifstream is-a reláció: az ifstream az egy istream. A kockarajzológomb az egy gomb. A [leszármazott] az egy [ősosztály] Dinamikus memóriakezelésnél ez látványos: ha B leszármazottja A-nak, akkor A * m=new B(..) A típusfogalmunk finomodott: m-nek kétféle típusa van
Polimorfizmus ha B leszármazottja A-nak, akkor A * m=new B(..) m statikus típusa: A*, hisz így lett deklarálva m dinamikus típusa: B*, hisz így lett példányosítva Ennek a tulajdonságnak komoly előnye, hogy futásidőben dönthetjük el a (dinamikus) típust lehet pl. vektorunk különböző (dinamikus) típusú elemekkel mint a második beadandóban...
Polimorfizmus A metódushívásnál a típus művelete hívódik meg. De most melyik típus szerinti művelet? Ezt eldönthetjük: Alapértelmezésben a statikus típus szerinti tagfüggvény hívódik meg Ha szeretnénk, hogy egy tagfüggvény a dinamikus típus szerint hívódjék meg, használjuk a virtual módosítót
Polimorfizmus class A { public: void simafv() { cout << "A1 ";} virtual void vfv() { cout <<"A2 ";} class B : public A { public: void simafv() { cout << "B1 ";} virtual void vfv() { cout <<"B2 ";} int main(){ A *aa = new A; A *ab = new B; B *bb = new B; aa->simafv(); aa->vfv(); ab->simafv(); ab->vfv(); bb->simafv(); bb->vfv(); return 0; } Az eredmény: A1 A2 A1 B2 B1 B2
Polimorfizmus class A { public: void simafv() { cout << "A1 ";} virtual void vfv() { cout <<"A2 ";} class B : public A { public: void simafv() { cout << "B1 ";} virtual void vfv() { cout <<"B2 ";} int main(){ A *aa = new A; A *ab = new B; B *bb = new B; aa->simafv(); aa->vfv(); ab->simafv(); ab->vfv(); bb->simafv(); bb->vfv(); return 0; } Az eredmény: A1 A2 A1 B2 B1 B2 Dinamikus kötés
Öröklődés class A { protected: int x; public: A() {x=0;} virtual void kiir() {cout<<x;} class B : public A { public: B() {x=1;} void ujfuggveny(..) {..} T ujmezo; virtual void kiir() { //mást csinálunk, például //másképp írjuk ki } Általában tehát ennek lesz haszna: virtuális tagfüggvényekkel biztosítjuk a dinamikus kötést, ezzel nyitva hagyva a lehetőséget az általános ősosztályból a konkrét leszármazott felé
Néhány példa Sakkprogram Ősosztály: Bábu Leszármazottak: Vezér Bástya, stb Indoklás: a lépés lehetőségének kiszámítása más-más kódrészleteket kíván, de egységes felületre van szükség, tehát a virtuális műveletek: kirajzolás, lépéslehetőségvizsgálat az ősosztálynak már lehet akár közös lépés(ide) metódusa, ami ellenőrzi a lépés érvényességét a virtuális lépéslehetőség metódussal.
Néhány példa Ablakozó (ilyet kell majd csinálni) Ősosztály: widget ( bigyó ) Leszármazottak: gomb számbeállító, stb Indoklás: Így megoldható egy olyan közös kezelőfelület, amelyik a fókuszt, megjelenítést és az események továbbítását végzi, függetlenül a tartalomtól. Az egyes widgetek megjelenítő, kiválasztó, eseménykezelő tagfüggvényei virtuálisak, ezért közös felületet alkotnak
Néhány példa Játékprogram: Márió Ősosztály: Sprite Leszármazottak: Márió, Szörny, Háttér, Platform Indoklás: A rajzolás műveletei ezzel kiemelhetőek, a Szörny osztály továbbörökíthető az egyes jellemző viselkedésű leszármazottakra. Közös rajzolómotor hozható létre. A viselkedés virtuális tagfüggvény a Márió-ban a felhasználót, a Szörny-ben a gépi irányítást tartalmazza.
Öröklődés Nagyon sok lehetőség van az öröklődésben, ebből nekünk ezek fontosak egyelőre: Statikus és dinamikus típus Virtuális tagfüggvények Tehát egyelőre a következő irányelveket tartsuk be, bár néha indokolt lehet az ellenkezője: minden felüldefiniált metódus legyen virtuális private helyett protected láthatóság mindig :public öröklés
Öröklődés Témák, amikkel még foglalkozunk később: protected és private öröklés absztrakt metódus, absztrakt osztály esetleg többszörös öröklés Sok következménye van az öröklődés lehetőségének, ezekkel a lehetőségekkel szerencsére nem kell mind foglalkozni ahhoz, hogy a már megismert lehetőségeket használjuk
Objektum elvű programozás Objektum orientált programozás (OOP) Új hozzáállásra van szükség Eleve úgy tervezzük a típusainkat, hogy kihasználjuk a lehetséges öröklődésből származó előnyöket A meglevő (akár szabványos) típusokat is felhasználjuk ősosztálynak, ha hasznos A tagfüggvények szerepét az eddigieknél is alaposabban kell megválasztani, a funkciók szétválasztásának elmulasztása követhetetlenné teszi a szerkezetet
Összefoglalás Öröklés: egy osztály újrafelhasználása, hogy csak a változásokat kelljen megírni Dinamikus típus, dinamikus kötés, polimorfizmus: a konkrét tagfüggvény kiválasztás híváskor futásidőben dől el Új tervezési mód: az azonos szerepű tagfüggvényekkel rendelkező típusokat közös ős köré szervezzük