C++ Sablonok |
Tegyük fel, hogy írnunk kell egy programot, amely tetszőleges típusú
elemet tárol illetve rendez. Ez a feladat, ha a szimpla C szemszögéből
közelítjük meg, akkor szinte megvalósíthatatlannak tűnik. Lenne egy sor void* mutatónk, illetve egy más sornyi
azonosítónk, mely az elemek típusát tárolja, és még sornyi függvényünk, mely
különböző adattípusok összehasonlítását végzi el. Gigászi feladat. A C++ erre a
feladatra vezette be a sablonokat. Érdekesség, hogy C++ legelső kiadása
nem tartalmazta ezt a nagyon hasznos tulajdonságot, viszont a programozók
visszajelzéseiből alapján ez hasznosnak ítéltetett, és beiktaták az
elkövetkező kiadásokba. A C++ sablonok lehetőséget nyújtanak, hogy
egy absztrakt paraméterezési módszerrel (illetve változó típusdeklarálási
módszerrel) tetszőleges típusú függvényeket illetve osztályokat hozzunk
létre. A sablonok
nagyon hasznosak egy programozó életében, hisz egy (helyesen) megírt sablon
bármilyen felhasználó által definiált, illetve alap típusra működni fog.
Tekintsük példaként, hogy megírtunk egy CArray sablont, mely adatokat tárol illetve
rendez. Ennek a sablonnak az alapján létrehozhatunk egy int tömböt, egy float tömböt, és amennyibe megoldjuk két CEmployee osztály összehasonlítását, egy CEmployee tömböt. |
Függvény sablonok |
Egy függvény sablon egy végtelen elemű felüldefiniált
függvényhalmazt hoz létre. (Hogy mi is az a felüldefiniált függvény? Amikor
definiálunk egy függvényt, egy bizonyos parametrizálással, majd ezután
ugyanazzal a névvel definiálunk egy másik függvény, melynek a parametrizálása
különböző. Ez egy nagyon hasznos technológia, hogy egyszerűbben
megérthető kódot tudjunk írni.) Egy függvénysablont a
következőképpen definiálunk: template<class T, class T1, ...> return_type function (T par1, T
par2, ... ) ahol a template illetve a class egy egy C++ kulcsszó, a class T (illetve az összes többi) az absztrakt
adattípusokat képviselik (típus változó a rendes neve), a return_type tetszőleges adattípus, lehet
nyugodtan a definiált T (a class nélkül) a function a függvény neve, T par1, illetve az összes többi paraméter,
melyeket én három ponttal jelöltem, a függvény paraméterei. Vegyük például
a min függvényt, amely
mint tudjuk, két elem minimumát kell visszatérítse: template <class T> T min(T a, T b) { return
a<b?a:b; } A függvények
esetében a sablon paraméterek típusát nem definiáljuk explicit módon (vagyis
nem mondjuk meg, hogy most használjuk azt a min függvényt, mely int típusú paramétereket vár el). A C++
automatikusan létrehozza a használt típusokból a sablon alapján a
megfelelő függvényt. Tekintsük például: int i, j, k; double u, v, w; char a, b, c; ... k = min(i,j); v = min(u,v); c = min(a,b); Ezen esetben
három függvény lesz generálva, mégpedig: int min(int, int); double min(double, double); char min(char, char); Amennyiben egy
osztálynak definiáljuk a <
operátort, a min függvényünk osztálytípusú változókra is működni fog.
Viszont a következő függvényhívás hibát generál: k = min(i,u); Hogy miért, azt
könnyen ki lehet találni: nem definiáltunk olyan sablont, mely két absztrakt
típust képes kezelni, és ezek közül az egyiket visszatéríti. A C++ fordító
függvénysablon esetén nem végez automatikus adatkonverziót, viszont bármely
sablonfüggvényt felül tudjuk definiálni. |
Osztálysablonok |
Osztálysablonokat a következőképpen lehet definiálni: template<osztálysablon paraméterek> class osztálynév { }; Az
osztálysablon paraméterek egyrészt a fent említett class T típusúak lehetnek, másrészt
bevezethetünk alaptípusokat is, amelyek használata nagyban megkönnyíti
munkánkat. Tekintsük például a következő osztályt: template<class T, int
size> class CArray { public: CArray(T defval); ~CArray(); T& operator
[] (int idx); private: T els[size]; }; Mint látjuk, ez
az osztály nemcsak egy típust vár el, hanem egy int típusú változót is,
amelynek megadása kötelező. Általában ezt a típusú definíciót akkor
használjuk, amikor statikusan van szükségünk size (a mi esetünkbe) adatra, jelen esetben
látjuk a size elemű vektort
deklarálva. Nézzük tovább a
Carray osztály konstruktorának illetve
destrukotrának a deklarálását: template<class T, int size> CArray<T,size>::CArray(T
defval = 0) { for(int i=0;i<size;i++) { els[i]=defval; } } template<class T, int
size> CArray<T,size>::~CArray()
{ //do cleanup code here } Mint
kitűnik, elsősorban meg kell adnunk a sablon típusát, majd
megmondjuk a fordítónak, hogy ez a konstruktor illetve destruktor abba a
sabloncsaládba tartozik, amelynek típusparaméterei a T illetve a size. Ezek után jöhet magának a konstruktor
illetve destruktornak a definiálása. Mivel ezek különleges függvények, nincs
visszatérési értékük. Nézzük viszont az operator függvény deklarálását: template<class T, int
size> T&
CArray<T,size>::operator [](int idx) { return
els[idx]; } Látjuk hogy a
visszatérési érték a sablon típus után, és az osztályazonosító elé került.
Normál függvényeknél is így járunk el. Amikor sablon
osztályból származtatunk, a következőképpen definiáljuk a származtatott
osztályt: template<int size,int
defval>class CIntArray:public CArray<int,size>
{ public: CIntArray(); }; Ezzel
létrehoztunk egy CIntArray osztályt, melyet a CArray<int,size> osztályból származtattunk. Az osztály
konstruktor függvénye a következőképpen lesz deklarálva: template<int size,int
defval> CIntArray<size,defval>::CIntArray():CArray<int,size>(defval) { // init sutff } Egy sablon
alapján objektumokat a következőképpen hozunk létre: CIntArray<10,1> array1; Ezzel
létrehoztunk egy olyan objektumot, mely tíz darab int-tet tároló tömb lesz, amely elemeinek
alapértelmezett értéke 1. Általában a
mindennapi munkához elég ez a mélység, amire most behatoltunk a sablonok
világába, de nem árt azért elgondolkodni a következő pár definíción: template<class T, class T2 = T > class
Test { }; létrehoz egy
olyan sablont, amely két típusparamétert vár el, de a második megegyezik az
elsővel. Ezért amikor a Test osztályból példányosítunk, elégséges a
következő: Test <int> test; A
típusparamétereknek is lehet alapértelmezett értéke, és ők is lehetnek
származtatott típusúak, ezt mutatja a következő példa: template<int size =20,
class T2 = CIntArray<size , 0>
> class TestA { }; Elégséges, ha
így példányosítunk: TestA<> test2; Konklúzióként
pedig csak annyit tudok mondani, hogy használjuk a sablonokat. Igaz, picit
nehézkes a gondolatmenetük több kódot kell írni, viszont nagyon rugalmasak,
és sok tekintetben megkönnyítik munkánkat. |
Deák Ferenc |