950 likes | 1.08k Views
STL (Standard Template Library). Curs 14. STL. biblioteca C++ care contine clase pentru containere, algoritmi si iteratori Furnizeaza majoritatea algoritmilor de baza si structurilor de date necesare in dezvoltarea de programe
E N D
STL (Standard Template Library) Curs 14
STL • biblioteca C++ care contine clase pentru containere, algoritmi si iteratori • Furnizeaza majoritatea algoritmilor de baza si structurilor de date necesare in dezvoltarea de programe • STL este o bibiblioteca generica – componentele sunt parametrizate – aproape fiecare componenta din STL este template
Containere • Vector • List • Deque • Queue • Stack • Priority queue • Set • Map • Multiset • Multimap • Bitset Containere secventiale Adaptori de containere Containere asociative (cheie-valoare)
Vector • Elimina problema realocarii dinamice a spatiului de memorie • Se redimensioneaza in partea finala astfel incat sa poata fi realizata adaugarea de noi elemente • Compusi dintr-un bloc de memorie alocata secvential • Folosit cand se fac adaugari/stergeri de la sfarsit • Ineficient cand depasirile de memorie alocata pot determina copierea intregului vector • Constrangeri asupra lui T: T();T(const T&); ~T(); T& operator=(const T&); #include <vector> std::vector<int> myvector; std:: vector<int>::iterator it; Declaratie vector de intregi Declaratie iterator pe vector de intregi
List • Compuse din obiecte care au anterior-info-urmator • Nu detin ownership asupra elementelor • Folosite cand se fac inserari/stergeri oriunde in cadrul listei • Cerinte pentru tipul T: • T(); T(const T&); ~T(); T& operator=(const T&); T* operator&(); int operator < (const T&, const T&); int operator==(const T&, const T&); #include <list> std::list<int> identifier; std::list<int>::iterator identifier; Declaratie lista de intregi Declaratie iterator pe lista de intregi
Deque (deck) • deque=double ended queue • Nu e FIFO • Permite adaugarea/stergerea elementelor la ambele capete • Permite inserarea sau stergerea de elemente la pozitii arbitrare • Accesul la elemente de poate realiza similar vectorilor, cu operatorul [] sau metoda at() • #include <deque> • std::deque<int> dequeOfIntegers; • std::deque<int>::iterator itr; Declaratie coada de intregi Declaratie iterator coada de intregi
Vector sau deque? • Deque permite inserarea/stergerea elementelor de la inceputul/sfarsitul cozii in timp constant • Vectorul permite inserarea/stergerea elementelor doar la sfarsit in timp constant • Deque permite inserarea/stergerea de elemente la pozitii arbitrare mai eficient decat vectorul, dar nu in timp constant • Accesul aleator la elemente este mai rapid la vector decat la deque • Pentru secvente mari de elemente vectorul va aloca o zona mare de memorie contigua, pe cand deque va aloca mai multe blocuri de memorie de dimensiuni mai mici – mai eficient din punctul de vedere al sistemelor de operare • Deque se comporta atat ca o stiva, cat si ca o coada • Deque se expandeaza mai eficient decat vectorii
Stack • LIFO • Ca implementare, in STL, o stiva e un adaptor (implementare a sablonului de proiectare Adapter – vezi curs 11) – preia ca parametru al templateului un container secvential si lasa vizibile doar operatiile care sunt asociate unei stive • #include <stack> • #include <C> - C containerul care dorim sa fie adaptat la stiva #include <stack> #include <list> std::stack<int, std::list<int> > myStack; Implicit deque (lista consuma prea multa memorie, vectorii de expandeaza costisitor) std::stack<int> myStack;
Set • Cotainer in care toate elementele sunt distincte • Pastreaza elementele sortate!! • Functia find() are complexitate logaritmica #include <set> std::set<int> s; std::set<int>::iterator it=s.begin(); Declaratie multime de intregi Declaratie iterator multime de intregi #include <set> #include <iostream> using namespace std; int main(){ set<int> intSet; for(int i=0;i<25;i++) for(int j=0;j<10;j++) intSet.insert(j); set<int>::iterator it=intSet.begin(); while(it!=intSet.end()) {cout<<*it<<" "; it++; } return 0; } 0 1 2 3 4 5 6 7 8 9
Multiset • Accepta aparitii multiple ale unui element #include <set> #include <iostream> using namespace std; int main(){ multiset<int> intSet; for(int i=0;i<25;i++) for(int j=0;j<10;j++) intSet.insert(j); multiset<int>::iterator it=intSet.begin(); while(it!=intSet.end()) {cout<<*it<<" "; it++; } return 0; } 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
Map • Asociaza chei de un anumit tip cu valori de alt tip • Elementele sunt accesate direct, pe baza cheii • aMap["John Doe"] = 3.2; • Cheile trebuie sa fie unice • Atentie la utilizarea operatorului [] – algoritm: • Cauta cheia in dictionar • Daca e gasita se returneaza o referinta spre valoarea asociata • Daca nu e gasita se creeaza un nou obiect de tipul valorii si se returneaza o referinta spre el • Pentru a verifica apartenenta unui element la dictionar se foloseste metoda find() – nu creeaza un obiect daca cheia nu exista • Find returneaza un iterator pe o pereche de obiecte (cheie, valoare) daca cheia exista, altfel returneaza end() #include <map> std::map<string, double> aMap; std::map<string,double>::iterator it; Declaratie dictionar Declaratie iterator pe dictionar
Map-pair • pair este o clasa template care are doua date membre publice accesbile folosind pair::first (cheia), respectiv pair::second (valoarea) • Definita in <utility> • Pentru a crea un obiect de tip pair – functia template make_pair care are 2 parametri • Cerinte asupra tipului cheilor/valorilor – sa aiba operatorul = supraincarcat • Tipul cheilor trebuie sa respecte urmatoarele constrangeri (ordine stricta): • a<a e fals • Egalitatea se poate determina din expresia (!(a<b)&&!(b<a)) • Daca a<b si b<c, atunci a<c • Tipul valorilor trebuie sa aiba definit un constructor implicit • pair<string,double> p; • p = make_pair(string("Microsoft Share Price"), double(85.27));
Multimap • Permite existenta aceleiasi chei de mai multe ori • Pentru a gasi toate perechile cheie valoare corespunzatoare unei chei, vom folosi 2 iteratori-unul setat pe prima pereche din dictionar si altul setat pe ultima pereche din dictionar care corespunde cheii #include <map> multimap<string, int> mMap; multimap<string, int>::iterator itr; • multimap<string, int>::iterator itr; • multimap<string, int>::iterator lastElement; • itr = mMap.find(key); • if (itr == mMap.end()) return; • cout << "The following values are associated with the key " << key << endl; • lastElement = mMap.upper_bound(key); • for ( ; itr != lastElement; ++itr) cout << itr->second << endl;
Iteratori • Similari pointerilor in sensul ca elementele indicate sunt accesate indirect • o abstractizare intre container si utilizatorul sau (putem scrie algoritmi pe containere fara sa stim reprezentarea acestora) • Determina reducerea cuplarii (GRASP-low coupling) intre functii si containere accesate de acestea • Pentru a accesa elementul, iteratorul trebuie dereferentiat • Clase de iteratori: • Inainte (forward)-operator ++ - 3 categorii: • Input (read-only) • Output(write-only) • Random access (read/write) • Bidirectional – permit traversarea inainte/inapoi – operatorii ++/--, acces read/write • Acces aleator (random access) –permite acces la oricare element al containerului folosind operatorii +/- si ++/--
Iteratori • Majoritatea containerelor accepta iteratori, cu exceptia stivei, cozii si cozii cu prioritati • Parcurgerea elementelor containerului folosind iteratori: • Accesul la elementul curent: • Folosind * sau -> (daca obiectul referit este un agregat) • *itr=3; • struct pair { int first, second; }; • itr->first = 5; itr->second = 6; <container>::iterator <container>::const_iterator Declaratie iteratori • for (itr = container.begin(); itr!=container.end();++itr) • @proceseaza(*itr);
Container reversibil – produce iteratori care merg de la sfarsit spre inceput Toate containerele standard permit existenta iteratorilor reversibili Pentru initializare: rbegin(), rend() #include <vector> #include <iostream> using namespace std; int main(){ vector<int> v; v.push_back(3); v.push_back(4); v.push_back(5); vector<int>::reverse_iterator rit=v.rbegin(); while (rit<v.rend()) { cout<<*rit<<endl; ++rit; } return 0; } Iteratori pe containere reversibile <container>::reverse_iterator <container>::const_reverse_iterator 5 4 3
Destinati automatizarii unor sarcini comune (curente) STL furnizeaza metode de aplicare a unor algoritmi generici care sa inlocuiasca nevoia de a itera prin containere Un iterator pe stream foloseste un stream fie pentru intrare, fie pentru iesire ostream_iterator istream_iterator #include <iostream> #include <fstream> #include <iterator> #include <vector> using namespace std; int main () { vector<int> myvector; int value; cout << "Please, insert values: (CTRL+Z to stop) "; istream_iterator<int> eos; // end-of-stream iterator istream_iterator<int> iit (cin); // stdin iterator while (iit!=eos) { myvector.push_back(*iit); iit++; } ifstream fin("date.in"); istream_iterator<int> fiit (fin); //file iterator while (fiit!=eos) { myvector.push_back(*fiit); fiit++; } ostream_iterator<int> out_it (cout,", "); copy ( myvector.begin(), myvector.end(), out_it ); ofstream fout("date.out"); ostream_iterator<int> fout_it(fout,"|"); copy ( myvector.begin(), myvector.end(), fout_it ); return 0; } Iteratori pe streamuri
Iteratori de inserare (insertion/insert iterators) • x iterator • *x=3 • Daca x e un insert iterator *x=3 genereaza adaugarea elementului 3 la secventa pe care itereaza • Necesari unor algoritmi care au scopul de umplere a containerele, nu de suprascriere • insert_iterator – permite inserarea de elemente in mijlocul unei secvente • 2 clase adaptor: • front_inserter– containerul trebuie sa aiba metoda push_front • back_inserter - containerul trebuie sa aiba metoda push_back • Constructorii iau un container secvential (vector, list,deque) si produc un iterator care apeleaza push_back sau push_front Suprascrie valoarea existenta
#include <iostream> #include <iterator> #include <vector> using namespace std; int main () { vector<int> firstvector, secondvector; for (int i=1; i<=5; i++) { firstvector.push_back(i); secondvector.push_back(i*10); } back_insert_iterator< vector<int> > back_it (firstvector); copy (secondvector.begin(),secondvector.end(),back_it); ostream_iterator<int> out_it (cout,", "); copy ( firstvector.begin(), firstvector.end(), out_it ); return 0; } back_insert_iterator back_it 1 2 3 4 5 first 10 20 30 40 50 second 1, 2, 3, 4, 5, 10, 20, 30, 40, 50 first
front_insert_iterator #include <iostream> #include <iterator> #include <deque> using namespace std; int main () { deque<int> firstdeque, seconddeque; for (int i=1; i<=5; i++) { firstdeque.push_back(i); seconddeque.push_back(i*10); } front_insert_iterator< deque<int> > front_it (firstdeque); copy (seconddeque.begin(),seconddeque.end(),front_it); deque<int>::iterator it; ostream_iterator<int> oit(cout,","); copy(firstdeque.begin(),firstdeque.end(),oit); return 0; } front_it 1 2 3 4 5 firstdeque 10 20 30 40 50 seconddeque 50, 40, 30, 20, 10, 1, 2, 3, 4, 5 firstdeque
#include <iostream> #include <iterator> #include <list> using namespace std; int main () { list<int> firstlist, secondlist; for (int i=1; i<=5; i++) { firstlist.push_back(i); secondlist.push_back(i*10); } list<int>::iterator it; it = firstlist.begin(); advance (it,3); insert_iterator< list<int> > insert_it (firstlist,it); copy (secondlist.begin(),secondlist.end(),insert_it); for ( it = firstlist.begin(); it!= firstlist.end(); ++it ) cout << *it << " "; cout << endl; return 0; } insert_iterator it 1 2 3 4 5 firstlist 10 20 30 40 50 secondlist 1 2 3 10 20 30 40 50 4 5 firstlist
Algoritmi generici • Operatii care nu modifica secventa pe care opereaza • Operatii care modifica secventa pe care opereaza • Sortare • Cautare binara • Interclasare • Ansambluri (heap) • Min/max
Folosite pentru a transmite valori predicatelor la executie Obiectele de tip functie: abstractizare care permite comportamentul de functie Instanta a unei clase care supraincarca operatorul() (operatorul apel de functie) #include <iostream> using namespace std; class gt_n{ int val; public: gt_n(int v):val(v){} bool operator()(int n){return val>n;} }; int main(){ gt_n f(4); cout<<f(3)<<endl;//f.operator()(3); cout<<f(5)<<endl;//f.operator()(5); return 0; } Obiecte de tip functie
Clasificarea obiectelor de tip functie • Pe baza tipului returnat si a numarului de argumente ale operatorului () • Generator – obiect de tip functie care nu ia nici un parametru si returneaza o valoare de tip arbitrar (ex: rand(), <cstdlib>) • Functie unara – functie cu un parametru care returneaza un tip arbitrar(inclusiv void) • Functie binara - functie cu 2 parametri care returneaza un tip arbitrar(inclusiv void) • Predicat unar – functie unara care returneaza bool • Predicat binar – functie binara care returneaza bool • Ordonare stricta – predicat binar care permite o interpretare mai generala a egalitatii ( 2 elemente sunt egale daca nici unul nu e strict mai mic decat altul – nr reale) • LessThanComparable – clasa care are operatorul< • Assignable – clasa care are operatorul = • EqualityComparable – clasa care are operatorul ==
Operatii care nu modifica secventa • for_each • find/find_end/find_first/adjacent_find • equal • count/count_if • mismatch • search/search_n
aplica o functie data ca parametru pe fiecare element al secventei specificate template <class InputIterator, class Function> Function for_each (InputIterator first, InputIterator last, Function f); #include <iostream> #include <algorithm> #include <vector> using namespace std; void myfunction (int i) { cout << " " << i; } int main () { vector<int> myvector; myvector.push_back(10); myvector.push_back(20); myvector.push_back(30); cout << "Vectorul contine:"; for_each (myvector.begin(), myvector.end(), myfunction); return 0; } for_each
template <class InputIterator, class T> InputIterator find ( InputIterator first, InputIterator last, const T& value ); Returneaza un iterator pe primul element egal cu value, sau un iterator pe end(), in cazul in care nu gaseste elementul #include <iostream> #include <algorithm> #include <vector> using namespace std; int main () { int elems[] = { 10, 20, 30 ,40 }; vector<int> myvector(elems,elems+4); vector<int>::iterator it; it = find (myvector.begin(), myvector.end(), 30); ++it; cout << “The element following 30 is " << *it << endl; return 0; } find The element following 30 is 40
template <class InputIterator, class Predicate> InputIterator find_if ( InputIterator first, InputIterator last, Predicate pred ); Predicate – functie care are ca parametru un obiect de tipul template-ului si returneaza o valoare booleana Gaseste un element in domeniul [first, last) care sa respecte conditia specificata de pred si returneaza un iterator pe element, altfel returneaza un iterator pe end() #include <iostream> #include <algorithm> #include <vector> using namespace std; bool positive (int i) { return (i>0);} int main () { vector<int> myvector; vector<int>::iterator it; myvector.push_back(-10); myvector.push_back(-95); myvector.push_back(40); myvector.push_back(-55); it = find_if (myvector.begin(), myvector.end(), positive); cout << "The first positive value" << *it << endl; return 0; } find_if predicat The first positive value 40
Alte variatii find • find_end – gaseste ultima aparitie a unei secvente in alta secventa si returneaza un iterator • find_first_of – gaseste prima aparitie a oricarui element dintr-o secventa in alta secventa • adjacent_find – cauta prima aparitie a doua valori consecutive egale intr-o secventa si returneaza un iterator la ea
Verifica daca elementele din doua domenii sunt egale facand compararea pe perechi de elemente corespunzatoare template <class InputIterator1, class InputIterator2> bool equal ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2); template <class InputIterator1, class InputIterator2, class BinaryPredicate> bool equal ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, BinaryPredicate pred); #include <iostream> #include <algorithm> #include <vector> using namespace std; bool mypredicate (int i, int j) { return (i==j);} int main () { int myints[] = {20,40,60,80,100}; vector<int>myvector (myints,myints+5); if (equal (myvector.begin(), myvector.end(), myints)) cout << "The contents of both sequences are equal." << endl; else cout << "The contents of both sequences differ." << endl; myvector[3]=81; if (equal (myvector.begin(), myvector.end(), myints, mypredicate)) cout << "The contents of both sequences are equal." << endl; else cout << "The contents of both sequences differ." << endl; return 0; } equal The contents of both sequences are equal. The contents of both sequences differ.
Returneaza numarul de aparitii ale unui element intr-o secventa sau / numarul elementelor pentru care o conditie este adevarata template <class InputIterator, class T> typename iterator_traits<InputIterator>::difference_type count ( ForwardIterator first, ForwardIterator last, const T& value ); template <class InputIterator, class Predicate> typename iterator_traits<InputIterator>::difference_type count_if ( ForwardIterator first, ForwardIterator last, Predicate pred ); #include <iostream> #include <algorithm> #include <vector> using namespace std; bool positive(int i) { return (i>0); } int main () { vector<int> myvector; myvector.push_back(-10); myvector.push_back(-95); myvector.push_back(40); myvector.push_back(-55); int nrPos =(int) count_if (myvector.begin(), myvector.end(), positive); cout << "The no of positive values is " << nrPos << endl; return 0; } count/count_if -10 -95 40 -55 The no of positive values is 1
mismatch • Compara doua secvente si returneaza pozitia primei diferente #include <iostream> #include <algorithm> #include <vector> using namespace std; bool mypredicate (int i, int j) {return (i==j);} int main () { vector<int> myvector; for (int i=1; i<6; i++) myvector.push_back (i*10); int myints[] = {10,20,80,320,1024}; pair<vector<int>::iterator,int*> mypair; mypair = mismatch (myvector.begin(), myvector.end(), myints); cout << "First mismatching elements: " << *mypair.first; cout << " and " << *mypair.second << endl;; mypair.first++; mypair.second++; mypair = mismatch (mypair.first, myvector.end(), mypair.second, mypredicate); cout << "Second mismatching elements: " << *mypair.first; cout << " and " << *mypair.second << endl;; return 0; } Comparatie implicita (==) Comparatie cu predicat First mismatching elements: 30 and 80 Second mismatching elements: 40 and 320
Cauta prima aparitie a unei secvente in alta secventa si returneaza un iterator pe primul element al secventei (comparatia se face folosind == sau predicat) template <class ForwardIterator1, class ForwardIterator2> ForwardIterator1 search ( ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2 ); template <class ForwardIterator1, class ForwardIterator2, class BinaryPredicate> ForwardIterator1 search ( ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2. BinaryPredicate pred ); #include <iostream> #include <algorithm> #include <vector> using namespace std; bool mypredicate (int i, int j) { return (i==j); } int main () { vector<int> myvector; vector<int>::iterator it; for (int i=1; i<10; i++) myvector.push_back(i*10); // using default comparison: int match1[] = {40,50,60,70}; it = search (myvector.begin(), myvector.end(), match1, match1+4); if (it!=myvector.end()) cout << "match1 found at position " << int(it-myvector.begin()) << endl; else cout << "match1 not found" << endl; // using predicate comparison: int match2[] = {20,30,50}; it = search (myvector.begin(), myvector.end(), match2, match2+3, mypredicate); if (it!=myvector.end()) cout << "match2 found at position " << int(it-myvector.begin()) << endl; else cout << "match2 not found" << endl; return 0; } search 10 20 30 40 50 60 70 80 90 40 50 60 70 20 30 50 match1 found at position 3 match2 not found