Laborator 11 - Standard Template Library (STL)

Autor: Răzvan Cristea

Obiective Specifice

Studentul va fi capabil la finalul acestui laborator să:

  • înțeleagă importanța conceptului de programare modernă în C++ și avantajele oferite de utilizarea bibliotecii STL
  • recunoască și să explice clasele și componentele principale din biblioteca STL
  • utilizeze eficient diverse structuri de date și algoritmi disponibili în STL pentru rezolvarea unor probleme concrete
  • aplice principiile și cunoștințele dobândite anterior în programare pentru a dezvolta soluții robuste și eficiente folosind STL
  • identifice și să utilizeze algoritmi standard din STL pentru a optimiza procesele și pentru a simplifica implementarea unor operații complexe

Introducere

Până acum am învățat să scriem clase, fiecare clasă având un scop bine definit în cadrul unui program, reprezentând fundația pentru o organizare structurată a codului. De asemenea, am aprofundat utilizarea claselor template, care se dovedesc a fi extrem de utile în contextul programării generice, permițându-ne să creăm structuri de date și algoritmi care nu depind de tipul de date.

Toate aceste cunoștințe fundamentale despre programarea orientată obiect și programarea generică ne conduc către un pas natural și esențial în dezvoltarea aplicațiilor moderne și anume utilizarea bibliotecii Standard Template Library (STL). STL reprezintă o colecție puternică și versatilă de structuri de date, algoritmi și funcționalități reutilizabile, bazată pe conceptele de template-uri și moștenire.

Astfel, ceea ce am învățat până acum devine indispensabil pentru a înțelege și folosi corect STL-ul. Vom vedea cum principiile fundamentale ale POO și programării generice sunt integrate în STL, oferindu-ne instrumente eficiente pentru a rezolva probleme complexe într-un mod elegant și intuitiv. Acest laborator reprezintă o continuare logică a ceea ce am studiat până acum, punând în practică toate cunoștințele dobândite.

Scurt istoric

Standard Template Library a apărut în 1994, fiind creată de Alexander Stepanov și Meng Lee. Inițial, STL-ul a fost dezvoltat ca o bibliotecă independentă, iar în 1998, odată cu standardizarea limbajului C++ (ISO/IEC 14882:1998), STL-ul a fost inclus oficial ca parte a bibliotecii standard din limbajul C++.

Această introducere a STL-ului a reprezentat un moment revoluționar în istoria limbajului, oferind programatorilor un set bogat de structuri de date generice (precum vectori, liste, seturi, și map-uri), algoritmi și funcționalități asociative, toate bazate pe concepte precum template-uri și iteratori.

De atunci, STL-ul a evoluat și a fost extins în versiunile ulterioare ale standardului C++ (C++11, C++14, C++17, C++20, etc.), fiind integrată cu noile caracteristici ale limbajului.

De ce să folosim STL?

Utilizarea bibliotecii STL în programele noastre oferă multiple avantaje care fac dezvoltarea mai eficientă, clară și profesională:

Evitarea reinventării roții

În loc să pierdem timp implementând de la zero structuri de date și algoritmi bine-cunoscuți, putem folosi implementările standard oferite de STL. Aceste implementări sunt create de experți, sunt testate riguros și sunt optimizate pentru performanță. Astfel, putem să ne concentrăm pe logica specifică a aplicației noastre, lăsând în seama STL-ului gestionarea eficientă a colecțiilor și algoritmilor.

Claritate și concizie în cod

Folosind STL-ul, codul nostru devine mai scurt și mai expresiv. În loc să definim structuri complexe sau să scriem algoritmi în detaliu, putem apela direct funcționalități standardizate. Acest lucru face ca programul să fie mai ușor de citit și de întreținut, permițând altor programatori să înțeleagă rapid intențiile noastre. Practic, biblioteca STL promovează un stil de programare modern, care pune accent pe lizibilitate și simplitate.

