Standard Template Library - „Knihovna standardních šablon“ Knihovna STL Standard Template Library - „Knihovna standardních šablon“
Struktura knihovny Kontejnery Iterátory Utility (nástroje) Algoritmy Zpracování chyb Řetězce Vstup/výstup Lokalizace Jazyková podpora Numerické výpočty
Kontejnery Kontejnery dovolují ukládání libovolného počtu hodnot typu T Protože prvky se do kontejneru ukládají celé (kopií parametrů - ne referencí), musí typ T obsahovat tyto metody: implicitní konstruktor 'copy' konstruktor operator= destruktor porovnání (jen pro map, set atd...) kontejnery poskytují množinu standardních operací přístup k prvkům uloženým v kontejneru je unifikován přes tzv. iterátory - tj. třídy, jejichž funkce je analogií ukazatelům. Protože iterátory pro práci se všemi kontejnery mají stejný typ, mohou jejich prostřednictvím algoritmy z STL (např. find, qsort) operovat efektivně s libovolným kontejnerem bez nutnosti volat specifické metody kontejneru
Typy kontejnerů v STL STL definuje tyto kontejnery: <vector> - jednorozměrné pole objektů typu T <list> - dvojsměrně vázaný seznam <deque> - fronta se dvěma konci <queue> - fronta(queue, priority_queue) <stack> - zásobník <map> - asociativní pole, slovník(map, multimap) <set> - množina (set, multiset) <bitset> - množina bool
<vector> Vector je dynamické pole, ve kterém jsou prvky uloženy v souvislém bloku paměti (rychlý přímý přístup). Pole můžeme podle potřeby zmenšovat/zvětšovat, ale pozor - tyto operace obnášejí alokaci nové paměti, kopírování starých prvků do této paměti a uvolnění staré paměti. Metody třídy Vector: push_back(...), pop_back() - přidání/odebrání prvku na konec insert(...) - vloží nové prvky před prvek, na který ukazuje iterátor (1. param.) erase() - opak insert. Stejně jako insert - nevhodné pro vektor, vhodné pro List POZOR!!! Insert a erase znaplatní všechny iterátory, přístup přes neplatný iterátor způsobí nejčastěji pád programu! clear() - zruší všechny prvky kontejneru size() - počet prvků capacity() - vrátí velikost místa pro prvky empty() - je prázdný? max_size() - limit velikosti (max. počet prvků, který lze uložit) resize() - přidá/ubere prvky reserve() - přidá pouze místo pro prvky swap() - výměna obsahu dvou vektorů (efektivní) assign() - přiřazení více prvků do aktuálního vektoru
capacity, size, max_size // Příklad z MSDN Library #include <iostream> #include <vector> using namespace std ; void main() { vector<int> mujVector; mujVector.push_back(42) ; cout << "mujVector's size is: " << mujVector.size() << endl; cout << "mujVector's maximum size is: " << mujVector.max_size() << endl; cout << "mujVector's capacity is: " << mujVector.capacity() << endl; mujVector.reserve(1000); cout << endl << "After reserving storage for 1000 elements:" << endl; mujVector.resize(2000); cout << endl << "After resizing storage to 2000 elements:" << endl; }
<vector> Přístupy na prvky: T& operator[](size_type n) - přístup na libovolný prvek T& at(size_type n); - přístup na libovolný prvek s kontrolou mezí indexu T& front(); - přístup na první prvek T& back(); - přístup na poslední prvek Přístup přes iterátory (analogie ukazatelů): template <class InputIterator> vector(InputIterator first, InputIterator last) iterator begin(); - ukazuje na první prvek iterator end(); - ukazuje za poslední prvek reverse_iterator rbegin(); - ukazuje na první prvek reverse_iterator rend(); Operátory operator=, operator==, operator<,
<vector> #include <vector> #include <iostream> #include <algorithm> using namespace std; int main() { vector<int> v(3); // Declare a vector of 3 elements. v[0] = 7; v[1] = v[0] + 3; v[2] = v[0] + v[1]; // v[0] == 7, v[1] == 10, v[2] == 17 reverse(v.begin(), v.end()); // obrátí poradi prvku vektoru vector<int>::iterator itCil = v.begin(); v.insert(itCil, 3); // pozor - itCil je nyni neplatny! Doslo k realokaci pole! // v.insert(itCil, 10, 4); - vyvola zcela jiste poruseni ochrany pameti vector <int> v2; v2.insert(v2.begin(), 10, 5); // vlozi do v2 10x cislo 5 v2.insert(v2.begin(), v.begin(), v.end()); //vlozi do v2 vektor v for (int i = 0; i < v.size(); ++i) cout << "v[" << i << "] = " << v[i]<<endl; for (int i = 0; i < v2.size(); ++i) cout << "v2[" << i << "] = " << v2[i]<<endl; v2.erase(v2.begin(), v2.end()); // ruší do konce vektoru system("pause"); return 0; }
<vector> Konstruktory vector(), vector(const vector& x) - implicitní a kopírovací konstruktor vector(size_type n, const T& val=T() ) - umožňuje vytvořit vector s počáteční velikostí n prvků. 2. parametr umožňuje definovat, který konstruktor třídy T se bude volat při alokaci jednotlivých prvků pole. Příklad: #include <vector> class mojeTrida { int n; public: mojeTrida() { n=0;} mojeTrida(int N) { n=N; } }; vector<mojeTrida> v(2000, mojeTrida(10));
vector<bool> šablona vector obsahuje specializaci pro typ bool - pole typu bool uchovává v poli bitů šablona vector<bool> má všechny operace stejné jako vector, navíc poskytuje metodu flip() pro inverzi obsahu vektoru a operaci anyinstance[i].flip() pro inverzi jednoho bitu
<map> - asociativní pole Šablona <map> implementuje asociativní pole, tzn. pole, jehož indexem nemusí být jen integer, ale jakýkoli vhodný typ. <map> tedy obsahuje páry hodnot, z nichž první je klíč a druhá je asociovaná hodnota. V kontejneru jsou páry hodnot uloženy v prvcích typu value_type (tj. pair<const key_type, mapped_type>). Tyto prvky jsou pro rychlé vyhledávání uloženy např. ve stromové struktuře (v konkrétních implementacích se často používá binární strom typu "red-black tree“ nebo AVL tree. Šablona map má 3 parametry: 1. je typ klíče, 2. je typ hodnoty, 3. je funkční objekt pro porovnání hodnoty 2 klíčů (funkční objekt je třída, která obsahuje operator()) – tímto způsobem je možné porovnávat libovolné typy klíčů <map> implementuje tradiční asociativní pole s unikátními klíči, tzn. každý klíč mapuje jednu hodnotu. Kontejner multimap dovoluje vícenásobný výskyt stejných klíčů. Příbuzné jsou kontejnery obsahující pouze klíče (set, multiset), které budou popsány později. <map> umožňuje rychlé vyhledávání podle hodnoty klíče. Klíč musí být unikátní v rámci jednoho kontejneru map. Map poskytuje obousměrné iterátory. <map> vyžaduje, aby pro data byl definován operátor =, a pro klíč operátor "menší než", pomocí kterého udržuje kontejner uspořádaný, takže iterace přes map je uspořádaná. Pokud chceme nadefinovat/předefinovat chování uspořádání (např. pro typ char*, nebo aby byl obsah map<int> řazen sestupně), musíme při šabloně dodat 3. parametr – funkční objekt map definuje operator[] tak, aby se s ním dalo pracovat jako s polem indexovaným klíčem. Operátor [] vrací referenci na hodnotu spojenou s daným klíčem. Pokud dvojice (klíč,hodnota) není v kontejneru obsažena, automaticky se vloží s implicitní hodnotou (nula nebo výsledek implicitního konstruktoru).
<map> Pro vkládání a rušení prvků v kontejnerech <map> a <multimap> je možné použít metod insert(const value_type &val), erase(const key_type &k), erase(iterator pos) erase(iterator first, iterator last) Operace insert a erase narozdíl od vectoru nezneplatní všechny iterátory Vkládat je možné také použitím operátoru [] a kopírováním ze sekvence prvků. Operace m[k] je ekvivalentní *(m.insert(make_pair(k,V())).first).second, kde V() je implicitní hodnota typu mapped_type. Metoda insert vrací dvojici (iterátor,booleovský příznak úspěšného vložení). V případě, že prvek s daným klíčem v mapě již je, vložení se neprovede. V každém případě insert vrátí iterátor odkazující na prvek s daným klíčem. Metody erase vrací počet zrušených prvků. Metoda clear() zruší celý obsah kontejneru. size() vrací počet položek, empty() platí při prázdném kontejneru, swap(map&) vymění obsah dvou map, operátory pro porovnávání map ==,!=,<,>,<=,>= dovolují například řadit vektory map a podobně.
<map> Další metody: iterator find(const key_type& k) – vrací iterátor na klíč s klíčem k. Pokud takový klíč neexistuje, vrátí iterátor=end() size_type count(const Key& key) const; - vrací počet prvků, které mají klíč=key iterator upper_bound(const Key& key); - vrátí iterátor na první prvek, jehož klíč je > key iterator lower_bound(const Key& key); - vrátí iterátor na první prvek, jehož klíč je >= key size_type size() const; - vrací počet prvků, uložených v kontejneru max_size(),
<map> Příklad: telefonní seznam s rychlým vyhledáváním struct ltstr //funkční objekt, definující relaci uspořádání { bool operator()(string s1, string s2) const {return s1 > s2;} }; void tisk(map<string, int, ltstr> & tel_seznam) { typedef map<string,int, ltstr>::const_iterator iter_t; for(iter_t p=tel_seznam.begin(); p!=tel_seznam.end(); ++p) cout << p->first << '\t' << p->second << endl; } /* Iterátorem procházíme, pro každý pár tiskneme položku first (klíe = jméno) a second (hodnota = císlo). */ int main () { map<string,int, ltstr> seznam; string slovo; seznam["Jozka"]=777605123; seznam["Jozka"]=11111; //aktualizace prvku funguje… seznam["Hana"]=577301222; seznam["Natasa"]=3212154; // pokud chceme pri vkladani zjistit, zda prvek uz v seznamu byl pair<string,int> cislo("Honza",605111222); //vytvoreni prvku pair<map<string,int>::iterator,bool> prvek = seznam.insert(cislo); //zpracovani navr.hodnoty if(prvek.second) cout << "vložen nový prvek \n"; else cout << "prvek už v mapì byl \n"; map<string,int>::iterator i = prvek.first; // ukazuje na seznam[„Honza"] cout<<"Cislo Natasi="<<seznam["Natasa"]<<endl; tisk(seznam); system("pause");
<multimap> Kontejner se stejnými vlastnostmi a metodami jako <map> Jediný rozdíl – je možné do něj vložit více stejných klíčů Příklad: #include <iostream> #include <map> using namespace std; struct Moje{ int x; int y;} ahoj; int main () { multimap<string, int> m; m.insert(pair<string, int>("a", 1)); m.insert(pair<string, int>("c", 2)); m.insert(pair<string, int>("b", 3)); m.insert(pair<string, int>("b", 4)); m.insert(pair<string, int>("a", 5)); m.insert(pair<string, int>("b", 6)); cout << "Pocet prvku s klicem a: " << m.count("a") << endl; cout << "Pocet prvku s klicem b: " << m.count("b") << endl; cout << "Pocet prvku s klicem c: " << m.count("c") << endl; cout << "Elements in m: " << endl; for (multimap<string, int>::iterator it = m.begin(); it != m.end(); ++it) cout << " [" << it->first << ", " << it->second << "]" << endl; system("pause"); }
<list> list je obousměrný lineární seznam, což je sekvence prvků svázaných ukazateli, díky nimž je možné pohybovat se na následující a předchozí prvky seznamu. prvek1 prvek2 prvek3 begin end Princip seznamu umožňuje efektivní provádění těchto operací: Operace insert a erase na začátku, konci nebo za/před prvek, na který máme ukazovátko(iterátor) – tyto operace jsou rychlé a po jejich provedení zůstávají všechny iterátory platné! (samozřejmě kromě těch, které ukazovaly na smazané prvky) pohyb dopředu a zpět o jeden prvek Uspořádání seznamu se mění podle prováděných operací insert, erase – pokud iterátor ukazuje na první prvek seznamu a my vložíme na první prvek seznamu nový prvek, pak tento iterátor bude ukazovat na 2. prvek seznamu Nevýhody oproti <vector> = paměťová režie pro ukazatele.
<list> Šablona list obsahuje všechny metody jako ostatní kontejnery, navíc poskytuje následující metody: void merge(list& x); void merge(list& x, greater<T> pr); - spojí seřazené seznamy x a *this. Musí platit x!=*this, a oba seznamy musí být seřazené pomocí operator< nebo pomocí funkčního objektu pr. Seznam x je touto operací vyprázdněn. Výsledný seznam *this je seřazený se zachováním pořadí = pokud je stejný prvek v *this i v x, bude prvek z *this vždy před prvekem z x. Sloučení seřazených seznamů proběhne v lineárním čase (tj. bude provedeno max. size() + x.size() – 1) porovnání reference front(); - vrací odkaz na první prvek seznamu reference back(); - vrací odkaz na poslední prvek seznamu void sort() – na rozdíl od kontejneru vector, který umožňuje přistupovat na libovolný prvek kontejneru s konstatním časem, a proto je možno pracovat s ním pomocí random_access_iterator-u, který lze předat algoritmu sort z <algorithm>, seznam tuto možnost nemá, a proto je sort implementován přímo jako metoda seznamu. Rychlost metody list<>::sort je pro větší počet prvků řádově menší než u sort-u z <algorithm>!!! void unique(); - odstraní ze seznamu všechny shodné prvky, které leží bezprostředně za sebou tak, aby všechny prvky v seznamu byly unikátní. Příklad: seznam obsahuje hodnoty 1 0 2 2 1, po zavolání unique bude obsahovat pouze hodnoty 1 0 2 1. Metoda pracuje v lineráním čase = stačí pouze N porovnání void reverse(); - obrátí pořadí prvků v seznamů (u obousměrného seznamu stačí pouze zaměnit ukazatele na další s ukazateli na předchozí prvky). Všechny iterátory proto zůstanou platné. Metoda pracuje v lineárním čase
<list> void remove (const T& val); - odstraní všechny prvky, jejichž hodnota=val. void remove_if(binder2nd<not_equal_to<T> > pr); - odstraní ze seznamu všechny prvky, jejichž je volání funkčního objektu pr=true void remove_if(binder2nd<not_equal_to<T> > pr); void splice (iterator position, list<T, Alloc>& x); - pokud je position platným iterátorem v *this a seznam x je !=*this, pak odstraní všechny prvky z x zařadí je před position. void splice (iterator position, list<T, Alloc>& x, iterator i); - pokud jsou splněny podmínky jako u varianty výše, zařadí prvek z x, na který ukazuje iterátor i, do *this na pozici před position, a odstraní jej z x void splice (iterator position, list<T, Alloc>& x, iterator f, iterator l); - pokud jsou splněny podmínky jako u varianty výše, zařadí prvky z x, které se nachází mezi iterátory [first, last), do *this na pozici před position, a odstraní je z x void pop_back(); void pop_front(); - odstraní ze seznamu poslední/první prvek void push_back(); void push_front(); - vloží na první/poslední místo seznamu nový prvek
<list> Jednoduchý příklad: #include <iostream> #include <list> #include <iterator> using namespace std; int main () { list<int> L; L.push_back(0); L.push_front(1); L.insert(++L.begin(), 2); copy(L.begin(), L.end(), ostream_iterator<int>(cout, " ")); system("pause"); }
<deque> Deque (Double-Ended-Queue, fronta se dvěma konci) je podobná kontejneru vector – stejně jako vector, umožňuje náhodný přístup ke všem prvkům; vkládání a výmaz prvků na konci fronty probíhá v konstantním čase, uprostřed fronty pak s lineárním časem. Na rozdíl od vector-u ale deque umožňuje v konstatním čase vkládat a mazat prvky také na začátku fronty (čímž se podobá seznamu z <list>) deque také nemá žádné funkce jako capacity() a reserve(), a neposkytuje žádné záruky na validitu iterátorů, které poskytují tyto funkce Co se týče zneplatnění iterátorů, u deque funguje následovně: Insert (včetně push_front and push_back) a Erase uprostřed fronty zneplatňuje všechny iterátory, které se odkazují do fronty. Erase na začátku nebo konci fronty (včetně pop_front and pop_back) zneplatní pouze iterátory, které se odkazují na mazaný prvek
<deque> Příklad: #include <iostream> #include <deque> #include <iterator> using namespace std; int main () { deque<int> Q; Q.push_back(3); Q.push_front(1); Q.insert(Q.begin() + 1, 2); Q[2] = 0; // přepis prvku s hodnotou 3 na hodnotu 0 copy(Q.begin(), Q.end(), ostream_iterator<int>(cout, " ")); system("pause"); }
<queue> Queue je klasická fronta FIFO (first in-first out), která umožňuje vkládat prvky na konec fronty a vyjímat prvky ze začátku fronty. Neumožňuje přístup na prvky uprostřed fronty Parametry šablony: template<class T, class Cont = deque<T> > - první parametr určuje typ hodnot, které do queue chceme ukládat, druhý parametr určuje třídu, která bude použita k implementaci fronty – možní kandidáti jsou Deque a List - v podstatě je queue vlastně pouze jakýsi adaptér Queue poskytuje následující metody: void push(const T& x); - uloží prvek x na konec fronty value_type& front(); - vrátí referenci na prvek na začátku fronty value_type& back (); - vrátí referenci na prvek na konci fronty void pop(); - odstraní prvek na začátku fronty bool empty() const; - boolean – true, pokud je fronta prázdná size_type size() const; -vrací počet prvků ve frontě operátory ==, !=, <, >, <=, >= pro porovnání 2 front
<queue> Příklad: #include <iostream> #include <queue> #include <iterator> using namespace std; int main () { queue<int> Q; Q.push(1); Q.push(2); Q.push(3); Q.top(); cout<<Q.front()<<endl; cout<<Q.back()<<endl; system("pause"); }
priority_queue<..> V souboru <queue> je ještě šablona priority_queue<..>, implementující frontu s prioritou Parametry šablony: template<class T, class Cont = vector<T>, class Pred = less<Cont::value_type> > class priority_queue 2. parametr opět udává, jaký kontejner bude uvnitř šablony použit 3. parametr je funkční objekt, který definuje uspořádání prvků ve frontě = prioritu, s jakou se určitý prvek dostane na vrchol frotny Metody má zcela stejné jako queue<..>
<stack> Stack je klasický zásobník – FILO (first in – last out) Parametry, metody i operátory jsou stejné jako u queue, s tím rozdílem, že metody pop a top přistupují na stejné místo, jako push top push pop
set<…> a multiset<…> <set> implementuje množinu, která může obsahovat prvky jakéhokoli typu. Šablona set<> může obsahovat pouze unikátní klíče, multiset<> umí ukládat více stejných klíčů Šablona set<..> je v podstatě identická s map<..> (a multiset<> s multimap<>), ale s tím, že <set> ukládá pouze klíče bez asociovaných hodnot. Metody má <set> stejné jako <map> Jako <map> je i <set> uspořádaný kontejner, a operace insert a erase nezneplatňují iterátory Na <set> lze použít množinové algoritmy z <algorithm> jako includes, set_union, set_intersection, set_difference, and set_symmetric_difference
#include <iostream> #include <set> #include <iterator> using namespace std; int main() { const int N = 6; const char* jmenaOvoce[] = {"jabko", "hruska", "rybiz", "pomeranc", "citron", "grepfruit"}; const char* podstatnaJmena[] = {"dum", "citron", "clovek", "lod", "duse", "material"}; set<string> A(jmenaOvoce, jmenaOvoce + N); set<string> B(podstatnaJmena, podstatnaJmena + N); set<string> C; cout << "Mnozina A: "; copy(A.begin(), A.end(), ostream_iterator<string>(cout, " ")); cout << endl; cout << "Mnozina B: "; copy(B.begin(), B.end(), ostream_iterator<string>(cout, " ")); cout << "Sjednoceni: "; set_union(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<string>(cout, " ")); cout << "Prunik: "; set_intersection(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<string>(cout, " ")); set_difference(A.begin(), A.end(), B.begin(), B.end(), inserter(C, C.begin())); cout << "Mnozina C (Rozdil of A and B): "; copy(C.begin(), C.end(), ostream_iterator<string>(cout, " ")); system("pause"); }
<bitset> Šablona bitset<N> implementuje pole N bitů, se kterým je možno manipulovat pomocí různých bitových operací Poskytuje následující metody: Konstruktory bitset(), bitset(ulong val) Operátory !=, ==, &=, ^=, |=, ~, <<=, >>=, [] any() true if any bits are set count() how many bits are set flip() reverses the bitset none() true if no bits are set reset() clears bits set() sets bits size() number of bits that the bitset can hold test() state of a given bit to_string() string representation of the bitset to_ulong() integer representation of the bitset
bitset - operátory != returns true if the two bitsets are not equal. == returns true if the two bitsets are equal. &= performs the AND operation on the two bitsets. ^= performs the XOR operation on the two bitsets. |= performs the OR operation on the two bitsets. ~ reverses the bitset (same as calling flip()) <<= shifts the bitset to the left >>= shifts the bitset to the right [x] returns a reference to the xth bit in the bitset.
<string>
<iterator>
<algorithm>
C++ I/O <>
Zpracování chyb
Lokalizace
Jazyková podpora
Výpočty
Utility