Programiranje II STL Standardna C biblioteka STL n
Programiranje II STL Standardna C++ biblioteka
STL n n n Na prethodnom času pomenuli smo Standardnu Biblioteku Šablona (STL – Standard Template Library). Ova biblioteka je standardizovana od strane ANSI komiteta, a od 1998 dio je Standard C++ Library, koja pored STL-a uključuje biblioteku iostream, klasu sa stringovima, kompleksnim brojevima, podršku nacionalnim setovima karaktera, osnovne funkcije za obradu izuzetaka. . . Mi ćemo ovu biblioteku objasniti samo u kontekstu STL-a.
STL i Builder n n Borland Builder podržava STL kao i kompletnu Standard C++ biblioteku. Da bi pojedine klase iz C++-a mogle biti korišćene neophodno ih je, prije njihove upotrebe, “uključiti” naredbom: using std: : klasa; gdje je klasa naziv klase koja se uključuje. Kod drugih kompajlera pravila mogu biti nešto drugačija. Osnovni dijelovi STL-a su: n n n Šablonizovane strukture podataka nazvane kontejneri; Kontejnerima su asocirani pojedini operatori, dok je alokacija prepuštena alokatorima (umjesto new i delete) koji regulišu upravljanje memorijom; Iteratori služe za inteligentno indeksiranje kontejnera; Svi iole složeniji algoritmi su implementirani odvojeno od kontejnera.
Tipovi kontejnera n Tipovi kontejnera u STL-u su: n n Sekvencijalni kontejneri (vector, deque i list) Asocijativni kontejneri (set, multiset, map, multimap) Kontejnerski adapteri (stack, queue, priority_queue). Svi kontejneri posjeduju sljedeće f-je: podrazumijevani konstruktor, konstruktor kopije i destruktor, empty (tačno ako je kontejner prazan), max_size (maksimalan broj elemenata u kontejneru), size (broj elemenata trenutno u kontejneru), operator= (pridruživanje kontejnera); preklopljeni su i ostali operatori poređenja osim kod priority_queue-a, swap - zamjena elemenata dva kontejnera.
Funkcije kod sekv. kontejnera n n n begin (prvi element kontejnera), end (pozicija nakon kraja kontejnera), rbegin (posljednji element kontejnera), rend (pozicija prije prvog elementa kontejnera), erase (briše jedan ili više elemenata kontejnera), clear (briše čitav kontejner). Svi kontejneri se po pravilu nalaze u zaglavljima koja nose naziv kontejnera (npr. vector. h) i po potrebi ova zaglavlja trebaju biti uključena. Šablonizovana klasa vector namjenjena je za čuvanje i pristupanje većem broju elemenata pomoću indeksa.
Klasa vector n n n Šablon klase vector ima sljedeći oblik: template <class T, class A=allocator<T>> class vector{}; Prvi argument ovog šablona je tip elemenata u šablonu, dok je drugi alokator kontejnera koji vodi računa o alokaciji i dealokaciji kontejnera. Alokatori su izuzetno složena tema koja prevazilazi nivo našeg kursa. Prilikom deklaracije kontejnera ne moramo da navodimo alokator. Vektori se mogu deklarisati kao: Niz od tri vektora. vector<int> V; vector<float> F; vector<Student> Godine. Studija[3];
Klasa vector - Nastavak n n Ako napišete: vector<Student> Druga. Godina(30); vršimo alokaciju jednog podatka tipa vector<Student>, a taj vektor se sastoji od 30 Studenata i za svakog od tih studenata poziva se odgovarajući konstruktor. Pojedinačnim studentima se prestupa kao: Druga. Godina[5]=Marko; a mogu se pozvati i svi metodi klase Student za svakog pojedinačnog studenta Druga. Godina[5]. Daj. God. St(). Za vektor kao tip podataka mogu se primijeniti mnogi metodi kao što su: Druga. Godina. size(), koji daje koliko je trenutno elemenata u vektoru, zatim Druga. Godina. capacity(), koji daje koliko se elemenata ukupno može smjestiti u vektor, itd.
Vektor – Neki metodi n n n Metod empty() vraća logičku istinu ako je vektor trenutno prazan. Novi element se vektoru može dodati na više načina: Druga. Godina. push_back(Pedja); automatski dodaje 51. član vektoru i postavlja ga na kraj. Da bi ovo moglo da radi naša je obaveza da klasa Student ima konstruktor kopije. Od drugih funkcija treba napomenuti: front() i back(), koje daju referencu na prvi i posljednji elemenat vektora, at() koja radi kao operator indeksiranja [] (uz to da f-ja “baca” izuzetak tipa out_of_range u slučaju prekoračenja indeksa), insert() na datu poziciju umeće jedan ili više članova, pop_back() uklanja posljednji element vektora.
Kontejner list n n n Sličan kontejner vektoru je list<tip> Naziv. Liste; . Kao što ime pokazuje, ovo je lista i to dvostruko povezana u kojoj svaki element ima referencu na naredni i prethodni element liste. Posmatrajmo sljedeći primjer: #include <iostream. h> #include <list. h> main() {list <int> Lista. Cijelih; for(int i=1; i<=10; i++) Lista. Cijelih. push_back(2*i); for(list <int>: : const_iterator ci=Lista. Cijelih. begin(); ci!=Lista. Cijelih. end(); ++ci) cout<<*ci<<“ “; }
Objašnjenje primjera n n U prvom dijelu prethodnog primjera ne dešava se ništa spektakularno. Na kraj liste metodom push_back() upisujemo 10 prvih cijelih brojeva i formiramo listu cijelih brojeva: 2, 4, 6, . . . , 20. U drugom dijelu liste koristimo generalizaciju pokazivača, koji se naziva iterator, pomoću kojeg možemo da prođemo kroz sve elemente liste polazeći od početnog i idući ka krajnjem. U našem primjeru, iterator je konstantan (const_iterator) jer ne želimo da promjeni objekat na koji se pokazuje. Ako se želi dozvoliti promjena objekta piše se prosto iterator. Iterator se dereferencira (*ci) kako bi se pristupilo željenom objektu.
Dodatni metodi za šablon list n n n Lista posjeduje i metode push_front() i pop_front() koji služe za dodavanje i brisanje elemenata sa početka liste. Razlog za ovu dodatnu fleksibilnost leži u činjenici da imamo dvostruko povezanu listu. Ostale osobine ovog šablona upoznajte sami. Slična šablonskoj klasi vektor je i šablon deque, uz osnovne izmjene vezane za način kako se alocira memorija za nove članove deque-a, odnosno kako se alocira sam deque.
Asocijativni kontejneri n n Kod sekvencijalnih kontejnera elementima se pristupa sekvencijalno ili direktno (putem indeksa ili iteratora). Brzi pristup kod asocijativnih kontejnera se obezbjeđuje pomoću ključa. Četiri asocijativna kontejnera su: map, multimap, set i multiset. Asocijativni kontejner multiset se koristi za brzo smiještanje i vraćanje ključeva. Ovaj kontejner dozvoljava duple ključeve. Elementi mogu biti sortirani u rastući ili opadajući redosljed. Da bi ovo bilo moguće treba imati preklopljen operator < ili >.
multiset primjer n Sljedeći dosta složen primjer ćemo upotrijebiti da objasnimo niz važnih detalja vezanih za multiset kontejner, a ujedno i za kompletan STL. Primjer je preuzet (uz korekcije iz Deitel & Deitel). Stil programa je ostao kao u originalnom materijalu. #include <iostream> int. Multiset. insert(15); #include <set> cout<<“Sada ima”<<int. Multiset. count(15)<< #include <algorithm> <<“ 15 -tice u multisetun”; using namespace std; ims: : const_iterator result; int main(){ result=int. Multiset. find(15); const int SIZE=10; if(result!=int. Multiset. end()) cout<<“nadjen 15n”; int a[SIZE]={7, 22, 9, 1, 18, 30, 100, 22, 85, 13}; result=int. Multiset. find(20); typedef multiset<int, less<int>>ims; if(result==int. Multiset. end()) cout<<“nije nadjen 15n” ims int. Multiset; int. Multiset. insert(a, a+SIZE); ostream_iterator<int> output(cout, ” ”); cout<<“Nakon umetanja multiset sadržin”; cout<<“Trenutno ima”<<int. Multiset. count(15)<< copy(int. Multiset. begin(), int. Multiset. end(), <<“ 15 -tice u multisetun”; output());
multiset primjer - Nastavak cout<<“Donja granica od 22: *(int. Multiset. lower_bound(22)); cout<<“Gornja granica od 22: *(int. Multiset. upper_bound(22)); pair<ims: : const_iterator, ims: : const_iterator> p; p=int. Multiset. equal_range(22); cout<<“n. Korišćenje equal_range od 22”<< “n donja granica”<<*(p. first)<< “n gornja granica”<<*(p. second); cout<<endl; return 0; } Program sada treba pokušati objasniti što, i pored njegove relativne kratkoće, nije jednostavno. Počnimo redom: typedef multiset<int, less<int>>ims; ims int. Multiset; int. Multiset je multiset sastavljen od cijelih brojeva poređanih u rastući (less<int>) redosljed.
multiset primjer - Objašnjenje n n Funkcija count je jednostavna jer broji koliko objekata takvog tipa ima u multiset-u. Funkcija insert umeće u multiset novi elemenat. Nakon dva poziva iste funkcije broj elemenata sa upisanim brojem 15 je 2. Funkcija find (raspoloživa u svim asocijativnim kontejnerima) locira traženu vrijednost u multiset-u. Rezultat ove operacije je iterator (ili const_iterator) koji pokazuje na prvo pojavljivanje ovog objekta u multiset-u, dok ako ga nema vraća rezultat koji je jednak end. Funkcija insert umeće niz a u multiset (poziva se copy algorithm za ovu operaciju). Multiset se uvijek preuređuje u rastući redosljed.
multiset primjer - Objašnjenje n n n Funkcije lower_bound i upper_bound su raspoložive u svim asocijativnim kontejnerima i daju iterator (iteratorima se pristupa sa *) na prvo pojavljivanje datog elementa, odnosno na prvi element nakon posljednjeg pojavljivanja traženog elementa. Deklaracija: pair<ims: : const_iterator, ims: : const_iterator> p; inicijalizuje objekat šablonske klase pair koja se koristi da manipuliše parovima vrijednosti. U predmetnom primjeru, equal_range daje par iteratora koji pokazuju na zonu od pozicije gdje se pojavljuje traženi element (22) do onog iteratora iznad posljednjeg pojavljivanja.
multiset primjer - Objašnjenje n n p. first je iterator na prvi dio para (prvo pojavljivanje 22), dok je p. second iterator na drugi dio para (prvi element iznad 22). Elementu unutar iteratora uvijek se pristupa preko operatora dereferenciranja, koji je za ovu priliku morao biti preklopljen. Nadam se da predmetni primjer ilustruje moć kontejnera, a ujedno ilustruje i neke od funkcija vezanih za kontejnerske klase. Rad sa drugim asocijativnim kontejnerima je sličan, a kao početna literatura može da posluži Deitel-Deitel, strane 958 -963.
Kontejneri adapteri n n n STL posjeduje tri kontejnerska adaptera: stack, queue, i priority_queue. Adapteri nijesu implementirani kao strukture podataka i ne podržavaju iteratore. Adapteri se odnose na neku strukturu podataka i od interesantnih metoda imaju push i pop, kojima se umeće novi, odnosno briše stari adapter u predmetnu strukturu podataka. Mi ćemo ovdje ilustrovati primjenu stack adaptera koji može biti implementiran sa sekvencijalnim kontejnerima vector, list i deque (po default-u sa deque).
stack adapter n Osnovne operacije koje su implementirane kod stack-a su: n n n n push umeće element na vrh steka (poziva se push_back za asocirani kontejner); pop briše element sa vrha (pop_back iz kontejnera se poziva); top daje referencu na element na vrhu; empty određuje da li je stack prazan; size daje broj elemenata u stack-u. Svaki od predmetnih metoda se svodi na poziv nekog metoda iz odgovarajućeg asocijativnog kontejnera. Dajemo primjer za ovaj kontejner (preuzet iz Deitel-Deitel) i pozivamo vas da pogledate stranice 966 -969 za objašnjenje drugih kontejnerskih adaptera.
stack primjer cout<<“Uzimanje elemenata iz int. List. Stack: ” #include <iostream> pop. Elements(int. List. Stack); #include <stack> cout<<endl; #include <vector> return 0; #include <list> } using namespace std; template <class T> void pop. Elements(T &s){ void pop. Elements(T &s); while(!s. empty()){ int main(){ cout<<s. top()<<‘ ‘; stack<int> int. Deque. Stack; stack<int, vector<int>> int. Vector. Stack; s. pop(); } stack<int, list<int>> int. List. Stack; } for(int i=0; i<10; ++i){ int. Deque. Stack. push(i); Primjer je više nego prost. int. Vector. Stack. push(i); Deklarišemo 3 adaptera na deque int. List. Stack. push(i); } (podrazumijevani), list i vector. cout<<“Uzimanje elemenata iz int. Deque. Stack: ” U asocijativne kontejnere unosimo pop. Elements(int. Deque. Stack); nekoliko elemenata, a zatim pozivamo cout<<“Uzimanje elemenata iz int. Vector. Stack: ” funkciju koja ih štampa uz brisanje pop. Elements(int. Vector. Stack); štampanih elemenata.
Algoritmi n n n U prvim verzijama STL-a svi algoritmi su bili implementirani u kontejnerima, pa su bili implementirani preko virtuelnih metoda kako bi odgovorili aktuelnim klasama za koje su pozvani. Ovo je problematično sa stanovišta efikasnosti, a efikasnost je od primarne važnosti za kreatore VCL-a. Stoga su algoritmi odvojeni od kontejnera u kasnijim i aktuelnim verzijama VCL-a. Na ovaj način je izbjegnuto gubljenje performansi prouzrokovano pozivom virtuelnog metoda. Ovdje ćemo ilustrovati algoritme na dva primjera preuzeta iz Deitel -Deitel, a ova tema je u predmetnoj knjizi veoma detaljno obrađena na stranicama 969 -1004, pa vam preporučujem da to pogledate.
Primjer sa remove n Primjer demonstrira brisanje vrijednosti iz niza. #include <iostream> #include <algorithm> #include <vector> using namespace std; bool greater 9(int); int main(){ const int SIZE=10; int a[SIZE]={10, 2, 10, 4, 16, 6, 14, 8, 12, 10}; ostream_iterator<int>output(cout, ” ”); vector <int> v 1(a, a+SIZE); cout<<“Vektor v 1 prije zamjene svih 10: n”; copy(v 1. begin(), v 1. end(), output); replace(v 1. begin(), v 1. end(), 100); cout<<“Vektor v 1 nakon zamjene svih 10 sa 100: n”; copy(v 1. begin(), v 1. end(), output); vector <int> v 2(a, a+SIZE); vector<int> c 1(SIZE); cout<<“v 2 prije zamjeni svih 10 -ki i kopiranjan”; copy(v 2. begin(), v 2. end(), output); replace_copy(v 2. begin(), v 2. end(), c 1. begin(), 100); cout<<“c 2 nakon zamjene svih 10 -ki u v 2”; copy(c 1. begin(), c 1. end(), output); vector<int> v 3(a, a+SIZE); cout<<“v 3 prije zamjene vrijednosti >9”; copy(v 3. begin(), v 3. end(), output); replace_if(v 3. begin(), v 3. end(), greater 9, 100); cout<<“v 3 nakon zamjene vrijednosti >9”; copy(v 3. begin(), v 3. end(), output); vector<int> v 4(a, a+SIZE); vector <int> c 2(SIZE); cout<<“v 4 prije zamjene >9 i kopiranjan”; copy(v 4. begin(), v 4. end(), output); replace_copy_if(v 4. begin(), v 4. end(), c 2. begin(), greater 9, 100); cout<<“c 2 nakon zamjene svih >9 u v 4n”;
Objašnjenje primjera copy(c 2. begin(), c 2. end(), output); cout<<endl; n n n return 0; } bool greater 9(int x){return x>9; } Metod copy vrši štampanje sadržaja vektora od jednog do drugog iteratora (u ovom slučaju od početka do kraja) na output sredstvo (ostream_iterator). Metod: replace(v 1. begin(), v 1. end(), 100); mijenja svako pojavljivanje elementa 10 sa 100. Metod: replace_copy(v 2. begin(), v 2. end(), c 1. begin(), 100); kopira elemente vektora v 2 u vektor c 1, a ujedno mijenja svako pojavljivanje 10 sa 100.
Objašnjenje primjera n n n Metod: replace_if(v 3. begin(), v 3. end(), greater 9, 100); mijenja sve elemente koji zadovoljavaju uslov postavljen u metodu greater 9 sa 100. Konačno, metod: replace_copy_if(v 4. begin(), v 4. end(), c 2. begin(), greater 9, 100); vrši zamjenu elemenata u vektoru v 4, koji zadovoljavaju dati uslov, sa 100 u vektor c 2. Naredni primjer je posvećen osnovnim algoritmima pretraživanja i sortiranja i njihovoj realizaciji u STL-u. Napominjemo da postoji više algoritama sortiranja i pretraživanja implementiranih u STL-u, ali ćemo ovdje obraditi samo rudimentarne.
Sortiranje i pretraživanje #include <iostream> #include <algorithm> #include <vector> using namespace std; bool greater 10(int); int main(){ const int SIZE=10; int X[SIZE]={10, 2, 10, 4, 16, 6, 14, 8, 12, 10}; ostream_iterator<int>output(cout, ” ”); vector <int> v(X, X+SIZE); cout<<“Vektor v sadrži: n”; copy(v. begin(), v. end(), output); vector<int>: : iterator location; int a; cin<<a; location=find(v. begin(), v. end(), a); if(location!=v. end()) cout<<“Nadjen trazeni elemenat. n”; else cout<<“Nije nadjen elemenat. n”; location=find_if(v. begin(), v. end(), greater 10); if(location!=v. end()) cout<<“Element nadjen na lokaciji “<< (location-v. begin(); else cout<<“Element nije nadjen. n”; sort(v. begin(), v. end()); cout<<“Vektor nakon sortiranja. n”; copy(v. begin(), v. end(), output); if(binary_search(v. begin(), v. end(), a)) cout<<“Broj je nadjen!n”; else cout<<“Broj nije nadjen!n”; cout<<endl; return 0; } bool greater 10(int r){return r>10; }
Objašnjenje primjera n n n Objasnimo metode: find je standardno pretraživanje; find_if je pretraživanje za prvi elemenat koji ispunjava neki uslov; sort je sortiranje; binary_search je binarno pretraživanje (podijeli pa vladaj algoritam) koji se može sprovesti nad sortiranim nizom. Ovim smo objasnili STL u najrudimentarnijem mogućem obliku.
Klasa string n n n Standard C++ library pored STL-a uključuje i niz interesantnih klasa. Jedna od njih je klasa string koja enkapsulira string kao tip podataka. Ova klasa se razlikuje od niza karaktera u mnogim aspektima od kojih je najvažniji taj što klasa string posjeduje ugrađene metode, kao što je metod length (poziva se kao s. length()), koji daje dužinu stringa što, zatim, znači da se kod rada sa klasom string gubi potreba za vođenjem računa o terminacionom karakteru.
Druge string klase n n Klasa string je izuzetno bogata metodima koji enkapsuliraju unutar klase sve funkcionalnosti koje su inače predstavljene u C-u u biblioteci string. h za nizove karaktera koji se završavaju terminacionim simbolom. Osnovna prednost je u klasnoj realizaciji, ali postoje i druga poboljšanja. Kompajleri su razvili i svoje posebne verzije klase string. Jedna od najpopularnijih je Borlandova Ansi. String klasa. Ova klasa posjeduje ogroman broj metoda, uključujući preklopljene operatore, i jedna je od najpopularnijih klasa za stringove.
Metodi u Ansi. String klasi n n n n ~Ansi. String Ansi. Compare n Ansi. Compare. IC n Ansi. Last. Char n n Ansi. Pos n Ansi. String n Byte. Type n c_str n cat_printf n cat_sprintf n cat_vprintf n Curr. To. Str. F n data n Delete Float. To. Str. F Fmt. Load. Str Format. Float Insert Int. To. Hex Is. Delimiter Is. Empty Is. Lead. Byte Is. Path. Delimiter Is. Trail. Byte Last. Delimiter Length Load. Str n n n n Load. String Lower. Case operator != operator [] operator += operator <= operator == operator >= Pos printf Set. Length n n n n sprintf String. Of. Char Sub. String To. Double To. Int. Def Trim. Left Trim. Right Unique Upper. Case vprintf Wide. Char. Buf. Size
Zaključak n n n Prepuštavamo vam da sami “uronite” u dalje izučavanje string i Ansi. String klase. Napominjemo da ovo nijesu jedini elementi koji postoje u u okviru Standard C++ Library, ali su svakako najznačajniji. Ovi elementi su kreirani da se koriste, ali istovremeno vrijedi razmisliti i o njihovoj realizaciji jer je realizacija ovih elemenata velika škola OOP, C++ i softverskog dizajna i optimizacije koju vrijedi proći samostalno ili uz pomoć dodatne literature.
- Slides: 30