Pe scurt, STL-ul ne ajută să scriem un cod robust, eficient și ușor de înțeles, economisind timp prețios în procesul de dezvoltare. Totuși trebuie menționat faptul că o bună cunoaștere a conceptului de structuri de date și algoritmi va simplifca masiv înțelegerea modului în care componentele acestei biblioteci funcționează.

Alcătuirea bibliotecii standard din C++

Biblioteca standard C++ (Standard C++ Library) cuprinde toate bibliotecile standard C precum si biblioteci dedicate C++ (ex: STL).

Ca o scurtă alcătuire această bibliotecă cuprinde:

  • clasa string - destinată lucrului cu șiruri de caractere (în general procesarea datelor sub formă de text).
  • clase pentru procesarea fluxurilor de date - istream, ostream, ifstream, ofstream, etc.
  • STL - clase template (containere, algoritmi, iteratori).

În continuare vom prezenta diferite moduri de utilizare a câtorva clase din librăria standard pentru a înțelege conceptul de programare modernă în limbajul C++.

Clasa string

Așa cum am menționat mai sus clasa string este o clasă specială în C++ pentru lucrul cu șiruri de caractere. Această clasă a fost introdusă pentru a simplifca masiv lucrul cu datele de tip char*. Un string își actualizează dimensiunea dinamic în funcție de ce operații suferă iar la final, când durata de viață a acestuia se încheie, este chemat automat destructorul care dezalocă automat memoria.

În continuare vom prezenta un tabel unde sunt descrise principalele metode și operatori care există în clasa string.

Denumire metodă / Operator Descriere
append() Adaugă un șir la sfârșitul stringului curent.
insert() Inserează un șir sau un caracter la o anumită poziție în string.
erase() Șterge o secțiune din string, specificată prin poziție și lungime.
replace() Înlocuiește o secțiune din string cu un alt șir.
substr() Returnează un substring pe baza unei poziții de început și a unei lungimi.
find() Găsește prima apariție a unui șir sau caracter și returnează poziția acestuia.
rfind() Găsește ultima apariție a unui șir sau caracter și returnează poziția acestuia.
compare() Compară stringul curent cu un alt string și returnează o valoare specifică.
empty() Verifică dacă stringul curent este gol.
size() / length() Returnează lungimea stringului curent.
clear() Șterge conținutul stringului, făcându-l gol.
resize() Modifică dimensiunea stringului, adăugând sau eliminând caractere.
at() Returnează caracterul de la o poziție specifică (cu verificare de limite).
operator= Atribuie un string altui string.
operator[ ] Returnează caracterul de la o poziție specifică (fără verificare de limite).
operator+ Concatenează două stringuri sau un string și un șir de caractere.
operator+= Adaugă un string sau un șir de caractere la stringul curent.
operator==, !=, <, >, <=, >= Comparații lexicografice între stringuri.
operator>>, << Citire și scriere pe bază de fluxuri.
c_str() Returnează un pointer la un șir de caractere constant (stil C).
data() Similar cu c_str(), dar poate include caractere nule interne.
find_first_of() Caută primul caracter dintr-o listă de caractere specificată.
find_last_of() Caută ultimul caracter dintr-o listă de caractere specificată.
find_first_not_of() Găsește prima poziție cu un caracter care nu face parte dintr-o listă specificată.
find_last_not_of() Găsește ultima poziție cu un caracter care nu face parte dintr-o listă specificată.

Pe lângă tabelul de mai sus trebuie menționat faptul că în clasa string există și implementări pentru funcții membre precum: constructori și destructor.

În continuare vom prezenta un scurt exemplu de utilizare a acestei clase în C++. Pentru aceasta trebuie să includem fișierul corespunzător pentru această clasă și anume #include <string>.

#include <string>
#include <iostream>
 
