Az objektumorientált
programozást úgy lehet felvázolni, mint egy utat a gyors és hatékony
programozáshoz. A cikkben a C++-szon keresztül fogjuk végigvinni az
objektumorientáltság alapjait. A következő témákról fogok beszélni:
Az objektumorientáltság
alapeleme az osztály. Az osztályok létrehozásakor új adattípust hozunk létre,
melyeket ugyanúgy használhatunk, mint más beépített adattípusokat (mint pl. az
int, a char, stb.). Emellett az osztálydefinícióban megadunk váltózókat, és a
feladathoz kapcsolódó függvénykódot is. Az osztály nem más, mint tagváltozók és
a tagfüggvények gyűjteménye.
Mint láthatjuk az osztályok
definiálását a ’class’ kulcsszóval
vezetjük be, majd a ’{}’ operátorok
között adjuk meg az osztályt felépítő kódot, a tagváltozókat (adattagokat)
és a tagfüggvények, melyek az osztályhoz tartoznak. Az osztály definiálásakor
nem inicializálhatjuk az adattagokat (tehát nem adhatunk értéket sem nekik). Az
osztályt ’;’operátorral zárjuk le.
class Szam //az osztály deklarálása
{
int Egy; //a tagváltozó(k) megadása
int GetSzam () //a
tagfüggvény(ek) megadása
{
return Egy;
}
};
Az osztály deklarálásakor a
fordító nem foglal le az osztály számára memóriát, ehhez példányosítanunk kell,
azaz hozzunk létre belőle egy objektumot. Figyeljünk oda, hogy az osztály
deklarációjának mindig meg kell előznie a példányosítást.
Szam ElsoSzam; //egy objekum létrehozása
A létrehozott osztályobjektum
már memóriát foglal, és a beépített változókhoz hasonlóan addig létezik, amíg a
definíció érvényes.
Az osztályoknak van egy olyan
tulajdonsága, hogy megadhatjuk a hozzáférés „jogosultságát” az osztályban
foglalt tagokhoz. Ez azt jelenti, hogy az osztálytagok elé egy
hozzáférés-módosítót kell illesztenünk. Alapértelmezés szerint ez privát
(private/saját). Ahhoz, hogy kívülről is hozzáférjünk (pl. globális
függvényekkel) az osztály tagjaihoz, ezt módosítanunk kell. A
hozzáférés-módosító minden utána következő tagra vonatkozik a definíción
belül, amíg nincs újabb hozzáférés-módosító megadva.
class Szam
{
public:
int
Egy;
int
GetSzam ()
{
return
Egy;
}
};
public – a hozzáférés-módosítóval teljesen nyílttá tehetjük az
őt követő tagokat, tehát a nemtag függvények is hozzáférnek.
protected – a védett hozzáférés-módosítót az öröklődés
folyamán fogjuk tárgyalni
private – a „saját” tagokat csak az osztály tagfüggvényei érhetik
el.
Az objektum adattagjaihoz a
példányon keresztül férünk hozzá a ’.’ operátor
használatával. Egy osztályból akárhány példányt létrehozhatunk, ha azok neve
különböző.
Szam.Egy = 1; //hozzáférés és értékadás
Szam.GetSzam();
Az egységbezárás elvének
megfelelően az osztály belső adatstruktúráit a felhasználónak nem
szabad látnia, ez lehetővé teszi számunkra a tagváltozókba kerülő
értékek ellenőrzését. Optimális esetben az osztály adattagjaihoz csak az
osztály tagfüggvényein keresztül férhetünk hozzá. Ennek megfelelően
módosítjuk a kódot is. Ezáltal ellenőrizhetjük az adattagokba kerülő
értékeket, megelőzve a programozási hibákat.
class Szam
{
private: //hozzáférésmódosító
int
Egy;
public:
//hozzáférésmódosító
int
GetSzam ()
{
return
Egy;
}
void
SetSzam(int E)
{
Egy = E;
}
};
A tagfüggvényeknek van két
különleges típusa: a konstruktor és a destruktor.
A konstruktorok: az osztályokat
inicializáló tagfüggvények. Tehát olyan különleges függvény, ami minden esetben
meghívódik, mikor az osztályból egy példányt létrehozunk. Minden C++ osztálynak
van konstruktora, ha mi nem készítünk saját konstruktort az osztályhoz, akkor a
fordító készít egy alapértelmezett konstruktort, és az adattagoknak
alapértelmezett értéket ad meg (a mi alapértelmezett konstruktorainknak
nincsenek paraméterei, és általában az adattagokat mi is az alapértelmezett
értékekkel töltjük fel.).
Szam ( ) //alapértelmezett konstruktor
{
Egy = 1;
}
A konstruktorban az osztály
inicializálásához szükséges utasításokat adhatjuk ki, feladatokat végezhetjük
el, mint például az értékadás műveletet. A konstruktor neve megegyezik az
osztályéval, és sosincs visszatérési értéke (még void sem).
Paramétere, viszont akármennyi lehet. Ha az osztályhoz készítettünk
konstruktort, akkor a példányosításkor a konstruktor paramétereit is meg kell
adjuk. Egy osztályhoz több konstruktort is készíthetünk (túlterhelt
konstruktoroknak hívjuk őket), viszont fontos, hogy a paramétereik
különbözzenek.
Szam (int E) //konstruktor
{
SetSzam(E);
}
Hogy a fordító ezek közül
melyiket hívja meg, az attól függ, hogy a példányosításkor melyik konstruktor
paraméterlistájával dolgoztunk.
Szam MasodikSzam(1); //példányosítás
konstruktorral
A destruktor az osztályok másik
különleges függvénye. A destruktor akkor kerül meghívásra, amikor az objektumot
megszüntetjük. Ide kerülnek az objektum
megszüntetésével kapcsolatos elvégzendő feladatok. Neve szintén megegyezik
az osztály nevével, viszont a destruktort a ’~’
operátorral vezetjük be. A destruktoroknak ugyanúgy nincs visszatérési értékük,
mint a konstruktoroknak, viszont paraméterei sincsenek.
~Szam ( ) //destruktor
{
}
Megtehetjük, hogy egy osztály
definiálásakor egy meglévő osztályt veszünk fel adattagként (tagobjektum,
vagy beágyazott osztály). Ebben az
esetben a „tartalmazó” osztály konstruktorában inicializálhatjuk a tagobjektumot.
Ha nem tesszük, akkor a tagobjektum alapértelmezett konstruktora kerül
meghívásra.
class Beagyazott
{
Beagyazott
(int B1)
{...}
};
class Tartalmazo
{
Tartalmazo(int T1):Beagyazott (B1)
{...}
};
Az objektumtömböknél a tömb létrehozásakor
minden egyes példánynál a fordító meghívja az alapértelmezett konstruktort.
Szam TombSzam[10]; //objekumtömb létrehozása
Globális objektumoknál a
program elindulásakor hívódik meg a konstruktor (a main
előtt!!!), a destruktor pedig a program végén.
Lokális objektumoknál (pl. egy
függvényen belül) akkor hívódik meg a konstruktor, mikor a vezérlés eléri az
objektum definícióját, a destruktor, pedig amikor a vezérlés elhagyja a
blokkot, amiben a definíció szerepelt.
Lokálisan definiált statikus
objektumoknál (a ’static’ kulcsszóval bevezetett objektumokból
egyetlenegy példány jön létre az egész program futása alatt) a konstruktor
akkor hívódik, mikor a vezérlés eléri a definíciót, a destruktor pedig a
program végén.
Dinamikusan létrehozott
objektumoknál (’new’ operátor) az objektum létrehozásakor
hívódik a konstruktor, a destruktor, pedig mikor az objektumot megsemmisítjük (’delete’ operátor).
Az osztálytag függvények
definícióját az osztályon kívülre is helyezhetjük, ebben az esetben a
függvénydefiníciót meg kell előznie az osztály nevének a ’::’ hatókörfeloldó operátorral együtt.
<visszatérési érték>
<osztálynév>::<függvénynév> (<paraméterlista>)
Az osztályon belül definiált
függvényeket nevezzük inline
függvényeknek. Ezeket a függvényeket híváskor a fordító minden esetben a teljes
függvénykóddal helyettesíti, ezáltal az alkalmazás gyorsabb lesz, viszont a
függvénykód is terjedelmesebb. Javaslatként azokat a függvényeket definiáljuk
az osztályon belül, melyek rövidek, vagy melyeknél szükség van a gyorsaságra.
Egy osztályon kívül definiált függvényt a fordító inline-ként
kezel, ha a visszatérési érték után kitesszük az ’inline’
kulcsszót.
<visszatérési érték> inline
<osztálynév>::<függvénynév> (<paraméterlista>)
Fontos: az osztályhoz tartozó
függvényeket mindenféleképpen deklarálnunk kell az osztályon belül (a
visszatérési érték, a függvénynév és a paraméterlista megadásával).