You are on page 1of 7

Uvod u C++ ablone

Nemanja Trifunovi

Ovaj tekst je namenjen ljudima koji poznaju C++, ali nisu radili sa ablonima.
Namera mi je da pomognem programerima da koriste biblioteke zasnovane na
ablonima (pre svega STL), a ne da piu takve biblioteke. Dakle, izostaviu mnoge
"napredne" teme rada sa ablonima, kao to je meta-programiranje, liste tipova,
itd i zadrau se na osnovnim stvarima.

1. ta su abloni?

U programerskoj praksi je jedan od najznaajnijih pitanja, kako napisati kod koji


se moe koristiti za reavanje to vie praktinih problema (tzv "code reuse"). Tako
se dolo do zakljuka da mnogi algoritmi izgledaju praktino isto kada rade sa
razliitim tipovima. Uzmimo sasvim jednostavan primer: funkciju max koja treba
da vrati veu (u stvari "ne manju") od dve zadate vrednosti. Za celobrojne bojeve,
ova funkcija bi izgledala ovako:

1. const int& max (const int& a, const int& b)


2. {
3. return a > b ? a : b;
4. }

A ako bismo hteli da napravimo istu funkciju za realne brojeve, dobili bismo neto ovako:

1. const double& max (const double& a, const double& b)


2. {
3. return a > b ? a : b;
4. }

Ili ako imamo klasu "titula" za koju je preklopljen operator <

1. const titula& max (const titula& a, const titula& b)


2. {
3. return a > b ? a : b;
4. }

U emu se razlikuju ove tri funkcije? Jedino u tipu argumenata i povratne vrednosti. Kada
bismo mogli da i tip prosledimo kao argument funkcije, onda bismo imali jednu funkciju
koja radi sa svim tipovima koji "prepoznaju" operator >. Upravo tu dolaze abloni kao
reenje za na problem:

1. template <typename T>


2. const T& max (const T& a, const T& b)
3. {
4. return a > b ? a : b;
5. }

Ovde je T tip - int, double, titula ili ma koji drugi za koji postoji operator >. ablon funkcija
koju smo upravo definisali, strogo gledano, nije obina funkcija koja prihvata dodatni
parametar za tip promenljivih, ve upravo to to mu ime kae: ablon na osnovu koga
kompajler moe da napravi potencijalno bezbroj funkcija koje se razlikuju samo po tipu.
Osim ablon funkcija, postoje i ablon klase, koje se esto koriste kao "kontejneri" za
razliite tipove. Tipian primer takve ablon klase je STL klasa vector, koja se esto koristi
umesto C++ nizova.

Kako se u praksi koriste abloni? Evo jednostavnog primera:

1. template <typename T>


2. const T& max (const T& a, const T& b)
3. {
4. return a > b ? a : b;
5. }
6.
7.
8. #include <vector>
9. int main (void)
10. {
11. std::vector<int> niz;
12. niz.push_back(1);
13. niz.push_back(2);
14. niz.push_back(3);
15. int m = max<int> (niz[1], niz[2]);
16. }

Najpre smo definisali promenljivu niz kao vector<int>; onda smo joj dodali 3 cela broja, i
na kraju smo promenljivoj m dodelili vrednost veeg od poslednja dva elementa u niz-u.
vector-u smo naznaili da treba da sadri promenljive tipa int, a kasnije smo i funkciji max
naznaili da radi sa promenljivama tipa int (kod ablon funkcija, ovo esto nije potrebno
initi, jer je kompajler dovoljno pametan da sam zakljui o kom se tipu radi - o tome neto
vie kasnije). Kada bismo recimo umesto sa int-ovima, radili sa double-ovima, kod bi
izgledao ovako:
1. std::vector<double> niz;
2. niz.push_back(1.);
3. niz.push_back(2.);
4. niz.push_back(3.);
5. double m = max<double> (niz[1], niz[2]);

Dakle, nismo morali da menjamo kod unutar funkcije max ili klase vector, ve smo samo
iskoristili ablone da dobijemo tipove koji rade sa double, umesto sa int.

2. ablon funkcije

Kao to sam napomenuo, ablon funkcije je ablon kojim se definie skup funkcija koji
imaju isti kod, ali rade sa razliitim tipovima. Tako od ablon funkcije max moemo kreirati
funkcije max<int>, max<long>, max<std::string>, max<neka_druga_klasa&gt. Pri tome
esto (ali ne uvek!!!) nije obavezno eksplicitno navesti tip, ve kompajler moe sam da
zakljui o kom se tipu radi.

1. int i = max<int> (1,2);