int main()
{
	std::string text = "Salut";
	std::cout << text << '\n';
 
	text += " sunt un string.";
	std::cout << text << '\n';
 
	std::cout << "Al patrulea caracter din string este: " << text[3] << '\n';
	std::cout << "Al patrulea caracter din string este: " << text.at(3) << '\n';
	std::cout << "Lungimea stringului este: " << text.length() << '\n';
 
	std::string sir = "Acum am continutul altui string";
	text = sir;
 
	if (text == sir)
	{
		std::cout << "Stringuri identice\n";
	}
	else
	{
		std::cout << "Stringuri diferite\n";
	}
 
	sir.clear();
 
	if (sir.empty())
	{
		std::cout << "Variabila sir este goala\n";
	}
 
	std::cout << text << '\n';
	text.clear();
	std::cout << text << '\n';
 
	return 0;
}

Dacă vom citi un string cu operatorul >> trebuie menționat faptul că acesta va fi citit până la apariția primului spațiu, similar cu ceea ce se întâmpla în cazul șirurilor de caractere din limbajul C atunci când erau citite cu funcția scanf.

Pentru a citi de la tastatură sau din fișier un string până la apariția caracterului \n ne putem folosi de funcția getline.

#include <string>
#include <iostream>
 
int main()
{
	std::string sir;
 
	std::cout << "Introduceti o propozitie: ";
	std::cin >> sir;
	std::cout << "Propozitia introdusa este: " << sir << "\n\n";
 
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // necesar pentru a goli buffer-ul
 
	std::cout << "Introduceti o propozitie: ";
	std::getline(std::cin, sir);
	std::cout << "Propozitia introdusa este: " << sir << '\n';
 
	return 0;
}

Pentru toate metodele și operatorii puși la dispoziție de clasa string recomandăm citirea documentației oficiale care se poate găsi chiar aici.

Standard Template Library

În continuare vom explora câteva din clasele pe care STL-ul le pune la dispoziție. Dar înainte de a trece la acestă parte propunem un tabel cu principalele structuri de date pe care le găsim în această colecție.

Structura de Date Numele STL #include
Vector std::vector <vector>
Listă dublu înlănțuită std::list <list>
Deque (coadă cu 2 capete) std::deque <deque>
Stivă std::stack <stack>
Coadă std::queue <queue>
Coadă cu priorități std::priority_queue <queue>
Set std::set <set>
Multiset std::multiset <set>
Map std::map <map>
Multimap std::multimap <map>
Unordered Set std::unordered_set <unordered_set>
Unordered Multiset std::unordered_multiset <unordered_set>
Unordered Map std::unordered_map <unordered_map>
Unordered Multimap std::unordered_multimap <unordered_map>
Pereche std::pair <utility>
Array fix std::array <array>

Toate structurile de date prezentate în tabelul de mai sus în STL poartă denumirea de containere. Aceste containtere sunt de mai multe tipuri după cum urmează în descrierea de mai jos.

Containere Secvențiale (Sequence Containers)

Containerele secvențiale sunt structuri de date care păstrează o succesiune de elemente de același tip T. Fiecare element din container are o poziție specifică, iar ordinea elementelor este determinată de ordinea în care au fost inserate în container. Aceste containere permit accesul la elemente într-un mod secvențial, pe baza poziției lor în container.

Există mai multe tipuri de containere secvențiale în C++:

  • std::vector: Un container de tip vector care stochează elemente într-o secvență contiguă de memorie, oferind acces rapid la elemente pe bază de index (O(1) pentru accesul la elemente). Este ideal pentru manipularea unui număr variabil de elemente, având o performanță bună la accesul aleatoriu.
  • std::deque (Double Ended Queue): Permite adăugarea și eliminarea elementelor atât la început, cât și la sfârșit, având acces rapid la ambele capete ale secvenței.
  • std::list: Un container bazat pe o listă dublu înlănțuită, care permite adăugarea și ștergerea rapidă a elementelor la orice poziție din listă (O(1) pentru inserare/ștergere, O(n) pentru accesul la elemente).
  • C++11: std::array: Un container care stochează un număr fix de elemente într-un bloc contigu de memorie, fiind similar cu un tablou static, dar cu funcționalități suplimentare specifice STL-ului.
  • C++11: std::forward_list: O listă simplu înlănțuită care economisește memorie față de std::list, fiind mai eficientă pentru inserări și ștergeri la începutul listei, dar fără acces direct la elemente intermediare.

