3. Osztályok II. Programozás II
Bevezető feladat Írj egy Nevsor osztályt, amely legfeljebb adott mennyiségű nevet képes eltárolni. A maximálisan tárolható nevek számát a konstruktorban adjuk meg. Az osztályt el kell látni függvényekkel, hogy a listához új neveket adhassunk, illetve lekérdezhessük azokat. 2
Bevezető feladat add: A paraméterében megadott stringet hozzáadja a névsorhoz, ha ez már nem lehetséges, akkor hamis értékkel tér vissza, egyébként igazzal nevekszama: Visszaadja az eddig eltárolt neveket nev: Visszaadja a paraméterben megadott indexü nevet (az index 0-ról indul) 3
Megoldás class Nevsor { string * nevek; int kapacitas; int elemekszama; public: Nevsor(int mennyi) { nevek = new string[mennyi]; kapacitas = mennyi; elemekszama = 0; 4
Megoldás bool add(const string & ujnev) { if (elemekszama < kapacitas) { nevek[elemekszama] = ujnev; elemekszama++; return true; return false; int nevekszama() const { return elemekszama; const string & nev(int index) const { return nevek[index]; ; 5
Megoldás int main() { Nevsor avengers(5); avengers.add("hulk"); avengers.add("iron Man"); cout << avengers.nevekszama() << endl; int index; for (index = 0; index < avengers.nevekszama(); index++) { cout << avengers.nev(index) << endl; return 0; 6
Sor 0 Hulk 1 Iron Man 2 3 4 elemekszama = 2 kapacitas = 5 7
Felszabadítás A Nevsor osztály tartalmaz egy mutatót egy dinamikusan lefoglalt tömbre A tömböt fel kell szabadítani A program vége előtt hívjunk meg egy clear függvényt? avengers.clear(); return 0; 8
Felszabadítás A Nevsor osztály tartalmaz egy mutatót egy dinamikusan lefoglalt tömbre A tömböt fel kell szabadítani A program vége előtt hívjunk meg egy clear függvényt? avengers.clear(); return 0; 9
Destruktor A konstruktor ellentéte, akkor hívódik meg, mikor az objektum megszűnik Felszabadításra használjuk Szintaktika: ~ jel után az osztály neve Csak egy destruktor van, nincsen paramétere Nincsen visszatérési érték típus a függvény neve előtt 10
class Nevsor { public: Destruktor string * nevek; int kapacitas; int elemekszama; ~Nevsor() { Hullám vonal delete [] nevek; Nincsen visszatérési érték típus Az osztály neve 11
Destruktor Fontos! Ha tömböt szabadítunk fel, akkor a delete után kötelező a [] Ennek hatására a tömbben lévő minden objektumra meghívódik a destruktor Ha elhagyjuk a []-t akkor csak a tömb első elemére hívódik meg a destruktor 12
Mi történik? void foo(nevsor lista) { lista.add("deadpool"); int main() { Nevsor avengers(3); avengers.add("hulk"); foo(avengers); avengers.add("iron Man"); 13
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor 14
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers kapacitas =? elemekszama =? nevek =? class Nevsor { public: Nevsor(int mennyi) { nevek = new string [mennyi]; kapacitas = mennyi; elemekszama = 0; 15
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers kapacitas =? elemekszama =? nevek class Nevsor { public: Nevsor(int mennyi) { nevek = new string [mennyi]; kapacitas = mennyi; elemekszama = 0; 0 1 2 16
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama =? nevek class Nevsor { public: Nevsor(int mennyi) { nevek = new string [mennyi]; kapacitas = mennyi; elemekszama = 0; 0 1 2 17
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 0 nevek class Nevsor { public: Nevsor(int mennyi) { nevek = new string [mennyi]; kapacitas = mennyi; elemekszama = 0; 0 1 2 18
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 0 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 1 2 19
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 0 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man 1 2 20
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man 1 2 21
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek 0 1 2 Iron Man 22
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek 1 2 23
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek 1 Deadpool 2 24
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man Nevsor avengers elemekszama = 1 nevek lista elemekszama = 2 nevek 1 Deadpool 2 25
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama = 2 nevek class Nevsor { public: ~Nevsor() { delete [] nevek; 0 Iron Man 1 Deadpool 2 26
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man 1 Deadpool 2 27
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man 1 Deadpool 2 28
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 29
Probléma A másolatban lévő pointer ugyanarra a tömbre mutatott, mint az eredeti objektum pointere 2 probléma: A másolat objektum felszabadította a tömböt Ha másolatként adunk át valamit, akkor azt várjuk, hogy a másolat ne legyen kapcsolatban az eredeti objektummal 30
Probléma Az objektumhoz fizikailag nem tartozik hozzá a tömb, csak a rá mutató pointer A másolat létrehozásakor alap esetben csupán az objektum tagváltozói másolódnak le Mi azonban tudjuk, hogy magát a tömböt is le kell másolni 31
Másoló konstruktor Nevsor(const Nevsor & masik) { kapacitas = masik.kapacitas; elemekszama = masik.elemekszama; nevek = new string[kapacitas]; int i; for (i = 0; i < elemekszama; i++) { nevek[i] = masik.nevek[i]; 32
Másoló konstruktor A paraméter a lemásolandó objektum referenciája Akkor hívódik meg, mikor másolat jön létre az objektumról, például érték szerinti paraméterátadáskor A referencia kötelező, különben a másoló konstruktor hívásakor végtelen ciklus alakulna ki 33
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista kapacitas =? elemekszama =? nevek =? Nevsor(const Nevsor & masik) { kapacitas = masik.kapacitas; elemekszama = masik.elemekszama; nevek = new string[kapacitas]; int index; for (index = 0; index < elemekszama; index++) { nevek[index] = masik.nevek[index]; 0 Iron Man 1 2 34
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama =? nevek =? Nevsor(const Nevsor & masik) { kapacitas = masik.kapacitas; elemekszama = masik.elemekszama; nevek = new string[kapacitas]; int index; for (index = 0; index < elemekszama; index++) { nevek[index] = masik.nevek[index]; 0 Iron Man 1 2 35
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek =? Nevsor(const Nevsor & masik) { kapacitas = masik.kapacitas; elemekszama = masik.elemekszama; nevek = new string[kapacitas]; int index; for (index = 0; index < elemekszama; index++) { nevek[index] = masik.nevek[index]; 0 Iron Man 1 2 36
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek Nevsor(const Nevsor & masik) { kapacitas = masik.kapacitas; elemekszama = masik.elemekszama; nevek = new string[kapacitas]; int index; for (index = 0; index < elemekszama; index++) { nevek[index] = masik.nevek[index]; 0 Iron Man 1 2 0 1 2 37
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek Nevsor(const Nevsor & masik) { kapacitas = masik.kapacitas; elemekszama = masik.elemekszama; nevek = new string[kapacitas]; int index; for (index = 0; index < elemekszama; index++) { nevek[index] = masik.nevek[index]; 0 Iron Man 0 Iron Man 1 2 1 2 38
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man 0 Iron Man Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek 1 2 1 Deadpool 2 39
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek lista elemekszama = 1 nevek class Nevsor { public: ~Nevsor() { delete [] nevek; 0 Iron Man 0 Iron Man 1 2 1 2 Deadpool 40
Működés void foo(nevsor lista) { lista.add("deadpool"); [ ] Nevsor avengers(3); avengers.add("iron Man"); foo(avengers); avengers.add("hulk"); Nevsor avengers elemekszama = 1 nevek class Nevsor { public: bool add(const string & ujnev) { [ ] nevek[elemekszama] = ujnev; elemekszama++; 0 Iron Man 1 Hulk 2 41
Megjegyzés A másoló konstruktor (mint minden konstruktor) akkor hívódik meg, ha a létrejövő objektum egy másik másolata lesz Point2D a, b; Másoló konstruktor Point2D c = a; b = a; b már létezik, tehát nem a másoló konstruktor hívódik meg, hanem valami más 42
Több forráskódból álló programok main.cpp point2d.cpp line.cpp 41
Több forráskódból álló programok main.cpp point2d.cpp line.cpp Fordító main.o gépikód Point2D a; a.setx(21); Hova ugorjon a program a setx függvény meghívásakor? 42
Több forráskódból álló programok main.cpp point2d.cpp line.cpp Fordító main.o gépikód Point2D a; a.setx(21); Hova ugorjon a program a setx függvény meghívásakor? point2d.o setx gépi kódja line.o gépi kód 43
Több forráskódból álló programok main.o gépi kód Point2D a; a.setx ->? point2d.o setx gépi kódja line.o gépi kód Linker Az elkészült program a.setx -> setx gépi kódja 44
Több forrásfájlból álló programok Fontos: A forrásfájlokat egymástól függetlenül kell lefordítani Probléma: Ha a main.cpp forrásfájlban hivatkozunk a Point2D osztályra, amely a point2d.cpp-ben van, akkor a main.cpp nem fordítható le, mivel annak fordításakor a fordító nem ismeri a Point2D osztályt. 47
Több forrásfájlból álló programok Megoldás: Helyezzük el a main.cpp-ben a Point2D osztály deklarációját: class Point2D { double x, y; string name; public: const string & getname() const; Point2D(const string & nev); double length() const; void setx(double x); void sety(double y); double getx() const; double gety() const; ; 48
Több forrásfájlból álló programok Ennek a deklarációnak egyeznie kell azzal, amit a point2d.cpp-ben megvalósítunk. Ötlet: Helyezzük el ezt a deklarációt egy külön header fájlban, a point2d.h-ban! A point2d.h-t include-oljuk minden fájlban, ahol a Point2D osztályra hivatkozunk (például main.cpp, point2d.cpp) 49
Több forrásfájlból álló programok point2d.h tartalma: #ifndef POINT2D_H_ #define POINT2D_H_ class Point2D { [ ] ; #endif header guard, hogy ne okozzon gondot, ha ezt a fájlt több fájlba is include-oljuk 50
Több forrásfájlból álló programok point2d.cpp-ben pedig elhelyezzük az osztály tagfüggvényeinek a definícióit: #include point2d.h Point2D::Point2D(const string & nev): name(nev){ x = 0; y = 0; double Point2D::length() { return sqrt(x * x + y * y); Jelezzük, hogy melyik osztály függvényeiről van szó 51
Feladat Írj egy Halmaz osztályt, amely egész számokat tárol 0-tól N-1-ig. Az osztály konstruktora megkapja paraméterben az N értékét, és üresre inicializálja az objektumot. Írd meg a konstruktort, destruktort, másoló konstruktort. A Halmaz osztály számára készíts külön header és forrás fájlt! 52
Feladat A Halmaz osztály függvényei: hozzaad(int) : Hozzáadja a halmazhoz a paraméterben megadott értéket, ha az kisebb, mint N, és nem negatív, egyébként nem csinál semmit kivon(int) : Az előző függvény ellentéte, eltávolítja a paraméterben megadott értéket mennyi: Megadja, hogy mennyi számot tárolunk jelenleg a halmazban 53
Házi feladat Az eddigi házi feladatokban megírt forráskódot bontsd szét több forrásfájlra. Az osztályoknak legyen 1-1 külön header és forrás fájljuk. Mostantól minden osztályt így kell megírni a házi feladatokban 54
Házi feladat Módosítsd a Lovag osztályt úgy, hogy a benne lévő 10 elemű tömb helyett egy dinamikusan növekvő tömbbel helyettesítsd A tömb kezdetben 0 elemet tartalmaz Minden új tárgy hozzáadásakor megnöveljük a tömb méretét 1-el 55
Házi feladat Tipp: A tömböt minden bővítéskor foglaljuk újra, majd a régi tömb tartalmát másoljuk az újba, a régit pedig szabadítsuk fel A hozzáadó függvénynek nem kell logikai értékkel visszatérnie Gondoskodj a tömb megfelelő felszabadításáról, és biztosítsd a Lovag helyes lemásolását is 56
Házi feladat Írj egy Menu osztályt, amely egy menüt kezel Az osztály menüpontokat tárol, a menüpont egy megjelenítendő feliratból, és egy rövid, egyedi string azonosítóból áll A menüpontokat szintén dinamikusan növekvő tömbben tároljuk Példa: A lovag adatai listázás menüpontjának felirata: Lovag adatai, azonosítója: adatok 57
Házi feladat A Menu objektumok kezdetben egy menüpontot se tárolnak Írj függvényt, amellyel új menüpontot adhatunk a menühöz, a függvénynek meg kell adni a feliratot és az azonosítót A függvény térjen vissza igaz értékkel, ha a megadott azonosítóval még nem létezik menüpont, és ekkor tárolja is el az új menüpontot, egyébként hamissal térjen vissza, és ne bővítse a menüpont listát 58
Házi feladat Írj függvényt a Menu osztályhoz, amely kiírja a menüpontokat a képernyőre A függvény egy sorba írja a menüpontot, majd utána az azonosítóját, például: Lovag adatainak listázása : adatok Új tárgy hozzáadása : targyad Kilépés : kilep 59
Házi feladat Írj függvényt a Menu osztályhoz, amelynek átadhatunk egy stringet. Ha a megadott string (menü azonosító), már egy létező azonosító, akkor a függvény hamis, ellenkező esetben pedig igaz értékkel térjen vissza, és csak ez esetben adja hozzá a menüpontot a listához Gondoskodj a megfelelő felszabadításról, és a helyes másoló konstruktorról 60
Házi feladat Az eddigi statikus menüt cseréld ki az új Menu osztállyal A program hozzon létre egy objektumot a Menu osztályból A program először töltse fel a Menu objektumot menüpontokkal Az objektum hozzáadó függvény hamis visszatérési értéke esetén a program hibaüzenettel lépjen ki 61
Házi feladat A menü kirajzolását a létrehozott objektum végezze el A programban most már ne számokkal, hanem az azonosítók beírásával lehessen menüt választani Ha nem létező azonosítót írunk be, akkor adjon hibaüzenetet a program (az ellenőrzéshez használjuk a menü objektumot) 62
Házi feladat Fontos: A Menu osztály csupán abban segít, hogy a menüt kiírjuk, azt alkalomadtán bővíteni tudjuk, illetve ellenőrizhessük a segítségével, hogy helyes menüpontot választottunk-e A Menu osztályhoz nem tartoznak hozzá az egyes menüpontokhoz tartozó funkciók, azokat továbbra is a fő forrásfájlban kell megvalósítani 63