In cadrul acestui laborator vom aprofunda cunostintele legate de componentele parametrizate, functii clase template, si vom vorbi despre STL (Standard Template Library). Aceasta biblioteca generalizata ne va permite sa folosim algoritmi, structuri de date deja implementate.
Ca referinte externe, recomandam urmatorul capitol din Absolute C++:
STL (Standard Template Library) face parte din C++ Standard Library si este un set de clase template ce implementeaza structuri de date cunoscute si metode asociate lor.
Containerul reprezinta un obiect ce contine o colectie de alte obiecte. Clasele container implementeaza structuri de date clasice sub forma template, astfel ofera flexibilitate in utilizare. De asemenea, se ocupa de managementul spatiului de memorie pentru elemente si pun la dispozitie functii membre pentru accesul la obiecte.
Iteratorii permit accesul la elementele dintr-un container sau ajuta la parcurgerea acestora, independent de modul in care sunt stocate. Iteratorii reprezinta o generalizare a pointerilor, fiind obiecte ce indica alte obiecte (elementele din container).
Exista mai multe tipuri de containere:
Structura de date implementata | Nume STL | #include |
---|---|---|
Tablou dinamic | vector | <vector> |
Lista dublu inlantuita | list | <list> |
Stiva | stack | <stack> |
Coada cu doua capete | queue | <queue> |
Arbore binar/Multime | set | <set> |
Multime (se pot repeta valorile) | multiset | <set> |
Hash table | map | <map> |
Hash table (se pot repeta valorile) | multimap | <map> |
Max-heap | priority_queue | <queue> |
Exemplu: vector<int> vect;
Toate containerele au iteratori asociati, iar tipul lui e specificat prin:
container<T>::iterator
Toate containerele au urmatoarele functii membre:
Functie membra | Rezultat |
---|---|
int size() | returneaza numarul de elemente din container |
iterator begin() | returneaza iterator la primul element |
iterator end() | returneaza iterator la nodul santinela (ultimul element al containerului) |
bool empty() | returneaza true daca obiectul container este gol |
Clasa vector permite realizarea unui vector alocat dinamic, ce poate contine elemente de orice tip.
Clasa permite acces usor la orice element prin operatorul de indexare sau prin iteratori.
Implementarea din STL pune la dispozitie constructori, operator=, operator[], diferite metode pentru manipularea obiectelor din vector, realocarea spatiului, etc.
#include <vector> using namespace std; vector<int> a; //Vector gol cu elemente de tip int vector<double> b(10); //Vector cu 10 elemente de tip double vector<int> c(5,9); //Vector cu 5 elemente de tip int initializate cu valoarea 9 vector<int> d(a); //Copia vectorului a
Containerul List implementeaza o lista dublu inlantuita, ale carei elemente sunt imprastiate prin memorie si conectate prin pointeri. Lista poate fi parcursa in ambele sensuri, atat inainte, cat si inapoi.
Putem adauga sau elimina elemente atat de la inceputul listei, cat si de la finalul acesteia folosind metodele: push_back, push_front, pop_back, pop_front. Complexitatea acestor operatii este O(1).
Pentru a adauga sau sterge elemente de pe anumite pozitii se folosesc iteratori.
Principalul dezavantaj al listelor este ca NU se pot accesa imediat valori de pe o anumita pozitie.
Inserarea, mutarea si stergea unui element se face cu costuri mici, indiferent de pozitie.
#include <iostream> #include <list> #include <iterator> using namespace std; //Functie pentru afisarea elementelor din container void showlist(list<int> g) { //Iterator lista list<int>::iterator it; //Parcurgere container for(it = g.begin(); it != g.end(); ++it) { cout << *it << ' '; } cout << endl; } int main() { list<int> myList; myList.push_back(5); //Adauga elementul 5 la finalul listei myList.push_front(3); //Adauga elementul 3 la inceputul listei myList.push_back(10); //Adauga elementul 10 la finalul listei myList.push_front(1); //Adauga elementul 1 la inceputul listei showlist(myList); //Afiseaza 1 3 5 10 return 0; }
Containerul stack implementeaza o stiva, adica o lista simplu inlantuita de tip LIFO (Last In First Out). Varful stivei este singurul obiect al containerului care poate fi accesat.
Containerul queue implementeaza o coada, adica o lista simplu inlantuita de tip FIFO (First In First Out). Avem acces atat la inceputul cozii, cat si la finalul acesteia.
In ambele containere o sa se adauge elemente folosind metoda push si vor fi eliminate elemente folosind metoda pop.
#include <iostream> #include <queue> #include <stack> using namespace std; int main() { //Stiva cu elementele de tip char stack<char> s; //Coada cu elementele de tip int queue<int> q; //Adaugare elemente in varful stivei folosind push s.push('a'); s.push('b'); s.push('c'); //Adaugare elemente la sfarsitul cozii folosind push q.push(0); q.push(1); q.push(2); //Cat timp stiva nu este goala while (!s.empty()) { cout << s.top() << ' '; //Afisam elementul din varful stivei s.pop(); //Eliminam elementul din varful stivei } cout << endl; //Se va afisa: //c b a //Cat timp coada nu este goala while (!q.empty()) { cout << q.front() << ' '; //Afisam elementul de la inceputul cozii q.pop(); //Eliminam elementul de la inceputul cozii } //Se va afisa: //0 1 2 return 0; }
http://www.cplusplus.com/reference/stack/stack/ http://www.cplusplus.com/reference/queue/queue/
Containerul priority_queue implementeaza o coada cu prioritati, adica o structura de tip max-heap, in care cea mai mare valoare este intotdeauna in varf.
Avand in vedere faptul ca se fac comparatii intre valorile tipurilor de date prezente in container, trebuie sa definim mereu operator< pentru tipul respectiv de date.
#include <iostream> #include <queue> using namespace std; int main() { //Container cu elemente de tip int priority_queue<int> pq; //Adaugam elemente in coada cu prioritati pq.push(10); pq.push(20); pq.push(5); //Eliminam elementul din varf, adica elementul 20 (cu cea mai mare prioritate) pq.pop(); cout << pq.top() << endl; //Se va afisa 10 return 0; }
Containerele de tip set realizeaza un arbore binar balansat (de cautare), deci cautarea unui element va fi eficienta.
Pentru a se crea o multime, tipul de date trebuie sa aiba operator< definit.
Toate elementele din containerul set sunt distincte (in caz contrar se foloseste containerul multiset).
Obiectele din container sunt ordonate si nu mai pot fi modificate, acestea pot fi doar sterse sau se pot adauga noi obiecte.
#include <iostream> #include <set> using namespace std; int main() { //Multime cu elemente de tip int set<int> mySet; //Adaugam elemente in containerul set mySet.insert(1); mySet.insert(4); mySet.insert(2); mySet.insert(5); mySet.insert(3); //Iterator pentru parcurgere set set<int>::iterator it; //Parcurgem containerul set for (it = mySet.begin(); it != mySet.end(); it++) { cout << *it <<' '; //Afisare element } cout << endl; //Se va afisa: //1 2 3 4 5 return 0; }
Un container de tip map ne permite sa cautam intr-o maniera rapida un obiect folosind o cheie unica (spre exemplu cautam o persoana dupa id-ul acesteia).
Un astfel de container foloseste o pereche de tipuri template:
map<K, T>
K reprezinta tipul de date al cheii, iar T reprezinta tipul de date pe care vrem sa le stocam.
Containerul map va contine elementele indexate cu indecsi de tipul K al cheii.
Pentru tipul de date K al cheii este nevoie ca operator< sa fie definit, altfel se va folosi un container de tip unordered_map.
Elementele containerului sunt mereu sortate in functie de cheie (similar cu un arbore binar de cautare).
Pair este o structura template care are atributele first si second.
map<int, char> myMap; pair<int, char> obj(10, 'x'); myMap.insert(obj);
map<char, double> myMap; myMap['a'] = 20.189; myMap['b'] = 3.1415;
#include <iostream> #include <map> using namespace std; int main() { //Map cu tipurile de date char, double map<char, double> myMap; //Iterator pentru map map<char, double>::iterator it; //Adaugare element folosind operator[] myMap['a'] = 18.91; //Adaugare element folosind insert() si pair pair<char, double> obj('b', 3.1415); myMap.insert(obj); it = myMap.find('a'); //Cautam elementul cu cheia 'a' cout << it->second << endl; //Se va afisa 18.91 it = myMap.find('b'); //Cautam elementul cu cheia 'b' cout << it->second << endl; //Se va afisa 3.1415 return 0; }
it++ //muta iteratorul it cu o pozitie inainte
it-- //muta iteratorul it cu o pozitie inapoi
it1 = it2 //atribuie iteratorului it1 iteratorul it2
it1 == it2 si it1 != it2 //compara pozitiile dintre iteratorii it1 si it2
it = it + 4;
sau
it = it - 3;
Toate containerele STL contin metodele begin() si end() care returneaza iteratori.
Metoda begin() pointeaza la primul element al containerului.
Metoda end() pointeaza la nodul santinela (care nu contine niciun element, se afla imediat dupa ultimul element al containerului).
Contine o serie de functii care sunt utile pentru lucrul cu elementele din containere:
De obicei, functiile primesc ca parametri iteratori pentru pozitiile de inceput si final ale intervalului unde vrem sa realizam operatia.
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { //Container vector cu elemente de tip int vector<int> vect; //Adaugam elemente vect.push_back(10); vect.push_back(30); vect.push_back(15); vect.push_back(20); vect.push_back(10); //Iterator pentru parcurgere vector vector<int>::iterator it; for (it = vect.begin(); it != vect.end(); it++) { cout << *it << ' '; //Afisam elementele } cout << endl; //Se va afisa: //10 30 15 20 10 //Sortam vectorul folosind functia sort sort(vect.begin(), vect.end()); for (it = vect.begin(); it != vect.end(); it++) { cout << *it << ' '; //Afisam elementele } cout << endl; //Se va afisa: //10 10 15 20 30 cout << count(vect.begin(), vect.end(), 10) << endl; //Se va afisa 2 (numarul de aparitii al lui 10) return 0; }