Adaptoare de Containere (Container Adaptors)

Adaptoarele de containere sunt clase care oferă o interfață specifică pentru manipularea unui container de bază, de obicei un container secvențial sau asociativ. Acestea modifică comportamentul containerelor tradiționale pentru a răspunde unor nevoi particulare, cum ar fi accesul într-un mod specific sau implementarea unor tipuri de structuri de date cu comportamente specifice, cum ar fi stive, cozi sau cozi cu prioritate.

Cele mai frecvente adaptoare de containere sunt:

  • std::stack: O structură de date de tip LIFO (Last In, First Out) care permite doar accesul la elementul din vârful stivei. Operațiile permise sunt doar push, pop și accesul la vârful stivei.
  • std::queue: O structură de date de tip FIFO (First In, First Out) care permite inserarea de elemente la sfârșitul cozii și eliminarea elementelor de la începutul acesteia.
  • std::priority_queue: O structură de date care implementează o coadă cu priorități, în care elementele sunt accesate în funcție de prioritatea lor (nu în ordinea în care au fost adăugate). De obicei, elementele sunt stocate într-un heap (min-heap sau max-heap).

Containere Asociative (Associative Containers)

Containerele asociative stochează colecții de elemente în care poziția fiecărui element depinde de valoarea sa, conform unui criteriu de sortare specificat. Aceste containere permit acces rapid la elemente prin intermediul unei chei unice, ceea ce le face ideale pentru situațiile în care este necesară găsirea rapidă a unui element pe baza unei valori asociate (cheia). În general, operarea pe aceste containere are un timp de complexitate logaritmică O(log n) datorită implementării lor, adesea printr-un arbore binar de căutare echilibrat (AVL).

Containerele asociative din STL includ:

  • std::set: Un container care stochează elemente unice într-o ordine definită de o funcție de comparare (implicit, operatorul < este folosit). Nu permite duplicarea elementelor.
  • std::multiset: Similar cu std::set, dar permite stocarea de elemente duplicate. De asemenea, elementele sunt sortate într-o ordine specificată.
  • std::map: Un container care stochează perechi de chei și valori unice, iar cheia este folosită pentru a accesa valoarea corespunzătoare. Permite căutări rapide pe baza cheii.
  • std::multimap: Similar cu std::map, dar permite asocierea mai multor valori cu aceeași cheie.

Containere Asociative Neordonate (Unordered Associative Containers)

Introduse în C++11, containerele asociative neordonate stochează elemente care sunt accesibile rapid prin chei, dar nu sunt sortate într-o ordine specifică. În schimb, aceste containere folosesc o funcție de hash pentru a organiza elementele. Astfel, accesul la elemente este efectuat pe baza unui hash, ceea ce face căutările, inserările și ștergerile mult mai rapide (în medie O(1), dar în cel mai rău caz O(n)).

Containerele asociative neordonate includ:

  • std::unordered_set: Un container care stochează elemente unice, dar care nu garantează vreo ordine între ele. Cheia este folosită pentru a determina poziția elementului.
  • std::unordered_multiset: Similar cu std::unordered_set, dar permite elemente duplicate.
  • std::unordered_map: Similar cu std::map, dar elementele nu sunt ordonate. Stochează perechi cheie-valoare și permite acces rapid la valoare pe baza cheii.
  • std::unordered_multimap: Similar cu std::unordered_map, dar permite asocierea mai multor valori cu aceeași cheie.

Acum că avem clar în minte ce reprezintă fiecare tip de container putem trece la exemple concrete cu câteva dintre aceste clase template.

Clasa vector

Pentru clasa vector propunem următorul tabel ca și referință:

