C++ programozási nyelv Struktúrák a C++ nyelvben
Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. szeptember
A C++ programozási nyelv
Soós Sándor
1/37
Bevezetés ● ●
A struktúra, record fogalma Különbségek a C és a C++ között – szintaktika – tagfüggvények – automatikusan lefutó függvények ● konstruktorok ● destruktorok – hozzáférési hatáskörök szabályozása – öröklés
A C++ programozási nyelv
Soós Sándor
2/37
Szintaktikai különbségek ●
A struktúra nevének használata I. – Adott a következő struktúra: struct Valami{ int x; } –
Hogyan definiálunk egy ilyen típusú változót C-ben? 1. A struct Valami típusazonosítóval: void main() { struct Valami v; } 2.Typedef-fel elnevezzük a típust: typedef struct Valami Akarmi; void main() { Akarmi v; }
A C++ programozási nyelv
Soós Sándor
3/37
Szintaktikai különbségek (folyt.) ●
A struktúra nevének használata I. (folyt.) – Hogyan definiálunk egy ilyen típusú változót C-ben? 3. Az 1. és 2. megoldás összevonva:
typedef struct Valami{ int x; } Akarmi; void main() { Akarmi v; }
A C++ programozási nyelv
Soós Sándor
4/37
Szintaktikai különbségek (folyt.) ●
A struktúra nevének használata I. (folyt.) – Hogyan definiálunk egy ilyen típusú változót C++ -ban? ●
Nincs szükség typedef-re, az új név struct nélkül is használható.
struct Valami{ int x; }; void main() { Valami v; }
A C++ programozási nyelv
// ez C-ben hibás lenne
Soós Sándor
5/37
Szintaktikai különbségek (folyt.) ●
A struktúra nevének használata II.: Önhivatkozó struktúrák – C-ben az önhivatkozó struktúrában mindenképpen ki kell tennünk a struct kulcsszót a mutató deklarációjánál. struct Valami{ int x; struct Valami *Kovetkezo; } –
Ez akkor is így van, ha typedef-et alkalmazunk: typedef struct Valami{ int x; struct Valami *Kovetkezo; } Akarmi;
–
C++ -ban nincsen szükség a struct kulcsszó kiírására: struct Valami{ int x; Valami *Kovetkezo; };
A C++ programozási nyelv
Soós Sándor
// C++ -ban ez helyes 6/37
Struktúrák és osztályok ●
Az objektum orientált nyelvek új lehetőségei – C++ ● ●
– –
struktúrák (struct) osztályok (class)
Csak apró eltérések vannak a kettő között A C-vel való kompatibilitás megtartása érdekében maradt meg a struct.
A C++ programozási nyelv
Soós Sándor
7/37
Új fogalom: tagfüggvény (metódus, method) ●
Miről van szó? – Adott a korábbi Valami struktúra: struct Valami{ int x; } –
Írjunk egy függvényt, ami megduplázza az x mező értékét! ●
Hogyan oldanánk meg ezt a feladatot C-ben, illetve C++ -ban?
A C++ programozási nyelv
Soós Sándor
8/37
Tagfüggvény (folyt.) ●
A feladat megoldása C-ben: void ValamiDuplaz(struct Valami* VP) { VP->x *= 2; } void main() { struct Valami v1,v2; v1.x = 3; ValamiDuplaz(&v1); printf("v1.x: %d\n",v1.x); v2.x = 4; ValamiDuplaz(&v2); printf("v2.x: %d\n",v2.x); } A C++ programozási nyelv
Soós Sándor
// v1 x-ét duplázzuk // Kimenet: v1.x: 6 // v2 x-ét duplázzuk // Kimenet: v2.x: 8 9/37
Tagfüggvény (folyt.) ●
C++ -ban van egy másik megoldás is, definiáljunk egy tagfüggvényt: –
A deklaráció: struct Valami{ int x; void Duplaz(); // tagfüggvény };
–
A függvény definíciója: void Valami::Duplaz() { x *= 2; }
A C++ programozási nyelv
Soós Sándor
10/37
Tagfüggvény (folyt.) ●
Hogyan használjuk a tagfüggvényt? void main() { Valami v1,v2; v1.x = 3; v1.Duplaz(); // tagfüggvény hívása printf("v1.x: %d\n",v1.x); // Kimenet: v1.x: 6 v2.x = 4; v2.Duplaz(); // tagfüggvény hívása printf("v2.x: %d\n",v2.x); // Kimenet: v2.x: 8 } – Hasonlítsuk össze a két megoldást: ● Hagyományos függvény: ValamiDuplaz(&v1); ● Tagfüggvény: v1.Duplaz();
A C++ programozási nyelv
Soós Sándor
11/37
Tagfüggvény (folyt.) ●
Hasonlítsuk össze a két megoldást: ● ●
●
Hagyományos függvény: Tagfüggvény:
ValamiDuplaz(&v1); v1.Duplaz();
Tagfüggvény hívása – megegyezik a mezőhivatkozással – pointer esetén is: Valami v1; Valami *vp; vp = &v1; vp->x = 3; vp->Duplaz();
A C++ programozási nyelv
Soós Sándor
12/37
Tagfüggvény (folyt.) ●
Mikor használjunk tagfüggvényt és mikor hagyományos "szabad" függvényt? – Erről szól az objektum-orientált programtervezés – Erről fog szólni ez a tantárgy –
Végső megoldás: Java - Csak tagfüggvény létezik!
A C++ programozási nyelv
Soós Sándor
13/37
Konstruktorok ●
Hogyan inicializálunk egy struktúrát C-ben? void ValamiKonstruktor(struct Valami* VP) { VP->x = 0; }
●
Az inicializáló fv. meghívása: void main() { struct Valami v1; ValamiKonstruktor(&v1); // inicializálás }
A C++ programozási nyelv
Soós Sándor
14/37
Konstruktorok (folyt.) ●
Mi a helyzet dinamikusan allokált struktúra esetén? struct Valami *vp; vp = malloc(sizeof(struct Valami)); ValamiKonstruktor(vp);
●
●
Miért nem biztonságos ez a megoldás – Mi történik, ha elfelejtkezünk az inicializálásról? – Hogyan lehetne elkerülni ezt a veszélyt? A megoldás C++ -ban: – "szabad" függvény helyett legyen tagfüggvény az inicializáló fv. – A szükséges időben hívja meg a rendszer automatikusan. Ez a konstruktor A C++ programozási nyelv
Soós Sándor
15/37
A konstruktor fogalma C++ -ban ●
●
Hogyan ismeri meg a rendszer a konstruktort? – neve megegyezik a struktúra nevével – nincs visszatérő értéke (nem void!!!) Példánkban: struct Valami{ int x; Valami(); // konstruktor deklarációja }; Valami::Valami() // konstruktor definíciója { x = 0; printf("Konstruktor\n"); // demonstráció }
A C++ programozási nyelv
Soós Sándor
16/37
Hogyan működik most a programunk? ●
Ha deklarálunk egy Valami típusú változót, akkor automatikusan lefut a konstruktor: void main() { Valami v; // itt lefut a konstruktor! }
●
A fordítóprogram helyettünk dolgozik. Amikor létrejön egy új struktúra, akkor megnézi, hogy létezik-e konstruktora. Ha igen, akkor annak meghívását automatikusan beleteszi a kódba.
A C++ programozási nyelv
Soós Sándor
17/37
A new operátor ●
●
Mi a helyzet a dinamikusan allokált struktúrák esetén? – malloc függvény helyett new operátor A new operátor: – lefoglalja a memóriát – lefuttatja a konstruktort – visszaadott érték ● ●
–
NULL, ha nem sikerült a memóriafoglalás egyébként egy pointer, ami az új struktúrára mutat
Például: void main() { Valami *vp; vp = new Valami; }
A C++ programozási nyelv
Soós Sándor
18/37
Destruktorok ● ●
●
●
A konstruktor párja A struktúra megszűnésekor elvégzendő tevékenységek – file-ok lezárása – memória felszabadítás – hálózati kapcsolatok lezárása C-ben: – írhatunk egy függvényt – a struktúra felszámolása előtt meghívjuk ezt a függvényt – ugyanazok a veszélyek fennállnak, mint az inicializáló függvény esetében C++ – destruktor
A C++ programozási nyelv
Soós Sándor
19/37
A destruktor fogalma C++ -ban ●
Hogyan ismeri meg a rendszer a destruktort? – neve: ~ jel + a struktúra neve (~: jobb oldali Alt + 1) – nincs visszatérő értéke (nem void!!!)
A C++ programozási nyelv
Soós Sándor
20/37
A destruktor fogalma C++ -ban (folyt.) ●
Példa: struct Valami{ int x; Valami(); ~Valami(); };
// konstruktor deklarációja // destruktor deklarációja
Valami::Valami() // konstruktor definíciója { x = 0; printf("Konstruktor\n"); // demonstráció } Valami::~Valami() { printf("Destruktor\n"); } A C++ programozási nyelv
Soós Sándor
// destruktor definíciója // demonstráció 21/37
A destruktor fogalma C++ -ban (folyt.) ●
A főprogram: void main() { Valami v; }
●
A program kimenete a következő lesz: Konstruktor Destruktor
A C++ programozási nyelv
Soós Sándor
22/37
A delete operátor ● ●
●
●
A new operátor párja C-ben a free függvénnyel szabadítjuk fel a dinamikusan foglalt tárterületet C++ -ban a delete operátorral szabadítjuk fel a new operátorral létrehozott objektumokat delete operátor – meghívja a destruktort – felszabadítja az objektum által foglalt tárterületet
A C++ programozási nyelv
Soós Sándor
23/37
new és delete operátor példaprogram void main() { Valami *vp; printf("new előtt\n"); vp = new Valami; // meghívja a konstruktort ... delete vp; // meghívja a destruktort printf("delete után\n"); } Az eredmény: new előtt Konstruktor Destruktor delete után A C++ programozási nyelv
Soós Sándor
24/37
Hozzáférési hatáskörök szabályozása ●
●
●
●
Adatelrejtés: minden adattagról és tagfüggvényről eldönthetjük, hogy ki férhet hozzá. private – csak az osztály tagfüggvényei férhetnek hozzá – class esetében ez az alapértelmezés public – minden külső függvény hozzáférhet az adott taghoz – struct esetében ez az alapértelmezés protected – később, az öröklődéskor fogunk róla beszélni – mint a private, de a leszármazottak tagfüggvényei hozzáférhetnek
A C++ programozási nyelv
Soós Sándor
25/37
A private hozzáférés ●
Vegyük a korábbi példánkat: struct Valami{ private: int x; void f1(); };
●
void main() { Valami v; v.x = 7; // ez hiba! } De: void Valami::f1() { x = 7; // ez rendben van, mert f1 tagfüggvény! }
A C++ programozási nyelv
Soós Sándor
26/37
A private hozzáférés (folyt.) ●
●
Nem csak adattag lehet private, hanem tagfüggvény is: struct Valami{ public: void PublikusFv(); // public függvény private: void PrivatFv(); // private függvény }; void Valami::PrivatFv() { printf("PrivatFv"); } void Valami::PublikusFv() { PrivatFv(); } // Ez szabályos Ekkor: void main() { Valami v; v.PublikusFv(); // ez rendben van! v.PrivatFv(); // ez hiba, private fv. itt nem látszik }
A C++ programozási nyelv
Soós Sándor
27/37
A private hozzáférés (folyt.) ●
●
Hogyan férhetünk hozzá a private adattagokhoz? – Az osztály tagfüggvényeiből közvetlenül – A külvilág számára írhatunk olyan public tagfüggvényeket, amelyek szabályozott módon engednek hozzáférést a private adattagokhoz. Példa a következő dián
A C++ programozási nyelv
Soós Sándor
28/37
A private hozzáférés (folyt.) struct Valami{ int GetX(); // az x-et kiolvasó függvény void SetX(int ax); // az x-et beállító függvény private: int x; }; int Valami::GetX() { return x; } void Valami::SetX(int ax) { x = ax; } // ez engedélyezett void main() { Valami v; int a; v.SetX( 10 ); a = v.GetX(); }
A C++ programozási nyelv
Soós Sándor
29/37
Mire jó ez az egész? Miért korlátozza magát a programozó a private tagokkal? ●
1. példa: Készítsünk egy listát egész számok tárolására! Ehhez definiáljuk a következő struktúrát: struct Lista{ int Tomb[100]; int N_Elemek; }
●
Miért veszélyes ez? – A struktúrát használó programozónak tisztában kell lennie a használat módjával: ● ●
nem tehet 100-nál több elemet a listába megfelelően léptetni kell az N_Elemek mezőt, stb.
Ha úgy döntünk, hogy megváltoztatjuk a használt adatszerkezetet, akkor a teljes programot módosítani kell. A megoldás: – Rejtsük el a külvilág elől az adatstruktúra belsejét (private). –
●
A C++ programozási nyelv
Soós Sándor
30/37
Mire jó ez az egész? Miért korlátozza magát a programozó a private tagokkal? ●
●
2. példa: Nem a teljes adatszerkezetet változtatjuk, csak az adattípust. Egy üzlet adatait kezeljük egy programmal. Tegyük fel, hogy először forintban tartjuk nyilván az árakat. Nem kezelünk filléreket, ezért az áru árát egész típusú változóban tároljuk. Később áttérünk eurora, ezért szükség van a centek tárolására is. Ehhez módosítanunk kell a belső adattípust, de például a havi és éves forgalmi adatokat visszaadó függvényeket nem kell módosítani, azok maradhatnak egész típusúak.
A C++ programozási nyelv
Soós Sándor
31/37
Mire jó ez az egész? Miért korlátozza magát a programozó a private tagokkal? ●
●
3. példa: Bizonyos esetekben csak együtt változhat meg két vagy több adatmező, vagy csak több mező felhasználásával lehet visszaadni egy értéket. Az előző üzleti példában egy áru eladását be kell jegyezni az eladások listájába, ugyanakkor csökkenteni kell az árukészletet is. Ha ezeket külön-külön kell elvégeznie a programozónak, akkor fennáll a veszélye, hogy nem lesz szinkronban a két adatbázis. Ennek elkerülésére írhatunk egy Eladás tagfüggvényt, ami elvégzi mindkét műveletet.
A C++ programozási nyelv
Soós Sándor
32/37
Mire jó ez az egész? Miért korlátozza magát a programozó a private tagokkal? ●
●
4. példa: Vannak esetek, amikor csak bizonyos szabályok betartásával szabad beállítani egy mező értékét. Vannak olyan azonosító kódok, amelyek nem lehetnek tetszőlegesek. Ilyenek például a TAJ szám, vagy a hajdan volt személyi szám. Ezek egy ellenőrző algoritmust tartalmaznak, amivel egy adott számról eldönthető, hogy helyes-e. Ha ezt a mezőt private-tá tesszük és csak egy tagfüggvénnyel engedünk hozzáférést, akkor biztosíthatjuk, hogy mindig csak hibátlan adatok kerülhessenek az adatbázisba.
A C++ programozási nyelv
Soós Sándor
33/37
Az információrejtés elve adatbezárás, encapsulation ●
●
●
● ● ●
A tényleges, fizikai tárolást végző adatszerkezetet elrejtjük a struktúra belsejébe (private adattagok) Publikus tagfüggvényként azokat a műveleteket deklaráljuk, amelyek megvalósítják a struktúra szolgáltatásait. Például új elem hozzáadása, törlés stb. Ily módon a struktúra egy szolgáltatóvá válik, amitől szolgáltatásokat lehet kérni, de hogy azt hogyan hajtja végre, azt nem kell tudnia a felhasználónak. Természetesen mindez alapos tervezést, kísérletezést igényel! Az objektum-orientált tervezést is meg kell tanulni! Ha azonban egyszer megtanultuk ezt a gondolkodás módot, akkor el sem tudjuk képzelni, hogy eddig hogyan gondolkoztunk és programoztunk másképp.
A C++ programozási nyelv
Soós Sándor
34/37
Öröklődés, örököltetés ● ●
●
●
●
Ezzel a témakörrel külön előadásokon fogunk foglalkozni. Egyelőre annyit jegyezzünk meg róla, hogy ez az objektumorientált nyelvek talán legfontosabb újítása a "hagyományos" nyelvekhez képest. Röviden arról van szó, hogy egy struktúra, illetve osztály definiálásakor felhasználhatjuk egy már meglévő osztály tulajdonságait. Az új, ún. leszármazott osztály örökli az ősosztály tulajdonságait (adatmezőit és tagfüggvényeit). Ráadásul ezt anélkül tudjuk megtenni, hogy hozzáférnénk az eredeti osztály forráskódjához. A leszármazott osztály az örökölt tulajdonságokat módosíthatja és újakat is hozzájuk tehet.
A C++ programozási nyelv
Soós Sándor
35/37
Hogyan tovább? ●
●
Mire van szükség az objektum-orientált programozás elsajátításához? – a nyelvi eszközök megtanulására – az eszközök célszerű használatának megtanulására A nyelvvel kapcsolatban megtanulandó témakörök: – A C++, illetve az objektum-orientált írásmód megszokása – A program működésének megértése ●
●
●
– –
összetett struktúrák esetében milyen sorrendben futnak le a konstruktorok, destruktorok mikor generál a fordítóprogram önállóan egy függvényt (konstruktort, másoló operátort) mi történik egy kivétel kiváltásakor, stb.
A hozzáférési jogosultságok alapos megértése, különösen az örökléssel kapcsolatban A C++ esetében különösen fontos feladat a dinamikus memóriakezelés megértése!
A C++ programozási nyelv
Soós Sándor
36/37
Mi a megoldás! ●
●
●
●
Folytonosan legyünk tisztában azzal, hogy kétféle területen kell előrehaladnunk: a nyelv megismerésében, illetve a programtervezés módszereinek elsajátításában. Egy adott feladatra szinte mindig érdemes többféle megoldást kipróbálni, pl. – először szabad függvénnyel, aztán tagfüggvénnyel valósítjuk meg az adott funkciót; – egyszer örököltetést, egyszer kompozíciót (struktúrák egymásba ágyazását) használunk, stb. Legjobban összehasonlításokon keresztül ismerhetők meg az eszközök! A hozzáférési jogosultságokat érdemes először ’lazán’ (public) hagyni, majd amikor egy-egy programrész már kialakult, lépésenként ’elrejteni’ a tagokat. Ha már a kezdet kezdetén sok private tagot definiálunk, nagyon nehézzé, áttekinthetetlenné válnak az első kísérletező lépések. Induljunk ki egy olyan (pl. kevésbé objektum-orientált) megoldásból, amelyet eddigi tapasztalataink alapján meg tudunk írni és próbáljuk meg lépésenként átalakítani!
●
…
●
Ne keseredjünk el (nagyon), ha nehéznek érezzük ezt a témát. Tényleg az. A C++ programozási nyelv
Soós Sándor
37/37