Öröklés bemutatása Hozzunk létre egy Dolgozó adatbázist C++-ban! Definiáljunk egy osztályt, amely a közös tulajdonságokat tartalmazza. 1
Osztály hierarchia 2
Tulajdonságok Órabéres dolgozó: Ügynök Menedzser Órabér Óraszám Jutalék Típus Menedzser Heti fizetés
Osztály megvalósítása C++-ban class Dolgozo { public: Dolgozo(); Dolgozo( char *nm); char *getName () {return nev;} private: char nev[30]; }; 3
Alaposztály: közös tulajdonságok A Dolgozo csak a nevet tárolja, de tárolhatna több tulajdonságot is. Például: születési idő, cím stb. 4
Származtatott osztály Ezután definiáljunk egy olyan osztályt, amely a dolgozónak egy különleges típusa: órabérben dolgozik. Két lehetőség OraberesDolgozo adattagként tárolja a Dolgozo osztályt Az OraberesDolgozo osztályt a Dolgozo osztályból származtatjuk. 5
A származtatás szintaxisa class OraberesDolgozo : public Dolgozo { public: OraberesDolgozo(char *nm); void setOradij( float ora_d); void setOraszam(float ora_sz); private: float oradij; float oraszam; }; 6
Osztályok típusai Dolgozo: alaposztály (base class) OraberesDolgozo: származtatott osztály ( derived class) 7
Példa: Objektumok definiálása és műveletek OraberesDolgozo aDolgozo (“Kovács István”); char *str; aDolgozo.setOraszam(40.0); //OraberesDolgozo: setOraszam(); aDolgozo.setOradij(8.0); //OraberesDolgozo: setOradij(); str=aDolgozo.getname(); //Dolgozo:getname(); 8
Objektumok helyfoglalása 9
privat adattagok használata A származtatott osztályok tagfüggvényei nem használhatják a bázis osztály privát adattagjait. Például: void OraberesDolgozo: printName() { cout <<”Dolgozó neve” << name << ’\n’; } ehelyett Dolgozo:getName() 10
Oka ha bárki származtat osztályokat és azzal dolgozik, majd a bázisosztály megváltozik, akkor minden származtatott osztályban meg kellene változtatni az adatok kezelését! (Encapsulation!!!) 11
További származtatás class Ugynok : public OraberesDolgozo { public: Ugynok(char *nm); void setJutalek (float j_alap); void setTipus (float j_tipus); private: float jutalek; float tipus; }; 12
Ugynok osztály tulajdonságai Ügynök fizetése: órabéren kívül jutalékot kap, aszerint, hogy milyen típusba van besorolva. Egy Ugynok objektum minden olyan adattagot tartalmaz, amelyet a Dolgozo-ban és az OraberesDolgozo-ban, illetve az Ugynok-ben van definiálva. Egy Ugynok objektumra az összes olyan tagfüggvényt hívhatjuk, amely ebben a három osztályban lett definiálva. 13
Osztályhierarchia OraberesDolgozo alaposztály származtatott osztály is 14
Menedzser származtatása class Menedzser : public Dolgozo { public: Menedzser (char *nm); void setHetiFizetes( float fizetes); private: float heti_fizetes; }; 15
Tagfüggvények újradefiniálása Ezek után definiáljuk az egyes dolgozó típusokhoz a heti fizetések kiszámítását végző függvényeket! float OraberesDolgozo::computePay() { return oraszam * oradij; } 16
float Ugynok::computePay() //ez újradefiniálás { return oraszam * oradij + jutalek * tipus; } ez nem jó, mert private tagok - oraszam*oradij helyett computePay() szintén nem jó, mert rekurzív hívás lenne OraberesDolgozo::computePay() jó 17
Nem újradefiniálás float Menedzser:computePay() //ez nem újradefiniálás, mert a Menedzser osztály nem leszármazottja egyiknek sem { return heti_fizetes; } 18
Származtatott osztályok tagfüggvényei Egy származtatott osztály egy előfordulása tartalmazza az alaposztály és a bázisosztály mindegyik adattagját. Egy származtatott osztály konstruktora meg kell, hogy hívja a bázisosztály konstruktorát. 19
Konstruktorok OraberesDolgozo :: OraberesDolgozo (char *nm): Dolgozo(nm) { oradij = 0.0.; oraszam = 0.0.; }; 20
Konstruktorok Ugynok :: Ugynok (char *nm) : OraberesDolgozo (nm) { jutalek = 0.0; tipus = 0.0; } 21
Konstruktorok Menedzser :: Menedzser (char *nm) : Dolgozo (nm) { heti_fizetes = 0.0; } 22
Konverzió bázis és származtatott osztályok között OraberesDolgozo aDolgozo; Ugynok aUgynok (“Kovács István”); aDolgozo = aUgynok; // jó! aUgynok = aDolgozo; //nem jó! 23
Objektumok és pointereik Dolgozo *DolgPtr; OraberesDolgozo aOraberesDolgozo (“Kiss Lajos”); Ugynok aUgynok (“Kovács István”); Menedzser aFonok (“Nagy Lajos”); DolgPtr = &aDolgozo; //Implicit konverzió DolgPtr = &aUgynok; //Dolgozo-ra DolgPtr = &Fonok; 24
Objektumok és pointereik Ugynok aUgynok (“Kovács István”); Ugynok *UgynokPtr; OraberesDolgozo *OraberesPtr; UgynokPtr = &aUgynok; OraberesPtr = & aUgynok; 25
OraberesPtr -> setOrak(40.0); // OraberesDolgozo:setOrak OraberesPtr -> setOradij(6.0); // OraberesDolgozo:setOradij OraberesPtr -> setTipus (1000.0); //Hiba nincs ilyen tagfüggvény // OraberesDolgozo:setTipus UgynokPtr -> setTipus (1000.0); // Ugynok: setTipus UgynokPtr -> setJutalek (0.05); // Ugynok: setJutalek 26
Származtatott osztályok tagfüggvényei Ha egy olyan tagfüggvényt hívunk meg, amely a bázis és a származtatott osztályban is definiálva van, akkor a pointer típusától függ, hogy melyiket hajtjuk végre. float base, total; base = OraberesPtr -> computePay(); // OraberesDolgozo: computePay(); total = UgynokPtr -> computePay(); // Ugynok: computePay(); 27
Virtuális függvények class Dolgozo kiegészítése egy tagfüggvénnyel virtual float computePay() ; 28
Tagfüggvények Minden osztályt kiegészítünk ezzel a tagfüggvénnyel: float computePay() ; 29
Tagfüggvény definiálása float Dolgozo:: float computePay() { cout << "Nem számít fizetést!\n"; return 0.0. ; } 30
DolgPtr = &aOraberesDolgozo; Mi történik, ha a float computePay() -t egy Dolgozo típusú pointeren keresztül hívjuk meg? DolgPtr = &aOraberesDolgozo; fizetes = DolgPtr -> computePay(); // OraberesDolgozo:computePay() DolgPtr = &aUgynok; fizetes = DolgPtr -> computePay(); // Ugynok:computePay() DolgPtr = &aFonok; fizetes = DolgPtr -> computePay(); // Menedzser:computePay() 31
Virtuális tagfüggvények Ha a computePay() függvény nem lenne virtuális, akkor mindegyik a Dolgozo: computePay()-t hívná meg. 32
Polimorfizmus Az a képesség, hogy egy objektumra vonatkozóan úgy tudunk tagfüggvényeket hívni, hogy az objektum pontos típusát nem ismerjük. 33
Dinamikus kötés Late binding Dynamic binding Fordítási időben nem képes meghatározni, hogy milyen függvényt hívunk. Futási időben rendeli hozzá. 34
Virtuális függvények implementálása Nagyon hatékony. Gyorsan hívhatók (mint a normál függvények) Dinamikus kötés a C++-ban egy tábla (virtuális fv-ek táblája, vagy V-tábla) segítségével. Ez egy tömb, amelyet a compiler hoz létre minden osztály számára. (Függvény pointerek tömbje a virtuális függvények számára). 35
V tábla kezelése Egy osztály minden előfordulása tartalmaz egy pointert a V táblára. Amikor egy DolgPtr->computePay() utasítás végrehajtódik, akkor a DolgPtr által mutatott objektumban levő V-tábla pointer alapján megtalálja azt a fv-t, amelyet hívnia kell. 36
V-tábla 37
V-tábla 38
Absztrakt osztályok Pure virtual függvények virtual float computePay() = 0; Ekkor nem kell definíció! Az az osztály, amely pure virtual függvényt definiál, absztrakt osztály. 39
Öröklődés Objektumok közötti speciális kapcsolat. (Egy osztály a másik általánosításaként, vagy éppen specializált változataként jelenik meg.) Ember, diák, tanár.
Program készítése két módon: IS_A reláció. Program készítése két módon: független osztály kialakítása öröklődéssel történő definíció alaposztály származtatott osztály
Újrafelhasználhatóság Az öröklődéssel történő megoldás előnyei: hasonlóság kiaknázása miatt a program egyszerűbb - osztálykönyvtárak kialakítása, felhasználása (újrafelhasználhatóság).
Öröklődés leírása Ember név, kor Diák +átlag, évfolyam általánosít Ember név, kor az egy az egy Diák +átlag, évfolyam Tanár +fizetés, tárgy specializál
Geometriai alakzatok példája Shape hely, szín, Move() analitikus öröklés Rect +sarok, Draw() Line +vég, Draw() Circle +sugár, Draw() korlátozó öröklés Square
Alakzatok fajtái Általános alakzat: Shape. (szín, hely) Speciális alakzatok: Rect, Line, Circle
Általános alakzat osztálya class Shape { protected: int x, y, col; public: Shape ( int x0, int y0, int col0 ) { x = x0; y = y0; col = col0; } void SetColor ( int c ) { col = c; } };
Line osztály class Line : public Shape { // Line = Shape + ... int xe, ye; public: Line( int x1, int y1, int x2, int y2, int c ) : Shape( x1, y1, c ) { xe = x2, ye = y2; } void Draw( ); void Move (int dx, int dy ) ; };
Line osztály Draw() metódusa void Line :: Draw( ) { // cout << "\nLine: " << x << ',' << y; // cout << " : " << xe << ',' << ye << " ; " << col; _SetColor (col ) ; // rajz a grafikus könyvtárral _MoveTo ( x,y ); _LineTo (xe, ye ) ; }
Line osztály Move() metódusa void Line :: Move( int dx, int dy ) { int cl = col; //tényleges rajzolási szín elmentése col = BGD; //rajzolási szín legyen a háttér színe Draw( ); // A nonal letörlése az eredeti helyről x += dx; y += dy; // mozgatás: a pozíció változik col = cl; // a rajzolási szín a tényleges szín Draw( ); // A vonal felrajzolása az új pozícióra
Rect osztály class Rect : public Shape { // Rect = Shape + ... int xc, yc; public: Rect( int x1, int y1, int x2, int y2, Color c ) : Shape( x1, y1, c ) { xc = x2, yc = y2; } void Draw( ); void Move ( int dx, int dy ) ; };
Rect osztály Draw() metódusa void Rect :: Draw( ) { cout << "\nRect: " << x << ',' << y; cout << " : " << xc << ',' << yc << " ; " << col; }
Származtatással kapcsolatos fogalmak protected hozzáférés-módosító szó class Line :: public Shape {.....} - származtatás, szülő (ős), gyermek (származtatott) public és private jelentése. Származtatott osztályban a tagfüggvényeket újradefiniálhatjuk (ez felülbírálja az alaposztály ugyanilyen nevű tagfüggvényét.
Konstruktorok meghívása Line konstruktorának definíciója: Line( int x1, int y1, int x2, int y2, int c ) : Shape( x1, y1, c ) { xe = x2, ye = y2; } Definíció szerint az alaposztály konstruktora is meghívásra kerül.
Move függvény vizsgálata Vizsgáljuk meg az egyes osztályokban lévő Move függvényt! Ugyanaz, csak mindegyik más Draw() függvényt hív meg. Virtuális tagfüggvény!
Move() elhelyezése az ősosztályban class Shape { protected: int x, y, col; public: Shape( int x0, int y0, int col0 ) { x = x0; y = y0; col = col0; } void SetColor( int c ) { col = c; } void Move ( int dx, int dy ) ; virtual void Draw ( ) { } };
Move() megvalósítása void Shape :: Move( int dx, int dy ) { int cl = col; //tényleges rajzolási szín elmentése col = BGD; //rajzolási szín legyen a háttér színe Draw( ); // A vonal letörlése az eredeti helyről x += dx; y += dy; // mozgatás: a pozíció változik col = cl; // a rajzolási szín a tényleges szín Draw( ); // A vonal felrajzolása az új pozícióra
Line osztály class Line : public Shape { // Line = Shape + ... int xe, ye; public: Line( int x1, int y1, int x2, int y2, int c ) : Shape( x1, y1, c ) { xe = x2, ye = y2; } void Draw( ); };
Rect osztály class Rect : public Shape { // Rect = Shape + ... class Rect : public Shape { // Rect = Shape + ... int xc, yc; public: Rect( int x1, int y1, int x2, int y2, Color c ) : Shape( x1, y1, c ) { xc = x2, yc = y2; } void Draw( ); };
Üzenetek objektumoknak void main ( ) { Rect rect( 1, 10, 2, 40, RED ); Line line( 3, 6, 80, 40, BLUE ); Shape shape( 3, 4, GREEN ); shape.Move( 3, 4 ); // 2 db Draw hivás line.Draw( ); line.Move( 10, 10 ); // 2 db Draw hivás
Metódusok meghívása indirekten Shape * sp[10]; sp[0] = ▭ sp[1] = &line; // nem kell cast for( int i = 0; i < 2; i++ ) sp[i] -> Draw( ); //indirekt Draw() }
Virtuális Shape::Draw Nem virtuális shape.Move() line.Draw() Line::Draw line.Move() sp[0] -> Draw(), mutatótípus Shape * de Line objektumra mutat sp[1] -> Draw(), de Rect objektumra mutat Rect::Draw
Szimuláció C programmal struct Shape { int x, y, col }; //Shape adattagjai void Draw_Shape (struct Shape * this) {} //Shape::Draw
Move() szimulációja void Move_Shape (struct Shape * this, int dx, int dy) { //Shape::Move int cl = this ->col; //tényleges rajzolási szín elmentése this ->col = BGD; //rajzolási szín legyen a háttér színe Draw_Shape( this ); // A vonal letörlése az eredeti helyről this ->x += dx; this ->y += dy; // mozgatás: a pozíció változik this ->col = cl; // a rajzolási szín a tényleges szín Draw_Shape( this ); // A vonal felrajzolása az új pozícióra }
Tisztán virtuális tagfüggvény - absztrakt alaposztály. class Shape { protected: int x, y, col; public: Shape( int x0, int y0, int col0 ) { x = x0; y = y0; col = col0; } void SetColor( int c ) { col = c; } void Move ( int dx, int dy ) ; virtual void Draw ( ) = 0; };
Virtuális függvények Tegyük fel, hogy van egy A alaposztályunk és egy B származtatott osztályunk, amelyben az alaposztály f függvényét újradefiniáltuk. class A { public: void f ( ); // A: :f }; class B : public A { void f ( ); // B: : f
Metódus kiválasztása Az objektum orientált programozás alapelve szerint egy üzenetre lefuttatott metódust a célobjektum típusa és az üzenet neve ( valamint az átadott paraméterek típusa ) alapján kell kiválasztani. Tehát, ha definiálunk egy A típusú a objektumot és egy B típusú b objektumot, és mindkét objektumnak f üzenetet küldünk, akkor azt várnánk el, hogy az a objektum esetében az A :: f, míg a b objektumra a B:: f tagfüggvény aktivizálódik. Vannak egyértelmű esetek, amikor ezt a kívánságunkat a C++ fordító program minden további nélkül teljesíteni tudja:
Objektumok definiálása { A a; B b; a. f ( ) ; // A : : f hívás b. f ( ) ; // B : : f hívás }
Direkt üzenet küldése Ebben a példában az a.f ( ) A típusú objektumnak szól, mert az a objektumot az A a; utasítással definiáltuk. Így a fordítónak nem okoz gondot, hogy ide az A:: f hívást helyettesítse be. A C++ nyelvben azonban vannak olyan lehetőségek is, amikor a fordító program nem tudja meghatározni a célobjektum típusát. Ezek a lehetőségek a részint az indirekt üzenetküldést, részint az objektumok által saját maguknak küldött üzeneteket foglalják magukban. Nézzük először az indirekt üzenetküldést:
Indirekt üzenet küldése { A a ; B b; A * pa ; if ( getchar ( ) = = 'i ' ) pa = & a ; else pa = & b; pa - > f ( ) ; // indirekt üzenetküldés }
Célobjektum meghatározása Az indirekt üzenetküldés célobjektuma, attól függően, hogy a program felhasználója az i billentyűt nyomta-e le, lehet az A típusú a objektum vagy a B típusú b objektum. Ebben az esetben fordítási időben nyilván nem dönthető el a célobjektum típusa. Megoldásként két lehetőség kínálkozik:
Nem virtuális eset Kiindulva abból, hogy a pa mutatót A* típusúnak definiáltuk, jelentse ilyen esetben a pa -> f ( ) az A: : f tagfüggvény meghívását. Ez ugyan téves, ha a pa a b objektumot címzi meg, de ennél többre fordítási időben nincs lehetőségünk.
Virtuális eset 1Bízzuk valamilyen futási időben működő mechanizmusra annak felismerését, hogy pa ténylegesen milyen objektumra mutat, és ennek alapján futási időben válasszunk A : : f és B : : f tagfüggvények közül.
Két lehetőség a C++-ban A C++ nyelv mindkét megoldást felkínálja, melyek közül aszerint választhatunk, hogy az f tagfüggvényt az alaposztályban normál tagfüggvénynek ( 1. lehetőség ), vagy virtuálisnál ( 2. lehetőség ) deklaráltuk.
Önmagának szóló üzenet Hasonló a helyzet az "önmagukban beszélő" objektumok esetében is. Egészítsük ki az A osztályt egy g tagfüggvénnyel, amely meghívja az f tagfüggvényt. class A { public: void f ( ) ; // A : : f void g ( ) { f ( ) ; ) };
B osztályban f átdefiniálása class B : public A { public: void f ( ) ; // B : : f } ;
A::f() vagy B::f() meghívása A B típusú objektum változtatás nélkül örökli a g tagfüggvényt és újra definiálja az f-t. Ha most egy B típusú objektumnak küldenénk g üzenetet, akkor a saját magának, azaz az eredeti g üzenet célobjektumának küldene f üzenetet. Mivel az eredeti üzenet célja B típusú, az lenne természetes, ha ekkor a B::f hívódna meg. A tényleges célobjektum típusának felismerése azonban nyílván nem végezhető el fordítási időben. Tehát vagy lemondunk erről a szolgáltatásról és az f tagfüggvényt normálnak deklarálva a fordító a legkézenfekvőbb megoldást választja, miszerint a g törzsében mindig az A::f tagfüggvényt kell aktivizálni. Vagy pedig egy futási időben működő mechanizmusra bízzuk, hogy a g törzsében ismerje fel az objektum tényleges típusát és a meghívandó f-t ez alapján válassza ki.
Többszörös öröklődés (multiple inheritance) Irodai alkalmazottakat kezelő probléma: Alkalmazottak (Employee) Menedzserek (Manager) Ideiglenes alkalmazottak (Temporary) Ideiglenes menedzserek (Temporary manager)
Osztályok
Employee osztály class Employee { protected: char name[20]; long salary; public: Employee (char *nm, long sl) ( strcpy( name,nm ) ; salary = sl ; } };
Manager osztály class Manager : public Employee { int level ; public: Manager (char *nam, long sal, int lev) : Employee (nam, sal) ( level = lev; } } ;
Temporary osztály class Temporary : public Employee { int emp_time; Temporary (char *nam, long sal, int time ); : Employee (nam, sal ) { emp_time = time} };
Temp_Man osztály class Temp_Man : public Manager, public Temporary { Temp_Man (char *nam, long sal, int lev, int time) : Manager (nam, sal, lev), Temporary (nam, sal, time) {} };
Lehetséges elhelyezésük a memóriában
Nevek ütközése class A{ class B{ protected: protected: int x; int x; }; }; class C: public A, public B { int f() { x=3; x=5;} };
Többértelműség megszüntetése A többértelműség megszüntethető a scope operátor felhasználásával: int f() { { A::x=3; B::x=5;} Azonos nevű adattagok összevonása ellen szól a kompatibilitás elvesztése.
Megoldás: class Manager: virtual public Employee {…}; class Temporary: virtual public Employee {…}; class Temp_Man: virtual public Manager, public Temporary { public: Temp_Man ( char * nam, long sal, int lev, int ti,e) :Employee (nam, sal), MAneger (NULL, 0L, lev), Temporary (NULL, 0L, ti,e) { } };
Memóriában való elhelyezésük
A konstruktor feladatai A virtuális alaposztályok konstruktorainak hívása, akkor is, ha a virtuális alaposztály nem közvetlen ős. A közvetlen, nem virtuális alaposztályok konstruktorainak hívása. A saját rész konstruálása A virtuálisan származtatott osztályok objektumaiban egy mutatót kell beállítani az alaposztály adattagjainak megfelelő részre. ha az objektumosztályban van olyan virtuális függvény, amely itt új értelmet nyer (azaz az osztály a virtuális függvényt újradefiniálja), akkor az annak megfelelő mutatókat a saját megvalósításra kell állítani. A tartalmazott objektumok konstruktorainak meghívása. A konstruktornak a programozó által megadott részei csak a fenti feladatok elvégzése után kerülnek végrehajtásra.
A destruktorok feladatai A destruktor programozó által megadott részének a végrehajtása. A komponensek megszüntetése a destruktoraik hívásával. A közvetlen, nem-virtuális alaposztályok destruktorainak hívása. A virtuális alaposztályok destruktorainak hívása.
Függvénysablonok használata Különböző függvények hasonló feldolgozás különböző adattípusok
Sablonok definíciója A sablonok egy utasításhalmazt definiálnak, amellyel programjaink később további függvényeket hozhatnak létre.
Sablonok használata két vagy több függvény gyors definiálására valók, melyek hasonló utasításokkal, de különböző paraméterekkel, vagy visszaadott értéktípussal dolgoznak.
Sablonok feldolgozása A C++ fordító létrehozza a függvényeket a sablon nevére utaló prototípusokban megadott típusokkal.
max függvény sablonja template<class T> T max ( T a, T b) { template<class T> T max ( T a, T b) { if (a > b ) return (a); else return (b); }
Sablonok tulajdonságai A T a sablon általános típusa. Prototípusokat kell deklarálnunk az egyes típusokhoz: float max(float, float); int max (int, int)
Főprogram: { cout << "A 100 és 200 közül a nagyobb: "<< max (100, 200) << endl; cout << "Az 5.123 és 1.200 közül a nagyobb: " << max (5.123, 1.200) << endl; }
A teljes program #include <iostream.h> template<class T> T max ( T a, T b) { if (a > b ) return (a); else return (b); } float max(float, float); int max (int, int);
A teljes program folytatása void main() { cout << "A 100 ‚s 200 közül a nagyobb: "<< max (100, 200) << endl; cout << "Az 5.123 ‚s 1.2 közül a nagyobb: "<< max (5.123, 1.2) << endl; }
Osztálysablonok használata A template kulcsszó segítségével létrehozhatunk osztálysablonokat, amelyeket az osztálydefiníció használhat adattagok, és tagfüggvények visszaadott értékeinek valamint paraméterértékek típusainak megadására.
tomb osztály létrehozása #include <iostream.h> #include <stdlib.h> class tomb { public: tomb (int meret); long osszeg ( void); int atlag_ertek(void); void tomb_kiiras(void); int ertek_hozzaadas(int); private: int *adatok; int meret; int index; };
Metódusok definiálása tomb:: tomb(int meret) { adatok = new int [meret]; if (adatok == NULL) cerr <<"Keves a memoria -- a program befejezodik" << endl; exit(1); } tomb::meret = meret; tomb::index = 0;
Metódusok definiálása long tomb::osszeg(void) { long osszeg = 0; int i; for (i=0; i<index; i++) osszeg += adatok[i]; return (osszeg); }
Metódusok definiálása int tomb::atlag_ertek(void) { long osszeg = 0; int i; for (i=0; i<index; i++) osszeg += adatok[i]; return (osszeg/index); }
Metódusok definiálása void tomb::tomb_kiiras(void) { int i; for (i=0; i<index; i++) cout<< adatok[i]<<' '; cout<< endl; }
Metódusok definiálása int tomb::ertek_hozzaadas(int ertek) { if (index== meret) return(-1) else adatok[index] = ertek; index++; return(0); }
Főprogram void main(void) { tomb szamok(100); int i; for(i = 0; i<50; i++) szamok.ertek_hozzaadas(); szamok.tomb_kiiras(); cout<<"A szamok osszege: "<<szamok.osszeg() <<endl; cout<<"Az atlagertek: "<<szamok.atlag_ertek() <<endl; }
Az előző feladat sablonnal #include <iostream.h> #include <stdlib.h> template<class T, class T1> class tomb { public: tomb (int meret); T1 osszeg ( void); T atlag_ertek(void); void tomb_kiiras(void); int ertek_hozzaadas(T); private: T *adatok; int meret; int index; };
Metódusok definiálása template<class T,class T1> tomb<T,T1>:: tomb(int meret) { adatok = new T [mret]; if (adatok == NULL) cerr <<"Keves a memoria -- a program befejezodik" << endl; exit(1); } tomb::meret = meret; tomb::index = 0;
Metódusok definiálása template<class T,class T1> T1 tomb<T,T1>::osszeg(void) { T1 osszeg = 0; int i; for (i=0; i<index; i++) osszeg += adatok[i]; return (osszeg); }
Metódusok definiálása template<class T,class T1> T tomb<T,T1>::atlag_ertek(void) { T1 osszeg = 0; int i; for (i=0; i<index; i++) osszeg += adatok[i]; return (osszeg/index); }
Metódusok definiálása template<class T,class T1> void tomb<T,T1>::tomb_kiiras(void) { int i; for (i=0; i<index; i++) cout<< adatok[i]<<' '; cout<< endl; }
Metódusok definiálása template<class T,class T1> int tomb<T,T1>::ertek_hozzaadas(T ertek) { if (index== meret) return(-1) else adatok[index] = ertek; index++; return(0); }
Főprogram void main(void) { tomb<int, long> szamok(100); // 100 elemes tomb tomb<float, float> ertekek(200); // 200 elemes tomb int i; for(i = 0; i<50; i++) szamok.tomb_kiiras(); cout<<"A szamok osszege: "<<szamok.osszeg() <<endl; szamok.ertek_hozzaadas(i); cout<<"Az atlagertek: "<<szamok.atlag_ertek() <<endl;
Főprogram folytatása for(i = 0; i<100; i++) for(i = 0; i<100; i++) ertekek.ertek_hozzaadas( i * 100); ertekek.tomb_kiiras(); cout<<"A szamok osszege: "<<ertekek.osszeg() <<endl; cout<<"Az atlagertek: "<<ertekek.atlag_ertek() <<endl; }
Osztálysablon létrehozásához az osztálydefiníció elé tegyük a template kulcsszót és a típusszimbólumokat, például T és T1. Mindegyik osztályfüggvény definíciója elé ugyanezt a template utasítást kell tennünk. Ezenkívül osztály_ név < T, T1> :: függvény_nev
Objektumok létrehozása osztálysablonnal sablon osztaly_nev< típus1, típus2> objektum_nev ( par1, par2) Például: tomb<char, int> kis_szamok(100)
Student osztály #include <iostream.h> #include <string.h> enum BOOL { FALSE, TRUE } ; class Student { char name[30]; double average; public: Student (char *n =NULL, double a = 0.0 ) { strcpy(name,n); average = a;} double Average() { return average;} void Set (char *n, double a ) { strcpy(name,n);average = a;} char* GetNev(){return name;} double GetAtlag () {return average;} };
StudentListElem osztály class StudentList; class StudentListElem { friend class StudentList; Student data; StudentListElem *next; public: StudentListElem (){} StudentListElem (Student d, StudentListElem *n) { data =d; next = n;} };
StudentList class StudentList { StudentListElem head, *current; int Compare (Student& d1, Student& d2) {return (d1.Average() > d2.Average() ) ; } public: StudentList() { current = &head; head.next =0; } void Insert (Student&); BOOL Get (Student&); };
Metódusok definiálása void StudentList :: Insert (Student& data) { for (StudentListElem* p =&head; p->next !=NULL; p=p->next) if (Compare (p-> data, data) ) break; StudentListElem* old = new StudentListElem ( p->data, p->next); p->data = data; p->next = old; }
Metódusok definiálása BOOL StudentList:: Get (Student& e) { if(current->next == NULL ) { current = &head; return FALSE; } e = current ->data; current = current->next; return TRUE;
Főprogram main() { StudentList list; Student st; char nev[30]; double atlag; char cont; double average = 0; int nstudent = 0;
Főprogram folytatása do { cout << "\nName: "; cin >> nev; cout << "\nAverage: "; cin >> atlag; st.Set(nev,atlag); list.Insert( st ); nstudent++; average += atlag; cout << "\nMore Students? (y/n)"; cin >> cont; } while ( cont == 'y' );
Főprogram folytatása average /= nstudent; while ( list.Get( st ) ) { while ( list.Get( st ) ) { if (st.GetAtlag() >= average) cout << st.GetNev() << " " << st.GetAtlag() << "\n"; }
List osztály template-tel #include <iostream.h> #include <process.h> enum BOOL { FALSE, TRUE }; // GENERIC LIST TYPE template<class R> class List;
ListElem template <class T> class ListElem { friend class List<T>; T data; ListElem * next; public: ListElem( ) { } ListElem( T d, ListElem * n) { data = d; next = n; } };
List template <class R> class List { ListElem<R> head, *read_ptr; public: List( ) { head.next = NULL; read_ptr = &head; } void Insert( R& data ); // insert into the list BOOL Get( R& data ); // get next from the list, ret==IsEND void Select( int index ); // select for the next Get virtual int Compare( R& d1, R& d2 ) { return 1; } };
Metódusok definiálása template <class R> void List<R> :: Insert ( R& data ) { for( ListElem<R> *p = &head; p->next != NULL; p = p->next ) { if ( Compare( p->data, data) == 1 ) break; } ListElem<R> *old = new ListElem<R>( p->data, p->next); p->data = data; p->next = old;
Metódusok definiálása template <class R> BOOL List<R> :: Get ( R& data ) { if ( read_ptr -> next == NULL ) { // end of list read_ptr = &head; return FALSE; } else { // not end, step to next data = read_ptr -> data; read_ptr = read_ptr -> next; return TRUE; }
Metódusok definiálása /*template <class R> void List<R> :: Select ( int index ) { read_ptr = &head; if (index == 0) { if ( read_ptr->next == NULL) cerr << "Invalid Select in List"; }else { for( int i = 0; i < index; i++ ) { read_ptr = read_ptr->next; }
Student template-tel #include "templ11.cpp" struct Student { #include "templ11.cpp" struct Student { char name[20]; int year; double average; };
Főprogram void main() { Student st; List<Student> list; char cont; double average = 0; int nstudent = 0;
Főprogram folytatása do { cout << "\nName: "; cin >> st.name; cout << "\nYear: "; cin >> st.year; cout << "\nAverage: ";cin>> st.average; list.Insert( st ); nstudent++; average += st.average; cout << "\nMore Students? (y/n)"; cin >> cont; } while ( cont == 'y' );
Főprogram folytatása average /= nstudent; while ( list.Get( st ) ) { while ( list.Get( st ) ) { if (st.average >= average) cout << st.name << " " << st.average << "\n"; }