Denumire metodă/Operator Descriere
push_back() Adaugă un element la sfârșitul vectorului.
pop_back() Elimină ultimul element din vector.
at() Returnează elementul de la o anumită poziție, cu verificare de limite.
operator[ ] Returnează elementul de la o anumită poziție, fără verificare de limite.
size() Returnează numărul curent de elemente din vector.
capacity() Returnează capacitatea totală a vectorului, adică numărul maxim de elemente pe care le poate stoca fără a realoca memorie.
empty() Verifică dacă vectorul este gol.
resize() Schimbă dimensiunea vectorului, adăugând sau eliminând elemente.
reserve() Modifică capacitatea minimă a vectorului, fără a-i schimba dimensiunea curentă.
clear() Șterge toate elementele din vector.
insert() Inserează unul sau mai multe elemente la o poziție specificată.
erase() Elimină unul sau mai multe elemente din vector.
front() Returnează o referință la primul element al vectorului.
back() Returnează o referință la ultimul element al vectorului.
begin() Returnează un iterator la primul element din vector.
end() Returnează un iterator la poziția de după ultimul element al vectorului.
rbegin() Returnează un iterator invers la ultimul element din vector.
rend() Returnează un iterator invers la poziția dinaintea primului element din vector.
emplace_back() Construiește un element direct la sfârșitul vectorului, evitând copierea.
emplace() Construiește un element la o poziție specificată din vector.
shrink_to_fit() Redimensionează capacitatea vectorului pentru a se potrivi exact cu dimensiunea curentă.
assign() Înlocuiește toate elementele vectorului cu un anumit număr de copii ale unui element sau cu o altă secvență de elemente.
swap() Schimbă conținutul vectorului cu cel al unui alt vector.

Iar ca și exemplu simplu de utilizare a clasei vector propunem următorul bloc de cod.

#include <vector>
#include <iostream>
 
int main()
{
	std::vector<int> numere;
 
	numere.push_back(5);
	numere.push_back(10);
	numere.push_back(1);
 
	for (int i = 0; i < numere.size(); i++)
	{
		std::cout << numere[i] << ' ';
	}
 
	numere.pop_back();
 
	std::cout << '\n';
 
	for (int i = 0; i < numere.size(); i++)
	{
		std::cout << numere[i] << ' ';
	}
 
	std::vector<int> copie;
 
	copie.swap(numere);
	copie.push_back(100);
	copie.push_back(20);
 
	std::cout << '\n';
 
	for (int i = 0; i < copie.size(); i++)
	{
		std::cout << copie[i] << ' ';
	}
 
	copie.clear();
 
	return 0;
}

Pentru toate metodele și operatorii puși la dispoziție de clasa vector recomandăm citirea documentației oficiale care se poate găsi chiar aici.

Clasa list

Pentru clasa list propunem următorul tabel ca și referință.

Denumire metodă Descriere
push_back() Adaugă un element la sfârșitul listei.
push_front() Adaugă un element la începutul listei.
pop_back() Elimină ultimul element din listă.
pop_front() Elimină primul element din listă.
size() Returnează numărul de elemente din listă.
empty() Verifică dacă lista este goală.
clear() Șterge toate elementele din listă.
front() Returnează o referință la primul element din listă.
back() Returnează o referință la ultimul element din listă.
insert() Inserează unul sau mai multe elemente la o poziție specificată.
erase() Elimină unul sau mai multe elemente din listă.
begin() Returnează un iterator la primul element din listă.
end() Returnează un iterator la poziția de după ultimul element al listei.
rbegin() Returnează un iterator invers la ultimul element al listei.
rend() Returnează un iterator invers la poziția dinaintea primului element din listă.
emplace_back() Construiește un element direct la sfârșitul listei, evitând copierea.
emplace_front() Construiește un element direct la începutul listei, evitând copierea.
emplace() Construiește un element la o poziție specificată din listă.
assign() Înlocuiește conținutul listei cu un anumit număr de copii ale unui element sau cu o altă secvență de elemente.
remove() Elimină toate elementele egale cu o anumită valoare.
remove_if() Elimină toate elementele care satisfac o anumită condiție.
sort() Sortează lista în ordine crescătoare (implică compararea elementelor).
reverse() Inversează ordinea elementelor din listă.
merge() Combină două liste sortate într-una singură.
splice() Mută elemente dintr-o listă în alta la o poziție specificată.
unique() Elimină elementele duplicate consecutive.
swap() Schimbă conținutul listei cu cel al unei alte liste.