2. long l = max<long> (1, 2);
3. double m = max<double> (1., 2.);
4.
5. int b = max (1,2) // kompajler je sam odredio tip argumenata (int)
6.
7. int c = max (1., 2) // Greska!!! Da li je tip int ili double?
8. int d = max<double> (1., 2) // nema greske - eksplicitno zadat tip
double - drugi argument konvertovan u double

Mnoge ablon funkcije su dostupne programerima u STL biblioteci u headeru <algorithm>.


Tu se nalaze ablon funkcije za sortiranje, sumiranje, permutacije i mnoge druge.

3. ablon klase

Isto kao to ablon funkcije definiu skup funkcija koje rade ne isti nain sa razliitim
tipovima, tako i ablon klase definiu skup klasa koje na neki nain zavise od razliitih
tipova. Klasian primer ablona klasa je dat u sledeem primeru:

1. template <typename T, int SIZE>


2. class array
3. {
4. T data[SIZE];
5. public:
6. T& operator[](int index) {return data[index];}
7. };
8.
9.
10. int main (void)
11. {
12. array<int, 40> celob; // niz od 40 int-ova
13. celob[2] = 4;
14.
15. array<char*, 10> reci; // niz od 10 pointera na char*
16. reci[3] = "pevaj";
17. }

ablon klasa array nije neto preterano korisna u praksi, jer ne radi nita to ne pruaju i
C++ nizovi, ali je dovoljno jednostavna da nam poslui za ilustraciju kako rade ablon klase.
Kao to se vidi, ovoj ablon klasi se zadaju dva parametra: prvi je tip i njime specificiramo
kakve emo promenljive drati unutar niza, a drugi je celobrojna vrednost kojom
odreujemo veliinu niza. Parametri ablon klasa mogu biti:

1. Tipovi (klase, unije, C++ tipovi).


2. Celobrojne konstante ija je vrednost odreena u trenutku kompajliranja.
3. abloni (u praksi, mnogi kompajleri ne dozvoljavaju ovakve parametre).

Skrenuo bih panju na br 2. Mnogi poetnici pokuavaju da za parametar ablona daju


celobrojnu vrednost koja nije poznata u vreme kompajliranja (npr neku vrednost koju unese
korisnik). Takvi pokuaji se zavravaju porukom o greci koju prijavljuje kompajler.

Takoe, dozvoljeni su i podrazumevani parametri, pa smo mogli da uradimo neto kao:

1. template <typename T = int, int SIZE = 50>


2. class array
3. {
4. T data[SIZE];
5. public:
6. T& operator[](int index) {return data[index];}
7. };
8.
9.
10. int main (void)
11. {
12. array<double> realni; // niz od 50 double-ova
13. array<> celob; // niz od 50 int-ova
14. }

4. Eksplicitna specijalizacija ablona

Vratimo se za trenutak ablon funkciji max. Za veliki broj tipova, iz nje dobijamo funkcije
koje rade upravo ono to mi elimo. Meutim, pretpostavimo da imamo neki tip za koji je
vrednost max definisana na neki drugi nain. U tom sluaju, moemo da napravimo tzv
"eksplicitnu specijalizaciju" ablona za taj tip.
1. template<> const MojTip& max<MojTip> (const MojTip& a, const
MojTip& b)
2. (
3. if (a.vece(b))
4. return a;
5. else
6. return b;
7. )

Ako negde u programu imamo recimo:

int celo = max (1,2); // ovde je pozvan "generalni" sablon


const MojTip& neki = max(nekiA, nekiB); // ovde je pozvan specijalizovani sablon

Ako za neki tip postoji specijalizacija ablona kompajler e iskoristiti tu specijalizaciju, a ako
ne onda e koristiti originalni ablon da generie funkciju za izraunavanje maksimuma.

Postoji i mogunost parcijalne specijalizacije ablona (partial template specialization - PTS),


ali samo za klase ablone. U tom sluaju se specijalizuje ablon za samo neke parametre, a
ne za sve.

5. STL

Definitivno najbolji nain da se pone sa upotrebom ablona je kroz korienje dela


standardne C++ biblioteke koji se zove STL (standard template library). Korienje STL-a u
svakodnevnom programiranju omoguava veu produktivnost i manje greaka pri emu se
najee ne plaa nikakva cena u performansama. Krenimo sa prostim primerom: korisnik
unosi razne rei (jednu po jednu) dok ne otkuca kraj. Program tad sortira rei po
abecednom redu i prikae ih na ekranu:

1. #include <vector>
2. #include <iostream>
3. #include <algorithm>
4. #include <string>
5. using namespace std;
6.
7. int main (void)
8. {
9. // niz stringova
10. vector <string> nizStringova;
11.
12. // Unos reci u niz dok se ne otkuca "kraj"
13. string unos;
14. while (true)
15. {
16. cout << "Unesite neku rec ili kraj\n";
17. cin >> unos;
18. if (unos.compare("kraj") == 0)
19. break;
20. nizStringova.push_back(unos);
21. }
22.
23. // Sortiranje
24. sort(nizStringova.begin(), nizStringova.end());
25.
26. // Prikaz stringova na ekranu:
27. copy(nizStringova.begin(), nizStringova.end(),
ostream_iterator<string>(cout, "\n"));
28. }

Osim poslednjeg reda, ovaj program je toliko itljiv, da mu komentari gotovo nisu ni
potrebni, ak ni za nekog ko ne zna nita o STL-u.

U gornjem primeru se vide osnovni elementi koji sainjavaju STL:

a) algoritmi - u ovom primeru sort i copy. STL sadri 60-ak ablon funkcija za raznorazne
operacije: pretraga, sortiranje, generisanje, rotiranje, traenje maksimuma, itd. Ove ablon
funkcije su deklarisane u standardnom zaglavlju . Fleksibilnost ovih algoritama je u tome
to oni mogu da operiu sa raznim tipovima podataka. Sve to im treba je "neto" (iterator)
to pokazuje na poetak i kraj sekvence nad kojom e raditi.

b) kontejneri - u ovom primeru vector. Kontejneri su strukture podataka koje sadre druge
podatke. Npr, ovde smo imali vector<string> koji sadri niz stringova. STL kontejneri su
jako udobni za rad - posebno u poreenju sa C nizovima. Realokacija memorije se vri
automatski. Kontejneri (posebno vector) su ono sa ime treba poeti rad u STL-u.

c) iteratori - u ovom primeru ih nismo direktno videli, ali funkcije begin() i end() vraaju
iteratore. Ma koliko to izgledalo udno na prvi pogled, algoritmi i kontejneri ne znaju nita
jedni o drugima. Ono to ih povezuje su iteratori - objekti koji pokazuju na pojedine lanove
kontejnera (slino kao to pointeri mogu da pokazuju na lanove C niza). Da bi sort
algoritam poreao neku sekvencu, nije mu potrebna informacija o tome koje je sekvenca u
pitanju, ve samo iteratori koji pokazuju na poetak i kraj te sekvence. Ovo omoguava da
u nekim sluajevima jako lako zamenimo jedan kontejner drugim, uz minimalne promene
koda. Recimo, ako bismo u gornjem primeru hteli da umesto vector-a koristimo deque,
dobili bismo:

1. #include <deque>
2. #include <iostream>
3. #include <algorithm>
4. #include <string>
5. using namespace std;
6.
7. int main (void)
8. {
9. // niz stringova
10. deque <string> nizStringova;
11.
12. // Unos reci u niz dok se ne otkuca "kraj"
13. string unos;
14. while (true)
15. {
16. cout << "Unesite neku rec ili kraj\n";
17. cin >> unos;
18. if (unos.compare("kraj") == 0)
19. break;
20. nizStringova.push_back(unos);
21. }
22.
23. // Sortiranje
24. sort(nizStringova.begin(), nizStringova.end());
25.
26. // Prikaz stringova na ekranu:
27. copy(nizStringova.begin(), nizStringova.end(),
ostream_iterator<string>(cout, "\n"));
28. }

Uporedite gornja dva primera. Razlikuju se samo dve linije (umesto vector-a, koristimo
deque). Algoritmi sort i copy rade kao i pre - oni "nemaju pojma" da je dolo do neke izmene.

STL je biblioteka koju treba koristiti iz prostog razloga to je lake programirati sa njom nego
bez nje.

6. Zakljuak

U mnogim knjigama o C++u, abloni se obrauju na kraju, kao jedan od najteih elemenata
ovog jezika. To je samo delimino opravdano. Jeste komplikovano razvijati ablone, ali je jako
lako koristiti gotove ablone. Ako uporedimo C nizove i vector, teko je smisliti razlog zbog kog
bi neko danas koristio ovo prvo: C nizovi su komplikovaniji i lake se prave greke u radu sa
njima, a da i ne pominjemo probleme sa realokacijom memorije kod rasta niza.

abloni omoguavaju generiko programiranje - koncept koji je noviji i u mnogo emu moniji
od objektnog programiranja. Najlepe od svega je to to se ova dva koncepta meusobno ne
iskljuuju, ve se mogu kombinovati na razne naine, a sve to vodi kodu koji je lake
projektovati, kodirati i odravati.