Virtuális függvények (late binding) Miskolci Egyetem Általános Informatikai Tanszék Virtuális függvények CPP5 / 1 Azonos nevű függvények megkülönböztetése paraméterszignatúra (függvény overloading) - egy hatáskörön belül SCOPE operátor segítségével (classname::fname) mezőkiválasztó operátor (objname.fname) Ez a megkülönböztetés fordítási időben megtörténik. Származtatás: gond lehet az azonos nevű függvényekkel. Megoldás: late binding (késői kötés) Nyelvi eszköz: virtuális függvény Virtuális függvények CPP5 / 2 CPPEX12.C class jarmu protected: int kerekek; double suly; public: jarmu (int k, double s) kerekek = k; suly = s;} void hanykerek (void) cout << "A jarmu kerekeinek szama: " << kerekek << "\n";} Virtuális függvények CPP5 / 3
CPPEX12.C (folyt.) class gepkocsi : public jarmu protected: int szemelyek; public: gepkocsi (int k, double s, int szem) : jarmu (k, s) szemelyek = szem; } void hanykerek (void) cout << "A gepkocsi kerekeinek szama: " << kerekek << "\n";} Virtuális függvények CPP5 / 4 CPPEX12.C (folyt.) main () jarmu bicikli (2,15); bicikli.hanykerek(); gepkocsi trabant (4, 120.0, 4); trabant.hanykerek(); } // Az eremeny: // A jarmu kerekeinek szama: 2 // A gepkocsi kerekeinek szama: 4 Virtuális függvények CPP5 / 5 CPPEX12.C - tanulságok A tagfüggvény meghívása objektumhoz kapcsolva az objektum osztálya határozza meg a hatáskört, amelyben a függvény definíciót keresni kell a fordítóprogram azonosítani tudja a függvényt a megfelelő függvény hívódik meg Virtuális függvények CPP5 / 6
CPPEX13.C main ()// A CPPEX12 osztalydefiniciojaval jarmu *kerekes; kerekes = new jarmu (2,15.); kerekes->hanykerek(); kerekes = new gepkocsi (4, 120.0, 4); kerekes->hanykerek(); } // Az eredmeny: // A jarmu kerekeinek szama: 2 // A jarmu kerekeinek szama: 4 Virtuális függvények CPP5 / 7 CPPEX13.C - tanulságok A tagfüggvény meghívása objektumra mutató pointer segítségével a pointer alaptípusa határozza meg az osztályt, amelynek hatáskörében a függvény definíciót keresni kell a fordítóprogram azonosítani tudja a függvényt nem a megfelelő függvény hívódik meg, ha a pointer alaptípusa és az általa megcímzett objektum típusa eltér Virtuális függvények CPP5 / 8 Virtuális függvény: formai szabályok Csak tagfüggvény definiálható virtuálisnak, az osztálydeklaráció törzsében elhelyezett virtual alapszóval. Az öröklődési hierarchiában lejjebb álló osztályok tartalmazhatnak ugyanilyen nevű, paraméter szignatúrájú és visszatérési értékű tagfügvényeket. Ezek a függvények a virtual alapszó nélkül is virtuálisnak minősülnek. Virtuális függvények CPP5 / 9
Formai szabályok (folyt.) A virtuális függvényt a bázisosztályban kötelező definiálni, a leszármazottak mindegyikében nem. A bázisosztályban a virtuális függvény lehet valódi virtuális függvény - ekkor 0-val kell inicializálni. Például: virtual void hanykerek () = 0; Az ilyen osztály nem példányosítható (absztrakt osztály). Virtuális függvények CPP5 / 10 Formai szabályok (folyt.) Ha mutatón keresztül hívjuk meg a virtuális függvényt, nem a mutató alaptípusa határozza meg, hogy melyik változatot kell használni, hanem a megcímzett objektum típusa. A megcímzett objektum típusa általában csak futási időben derül ki A fordítóprogram általában nem tudja megkeresni a meghívandó függvényt, csak előkészíteni a futásidejű kiválasztását. Virtuális függvények CPP5 / 11 Virtuális függvény használata Használata: ugyanolyan funkciók a leszármazott osztályokban a funkció implementációja osztályfüggő Virtuális függvények CPP5 / 12
Virtuális függvény korai kötéssel Az alábbi esetekben a virtuális függvény hívását is fel tudja dolgozni a fordítóprogram fordítási időben (korai kötés): Objektum hivatkozáson keresztül hívjuk meg. SCOPE operátorral explicite meghatározzuk a definíció helyét. A bázisosztály konstruktorában vagy destruktorában hívjuk meg. Virtuális függvények CPP5 / 13 CPPEX14.C class jarmu protected: int kerekek; double suly; public: jarmu (int k, double s) kerekek = k; suly = s;} virtual void hanykerek (void) cout << "A jarmu kerekeinek szama: " << kerekek << "\n";} Virtuális függvények CPP5 / 14 CPPEX14.C (folytatás) class gepkocsi : public jarmu protected: int szemelyek; public: gepkocsi (int k, double s, int szem) : jarmu (k, s) szemelyek = szem; } void hanykerek (void) cout << "A gepkocsi kerekeinek szama: " << kerekek << "\n";} Virtuális függvények CPP5 / 15
CPPEX14.C (folytatás) main () jarmu *kerekes; kerekes = new jarmu (2,15.); kerekes->hanykerek(); kerekes = new gepkocsi (4, 120.0, 4); kerekes->hanykerek(); } // Az eredmeny: // A jarmu kerekeinek szama: 2 // A gepkocsi kerekeinek szama: 4 Virtuális függvények CPP5 / 16 Példa: grafikus elemek, 1. verzió Location X : Integer Y : Integer GetX() : Integer GetY() : Integer Point Visible : Boolean MoveTo(NewX : Integer, NewY : Integer) IsVisible() : Boolean Circle Radius : Integer MoveTo(NewX : Integer, NewY : Integer) Expand(Ratio : Single) Square Ellipse Radius1 : Integer MoveTo(NewX : Integer, NewY : Integer) Expand(Ratio : Single) Virtuális függvények MoveTo(NewX : Integer, NewY : Integer) CPP5 / 17 Példa: grafikus elemek 1. verzió (folyt.) Implementációk: void Point::MoveTo(int NewX, int NewY) ; // aktuális törlése X = NewX; // új pozíció Y = NewY; ; // megjelenítés az új helyen void Circle::MoveTo(int NewX, int NewY) ; // aktuális törlése X = NewX; // új pozíció Y = NewY; ; // megjelenítés az új helyen Virtuális függvények CPP5 / 18
Példa: grafikus elemek 1. verz.. (folyt.) A két implementáció azonosnak tűnik, mert azonos az algoritmus. A különbség: a Point::MoveTo-ban a Point:: hívódik meg a Circle::MoveTo-ban a Circle:: hívódik meg Ezért nem örökölhető a MoveTo (azaz külön implementáció szükséges minden osztályra)! Virtuális függvények CPP5 / 19 Példa: grafikus elemek 2. verzió Location X : Integer Y : Integer GetX() : Integer GetY() : Integer Visible : Boolean Point <<virtual>> <<virtual>> MoveTo(NewX : Integer, NewY : Integer) IsVisible() : Boolean Circle Radius : Integer Expand(Ratio : Single) Square Ellipse Radius1 : Integer Virtuális függvények CPP5 / 20 Expand(Ratio : Single) Példa: grafikus elemek 2. verzió (folyt.) Különbségek: a Show és Hide függvények virtuálisak a MoveTo függvény öröklődik Eredmény: a MoveTo függvény a this->show és this->hide függvényeket hívja meg a this mutató által megcímzett objektum típusa (amellyel a MoveTo-t használtuk) dönt az objektum aktuális típusa csak futásidőben dől el Virtuális függvények CPP5 / 21
Példa a használatra const int MAXELEM=100; Location* elemek[maxelem]; int aktelemszam=0; // Uj rajzelem letrehozasa aktelemszam++; elemek[aktelemszam] = new Virtuális függvények CPP5 / 22 Példa a használatra (folyt.) // Osszes elem ujra rajzolasa for (int i=0; i<aktelemszam; i++) elemek[i] -> ; Virtuális függvények CPP5 / 23