Iar acum pentru o mai bună înțelegere propunem următorul exemplu.

#include <list>
#include <iostream>
 
int main()
{
	std::list<int> lista;
 
	lista.push_back(-1);
	lista.push_back(2);
	lista.push_back(0);
	lista.push_back(4);
	lista.push_front(10);
	lista.push_front(22);
 
	for (const int& element : lista) // bucla de tip foreach
	{
		std::cout << element << ' ';
	}
 
	lista.push_front(90);
	lista.pop_back();
 
	// parcurgerea listei de la cap la coada
 
	std::list<int>::iterator it; // declaram un iterator de tipul listei pe care o parcurgem
 
	std::cout << '\n';
 
	for (it = lista.begin(); it != lista.end(); it++)
	{
		std::cout << *it << ' ';
	}
 
	// parcurgerea listei de la coada la cap
 
	std::list<int>::reverse_iterator rit; // declaram un iterator invers de tipul listei pe care o parcurgem
 
	std::cout << '\n';
 
	for (rit = lista.rbegin(); rit != lista.rend(); rit++)
	{
		std::cout << *rit << ' ';
	}
 
	lista.clear();
 
	return 0;
}

O listă nu are operatorul de indexare ([ ]) implementat, prin urmare pentru a o putea parcurge avem ca și variante bucla de tip foreach care este o buclă unidirecțională (valabil acest lucru pentru toate structurile de date din STL) și respectiv parcurgerea cu ajutorul iteratorilor. Lista din STL are iteratori bidirecționali ceea ce înseamnă că poate fi parcursă în ambele sensuri.

Pentru toate metodele și operatorii puși la dispoziție de clasa list recomandăm citirea documentației oficiale care se poate găsi chiar aici.

Clasele stack și queue

Pentru clasa stack propunem următorul tabel ca și referință.

Denumire metodă Descriere
push() Adaugă un element în vârful stivei.
pop() Elimină elementul din vârful stivei.
top() Returnează o referință la elementul din vârful stivei, fără a-l elimina.
size() Returnează numărul de elemente din stivă.
empty() Verifică dacă stiva este goală.
emplace() Construiește un element direct în vârful stivei, evitând copierea.
swap() Schimbă conținutul stivei cu cel al unei alte stive.

Iar pentru clasa queue propunem tabelul de mai jos.

Denumire metodă Descriere
push() Adaugă un element la sfârșitul cozii.
emplace() Construiește un element direct la sfârșitul cozii, evitând copierea.
pop() Elimină elementul din fața cozii.
front() Returnează o referință la elementul din fața cozii, fără a-l elimina.
back() Returnează o referință la elementul de la sfârșitul cozii.
size() Returnează numărul de elemente din coadă.
empty() Verifică dacă coada este goală.
swap() Schimbă conținutul cozii cu cel al unei alte cozi.

Iar ca și exemple de utilizare a acestor clase propunem următorul bloc de cod.

#include <stack>
#include <queue>
#include <string>
#include <iostream>
 
int main()
{
	std::queue<float> coada;
	std::stack<std::string> stiva;
 
	stiva.push("Raul");
	stiva.push("Alex");
	stiva.push("Ioana");
 
	coada.push(-5.5f);
	coada.push(10.23f);
	coada.push(22.0f);
 
	while (!stiva.empty())
	{
		std::cout << stiva.top() << ' ';
		stiva.pop();
	}
 
	std::cout << '\n';
 
	while (!coada.empty()) 
	{
		std::cout << coada.front() << ' ';
		coada.pop();
	}
 
	return 0;
}

Clasele stack, queue și respectiv priority_queue nu au iteratori puși la dispoziție deoarce singurele operații permise asupra acestor structuri de date sunt cele de push și pop.

Pentru mai multe detalii despre clasele stack, queue și priority_queue recomandăm citirea următoarelor documentații oficiale:

  • pentru stivă puteți citi de aici;
  • pentru coadă puteți citi de aici;
  • pentru coada de priorități puteți citi de aici.

Clasele map și set

Pentru clasa map propunem următorul tabel ca și referință.

Denumire metodă/Operator Descriere
operator[ ] Accesează sau inserează un element cu cheia specificată.
at() Returnează o referință la valoarea asociată unei chei, aruncând o excepție dacă cheia nu există.
insert() Inserează un element (pereche cheie-valoare) în map.
emplace() Construiește un element direct în map, evitând copierea.
erase() Elimină un element din map pe baza cheii sau a unui iterator.
find() Returnează un iterator către elementul cu cheia specificată sau end() dacă nu este găsit.
count() Returnează 1 dacă cheia există în map, altfel 0.
size() Returnează numărul de elemente din map.
empty() Verifică dacă map-ul este gol.
clear() Elimină toate elementele din map.
swap() Schimbă conținutul map-ului cu cel al unui alt map.
lower_bound() Returnează un iterator către primul element care nu este mai mic decât cheia specificată.
upper_bound() Returnează un iterator către primul element care este mai mare decât cheia specificată.

Iar pentru clasa set propunem tabelul de mai jos.

Denumire metodă Descriere
insert() Inserează un element în set, păstrând ordinea și unicitatea.
emplace() Construiește un element direct în set, evitând copierea.
erase() Elimină un element din set pe baza valorii sau a unui iterator.
find() Returnează un iterator către elementul specificat sau end() dacă nu este găsit.
count() Returnează 1 dacă elementul există în set, altfel 0.
size() Returnează numărul de elemente din set.
empty() Verifică dacă set-ul este gol.
clear() Elimină toate elementele din set.
swap() Schimbă conținutul set-ului cu cel al unui alt set.
lower_bound() Returnează un iterator către primul element care nu este mai mic decât valoarea specificată.
upper_bound() Returnează un iterator către primul element care este mai mare decât valoarea specificată.
equal_range() Returnează o pereche de iteratoare care delimitează intervalul elementelor egale cu o valoare specificată.

Iar ca și exemple de utilizare a acestor două clase propunem următorul bloc de cod.

#include <map>
#include <set>
#include <string>
#include <iostream>
 
int main()
{
	std::map<int, std::string> mapa;
 
	mapa[1] = "Unu";
	mapa[2] = "Doi";
	mapa[3] = "Trei";
 
	std::cout << "Elementele din mapa sunt afisate mai jos\n";
 
	for (const std::pair<const int, std::string>& pair : mapa)
	{
		std::cout << "Cheie: " << pair.first << ", Valoare: " << pair.second << '\n';
	}
 
	int keyToFind = 2;
 
	if (mapa.find(keyToFind) != mapa.end())
	{
		std::cout << "Cheia " << keyToFind
			<< " exista si valoarea asociata este: " << mapa[keyToFind] << '\n';
	}
	else
	{
		std::cout << "Cheia " << keyToFind << " nu exista.\n";
	}
 
	mapa.erase(2);
 
	std::cout << "Mapa dupa stergerea cheii 2\n";
 
	for (const auto& pair : mapa)
	{
		std::cout << "Cheie: " << pair.first << ", Valoare: " << pair.second << '\n';
	}
 
	std::set<int> arbore;
 
	arbore.insert(10);
	arbore.insert(20);
	arbore.insert(30);
	arbore.insert(20); // Ignorat automat deoarece 20 deja exista
 
	std::cout << "\nElementele din set sunt afisate mai jos.\n";
 
	for (int value : arbore)
	{
		std::cout << value << ' ';
	}
 
	std::cout << '\n';
 
	int valueToFind = 20;
 
	if (arbore.find(valueToFind) != arbore.end())
	{
		std::cout << "Valoarea " << valueToFind << " exista în set.\n";
	}
	else
	{
		std::cout << "Valoarea " << valueToFind << " nu exista în set.\n";
	}
 
	arbore.erase(10);
 
	std::cout << "Set-ul dupa stergerea valorii 10:\n";
 
	for (const int& value : arbore)
	{
		std::cout << value << ' ';
	}
 
	std::cout << '\n';
 
	mapa.clear();
	arbore.clear();
 
	return 0;
}

Pentru mai multe detalii despre clasele map și set recomandăm citirea următoarelor documentații oficiale:

  • pentru mapă puteți citi de aici;
  • pentru set puteți citi de aici;

Algoritmi

Pentru a folosi algoritmii din STL vom include fișierul antet algorithm. Aceast fișier cuprinde algoritmi de sortare, căutare, numărare și mulți alții pe care nu mai trebuie să îi rescriem noi de la zero. Propunem un exemplu simplu de utlizare a funcțiilor sort și count în următorul bloc de cod C++.

#include <vector>
#include <iostream>
#include <algorithm>
 
int main()
{
	std::vector<int> numere;
 
	numere.push_back(100);
	numere.push_back(5);
	numere.push_back(2);
	numere.push_back(15);
	numere.push_back(22);
	numere.push_back(6);
	numere.push_back(8);
	numere.push_back(18);
	numere.push_back(27);
	numere.push_back(18);
 
	std::sort(numere.begin(), numere.end()); // se sorteaza vectorul crescator
	std::cout << "Vectorul sortat crescator este: ";
 
	for (const int& numar : numere)
	{
		std::cout << numar << ' ';
	}
 
	int target = 18; // valoarea careia vrem sa ii aflam numarul de aparitii din vector
 
	std::cout << "\nNumarul de aparitii pentru " << target << " este: " 
		<< std::count(numere.begin(), numere.end(), target) << '\n'; // se numara de cate ori apare in vector valoarea variabilei target
 
	return 0;
}

Pentru mai mulți algoritmi puși la dispoziție de această librarie recomandăm citirea documentației oficiale care se află chiar aici.

Concluzii

În acest laborator am înțeles importanța fundamentelor solide în programare, pe care le-am construit pe parcursul a aproape trei semestre. Aceste cunoștințe ne permit acum să lucrăm eficient cu biblioteca STL, aplicând conceptele și tehnicile învățate anterior fără a fi nevoie să reinventăm algoritmi sau să implementăm structuri de date de la zero.

Am explorat principalele clase și metode din STL, precum std::vector, std::list, std::stack, std::queue, std::map și std::set. Am analizat modul în care acestea sunt proiectate pentru a oferi eficiență și claritate în rezolvarea diverselor probleme. Înțelegerea și utilizarea corectă a STL-ului ne permit să scriem cod mai concis, mai lizibil și mai ușor de întreținut, păstrând în același timp performanța.

Un punct cheie al laboratorului a fost reprezentat de analiza modului în care biblioteca STL îmbină conceptul de programare generică cu cel de eficiență. Am discutat avantajele utilizării STL-ului, precum reutilizarea codului bine testat și optimizat. De asemenea, am evidențiat cum se pot rezolva probleme mai complexe folosind STL-ul, pornind de la manipularea colecțiilor de date până la utilizarea algoritmilor predefiniți.

Acest laborator ne-a oferit o perspectivă practică asupra puterii STL-ului, consolidând în același timp legătura dintre cunoștințele teoretice acumulate anterior și aplicarea lor eficientă în rezolvarea problemelor. Pe scurt, am făcut un pas important spre a scrie un cod mai bun, bazat pe principiile programării moderne în C++.

poo-is-ab/laboratoare/11.txt · Last modified: 2025/01/19 22:30 by razvan.cristea0106
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0