Standard Template Library Programowanie uogólnione, parametryczne ( generic programming) Składniki biblioteki STL kontenery iteratory algorytmy adaptatory funktory Nicolai M. Josuttis, C++ Biblioteka standardowa, Helion 2003 kontener iterator Algorytm iterator kontener iterator kontener
WZORCE Wzorce funkcji template <class T> T minimum (T Pierwsza, T Druga) { return Pierwsza < Druga ? Pierwsza : Druga; } template <class T> bool greater (T Pierwsza, int Druga) return Pierwsza > Druga; // int a = 5; double x = -112.8; cout << minimum(a, -17) << endl; // wersja int cout << minimum(x, a); // błąd cout << minimum(x, (double)a) << endl; // wersja double cout << greater(a, 5) << endl; // wersja int, int cout << greater(x, a) << endl; // wersja double, int cout << greater(a, x) << endl; // wersja int, int
Wzorce klas template <class T> class Para { T Lewy; T Prawy; int Numer; public: Para(int Nr) : Numer(Nr) {} Para(T, T, int); ~Para(); int Wiekszy (T wzorzec); }; template <class T> Para <T> :: Para(T lewy, T prawy, int nr): Lewy(lewy), Prawy(prawy), Numer(nr) { cout << "Razem!" << endl; } template <class T> Para <T> ::~Para () { cout << "Rozwod!" << endl; } template <class T> int Para <T> :: Wiekszy (T wzorzec) { if (Lewy > wzorzec) return Numer; else return -1; }
Para <int> ParaCalk(1, 2, 5); cout << ParaCalk.Wiekszy(7) << endl; cout << ParaCalk.Wiekszy(-4) << endl; float x = 4.0, *px = &x; //Para <float> ParaRzecz(x, px, 5); // błąd Para<double>* WskPd = new Para<double>(3.0, 7.0, 9); cout << WskPd->Wiekszy(5.0) << endl; delete WskPd; Razem! -1 5 Rozwod!
Kontenery Wektor Kolejka o dwu końcach Lista sekwencyjne Zbiór lub wielozbiór Mapa lub multimapa sekwencyjne asocjacyjne
Wektory 1 2 3 4 5 6 7 #include "stdafx.h" #include <iostream> #include <vector> using namespace std; vector<int> coll; // kontener wektorowy dla liczb całkowitych // dołącz elementy o wartościach od 1 do 7 for (int i=1; i<=7; ++i) coll.push_back(i); // coll[i] = i : błąd // wyświetl wszystkie elementy rozdzielone spacjami for (int i=0; i < coll.size(); ++i) cout << coll[i] << ' '; 1 2 3 4 5 6 7
Kolejki o dwu końcach (deque) #include <deque> deque<float> coll; // kontener deque dla elementów typu float // wstaw elementy o wartościach od 1.1 do 7.7 na początek kontenera for (int i=1; i<=7; ++i) { coll.push_front(i * 1.1f); // wstaw na początek } // wstaw elementy o wartościach od -1.1 do -7.7 na koniec kontenera coll.push_back(i * -1.1f); // wstaw na koniec // wyświetl wszystkie elementy rozdzielone spacjami for (int i=0; i < coll.size(); ++i) cout << coll[i] << ' '; 7.7 6.6 5.5 4.4 3.3 2.2 1.1 -1.1 -2.2 -3.3 -4.4 -5.5 -6.6 -7.7
a b c d e f g h i j k l m n o p q r s t u v w x y z Listy #include <list> list<char> coll; // kontener list dla elementów znakowych // dolacz elementy od 'a' do 'z' for (char c='a'; c<='z'; ++c) { coll.push_back(c); // push_front(c) } /* wypisz wszystkie elementy : - gdy lista nie jest pusta - wypisz i usuń pierwszy element */ while (! coll.empty( )) cout << coll.front() << ' '; // coll.back() coll.pop_front(); // pop_back() a b c d e f g h i j k l m n o p q r s t u v w x y z
Adaptatory kontenerów sekwencyjnych Stos #include <stack> stack<int> st; // wpisz trzy elementy na stos st.push(1); st.push(2); st.push(3); // ze stosu zdejmij i wypisz dwa elementy cout << st.top( ) << ' '; st.pop( ); cout << '\t'; // zmodyfikuj szczytowy element st.top( ) = 77; // wpisz dwa nowe elementy st.push( 4 ); st.push( 5 ); // zdejmij jeden element nie przetwarzając go st.pop(); // zdejmij i wypisz pozostałe elementy while (!st.empty( )) { cout << st.top( ) << ' '; st.pop( ); } 3 2 4 77
liczba elementow w kolejce: 2 Kolejka #include <queue> #include <string> queue<string> q; // do kolejki wstaw trzy elementy q.push("Tu "); q.push("sa "); q.push("wiecej niz "); // z kolejki odczytaj i wypisz dwa elementy cout << q.front(); q.pop(); // wstaw dwa nowe elementy q.push("cztery "); q.push("slowa!"); // opusc jeden element // odczytaj i wypisz pierwszy i ostatni element cout << q.front() << endl; cout << q.back() << endl; // wypisz liczbe elementow w kolejce cout << "liczba elementow w kolejce: " << q.size(); Tu sa cztery slowa! liczba elementow w kolejce: 2
Kolejka priorytetowa TaskQueue 66.6 44.4 33.3 22.2 11.1 #include <queue> priority_queue<double> q; // do kolejki priorytetowej wstaw trzy elementy q.push(66.6); q.push(22.2); q.push(44.4); // odczytaj i wypisz dwa elementy cout << q.top() << ' '; q.pop(); cout << q.top() << endl; // wstaw kolejne trzy elementy q.push(11.1); q.push(55.5); q.push(33.3); // pomin jeden element // zdejmij i wypisz pozostale elementy while (!q.empty( )) { cout << q.top() << ' '; q.pop(); } TaskQueue 66.6 44.4 33.3 22.2 11.1
class Zadanie { int Priorytet; char* Opis; public: Zadanie(int, char* ); friend bool operator < (Zadanie, Zadanie); // less friend ostream& operator << (ostream&, Zadanie&); }; Zadanie::Zadanie(int pr = 0, char* op = NULL) : Priorytet(pr), Opis(op) {} bool operator < (Zadanie z1, Zadanie z2) { return z1.Priorytet < z2.Priorytet; } ostream& operator << (ostream& wy, Zadanie& zz) { wy << zz.Opis << " o priorytecie : " << zz.Priorytet << endl; return wy; }
Zad-2 o priorytecie : 12 Zad-5 o priorytecie : 8 priority_queue<Zadanie> ListaZadan; ListaZadan.push(Zadanie(5, "Zad-1")); ListaZadan.push(Zadanie(12, "Zad-2")); ListaZadan.push(Zadanie(3, "Zad-3")); ListaZadan.push(Zadanie(7, "Zad-4")); cout << ListaZadan.top(); ListaZadan.pop(); ListaZadan.push(Zadanie(8, "Zad-5")); ListaZadan.push(Zadanie(4, "Zad-6")); Zad-2 o priorytecie : 12 Zad-5 o priorytecie : 8
Iteratory Iterator – obiekt, który służy do nawigowania w kontenerze Operatory dla interatorów: * -> ++ -- == != = Funkcje kontenerów związane z iteratorami begin( ) end( )
Typy iteratorów kontener::iterator nazwa // zapis i odczyt kontener::const_iterator nazwa // tylko odczyt dwukierunkowy ( ++ , -- ) : list, set, multiset, map, multimap dostępu swobodnego (arytmetyka iteratorów i dwukierunkowy) : vector, deque
a b c d e f g h i j k l m n o p q r s t u v w x y z list<char> coll; // kontener typu lista dla elementów znakowych // dolacz elementy od 'a' do 'z' for (char c='a'; c<='z'; ++c) coll.push_back(c); // wypisz wszystkie elementy - iteruj po wszystkich elementach list<char>::const_iterator pos; for (pos = coll.begin(); pos != coll.end(); ++pos) // tylko ++ -- cout << *pos << ' '; a b c d e f g h i j k l m n o p q r s t u v w x y z vector<char> vw; for (char c='a'; c<='z'; ++c) vw.push_back(c); vector<char> iterator lok; for (lok = coll.begin(); lok != coll.end(); lok += 2) // co drugi { *lok = toupper(*lok); cout << *lok << ' '; } ONP1 A C E G I K M O Q S U W Y
Kontenery asocjacyjne Zbiory i wielozbiory typedef set<int> IntSet; // sort using less ( < ) IntSet coll; // kontener set dla wartości typu int // wstaw 7 elementów - wartość 1 wstawiana jest dwukrotnie coll.insert(3); coll.insert(1); coll.insert(5); coll.insert(4); coll.insert(1); coll.insert(6); coll.insert(2); coll.insert(7); // wypisz wszystkie elementy - iteruj po wszystkich elementach IntSet::const_iterator pos; for (pos = coll.begin(); pos != coll.end(); ++pos) cout << *pos << ' '; 1 2 3 4 5 6 7
typedef multiset<int,greater<int> > IntMultSet; // sort using greater ( > ) IntMultSet cont; cont.insert(3); cont.insert(1); cont.insert(5); cont.insert(4); cont.insert(1); cont.insert(6); cont.insert(2); cont.insert(7); // wypisz wszystkie elementy - iteruj po wszystkich elementach IntMultSet::const_iterator lok; for (lok = cont.begin(); lok != cont.end(); ++lok) cout << *lok << ' '; 7 6 5 4 3 2 1 1
jest to multimapa złożona z oznakowanych łancuchów Mapy i multimapy typedef multimap<int,string> IntStringMMap; IntStringMMap coll; // kontener na pary wartości typu int-string // wstaw kilka elementów w przypadkowej kolejności // - wartość o kluczu 1 wstawiana jest dwukrotnie coll.insert(make_pair(5,"oznakowanych")); coll.insert(make_pair(1,"jest")); coll.insert(make_pair(2,"multimapa")); coll.insert(make_pair(1,"to")); coll.insert(make_pair(4,"z")); coll.insert(make_pair(6,"łancuchów")); coll.insert(make_pair(3,"złożona")); /* wypisz wszystkie wartości elementów - iteruj po wszystkich elementach - druga składowa elementu to wartość */ IntStringMMap::iterator pos; for (pos = coll.begin(); pos != coll.end(); ++pos) cout << pos->second << ' '; jest to multimapa złożona z oznakowanych łancuchów
klucz: "dowolna liczba" wartosc: 4983.22 /* typ kontenera: * - mapa: elementy o postaci par klucz-wartość * - string: klucze posiadają typ string * - float: wartości posiadają typ float */ typedef map<string,float> StringFloatMap; StringFloatMap coll; // wstaw do kolekcji kilka elementów – tablica asocjacyjna // (tylko mapy) coll["VAT"] = 0.22; coll["Pi"] = 3.1415; coll["dowolna liczba"] = 4983.223; coll["Zero"] = 0; /* wypisz wszystkie elementy - iteruj po wszystkich elementach - pierwsza składowa elementu to klucz - druga składowa elementu to wartość */ StringFloatMap::iterator pos; for (pos = coll.begin(); pos != coll.end(); ++pos) cout << "klucz: \"" << pos->first << "\" " << "wartosc: " << pos->second << endl; MapFun2,1 klucz: "Pi" wartosc: 3.1415 klucz: "VAT" wartosc: 0.22 klucz: "Zero" wartosc: 0 klucz: "dowolna liczba" wartosc: 4983.22
Algorytmy funkcje globalne operujące na iteratorach Zakresy [ początek , koniec ) początek - pozycja pierwszego elementu zakresu koniec - pozycja następna za ostatnim elementem zakresu #include <algorithm>
max: 34 max: 35 list<int> coll; list<int>::iterator pos; for (int i=20; i<=40; ++i) coll.push_back(i); //elementy do 20 do 40 // ustal pozycje wartości 25 oraz 35 list<int>::iterator pos25, pos35; pos25 = find (coll.begin(), coll.end(), 25); // zakres, wartość pos35 = find (coll.begin(), coll.end(), 35); // zakres, wartość // wypisz największą wartość z odpowiedniego zakresu cout << "max: " << *max_element (pos25, pos35) << endl; // przetwarzaj elementy włącznie z ostatnią pozycją cout << "max: " << *max_element (pos25, ++pos35) << endl; max: 34 max: 35
min: 1 max: 6 vector<int>::iterator pos; vector<int> coll; vector<int>::iterator pos; // wstaw elementy od 1 do 6 w przypadkowej kolejności coll.push_back(2); coll.push_back(5); coll.push_back(4); coll.push_back(1); coll.push_back(6); coll.push_back(3); // znajdź i wypisz element o najmniejszej i największej wartości pos = min_element (coll.begin(), coll.end()); cout << "min: " << *pos << endl; pos = max_element (coll.begin(), coll.end()); cout << "max: " << *pos << endl; // zamień miejscami elementy z pozycji 3 i 5 swap( coll[3], coll[5] ); // od 0 // zamień miejscami element maksymalny i element ostatni swap( *pos, *(coll.end( )-1)); min: 1 max: 6
1 2 6 5 4 3 // posortuj wszystkie elementy sort (coll.begin(), coll.end()); // znajdź pierwszy element o wartości 3 pos = find (coll.begin(), coll.end(), 3); // zakres wartość // odwróć kolejność elementów, począwszy od // znalezionego elementu o wartości 3 do końca reverse (pos, coll.end()); // wypisz wszystkie elementy for (pos=coll.begin(); pos!=coll.end(); ++pos) { cout << *pos << ' '; } 1 2 6 5 4 3
Obsługa wielu zakresów pierwszy zakres : początek i koniec dalsze zakresy : tylko początek list<int> coll1; vector<int> coll2; for (int i=1; i<=9; ++i) coll1.push_back(i); // wstaw elementy od 1 do 9 // zmień rozmiar kolekcji 2 tak, // aby miała wystarczająco miejsca dla algorytmu nadpisującego coll2.resize (coll1.size()); // utwórz trzecią kolekcję z wystarczająca ilością miejsca // - rozmiar początkowy przekazywany jest jako parametr deque<int> coll3(coll1.size());
1 2 3 4 5 6 7 8 9 copy (coll1.begin(), coll1.end(), // źródło // przekopiuj elementy z pierwszej kolekcji do drugiej copy (coll1.begin(), coll1.end(), // źródło coll2.begin()); // przeznaczenie // przekopiuj elementy z drugiej kolekcji do trzeciej copy (coll2.begin(), coll2.end(), // źródło coll3.begin()); // przeznaczenie deque<int>::const_iterator lok; for (lok = coll3.begin(); lok != coll3.end(); ++ lok) cout << *lok << ' '; 1 2 3 4 5 6 7 8 9
Adaptatory iteratorów Iteratory wstawiające wstawiacze końcowe wstawiacze początkowe wstawiacze ogólne Wstawiacz Opis back_inserter ( kontener ) Dołącza w tej samej kolejności funkcja push_back() front_inserter ( kontener ) Dołącza w odwrotnej kolejności funkcja push_front() inserter ( kontener, pozycja ) Wstawia na podanej pozycji funkcja insert() (dla kontenerów asocjacyjnych pozycja : początek poszukiwań)
9 8 7 6 5 4 3 2 1 list<int> coll1; // wstaw elementy od 1 do 9 do pierwszej kolekcji for (int i=1; i<=9; ++i) coll1.push_back(i); // przekopiuj elementy z coll1 do coll2 dołączając je na końcu vector<int> coll2; copy (coll1.begin(), coll1.end(), // źródło back_inserter(coll2)); // przeznaczenie // przekopiuj elementy z coll1 do coll3 wstawiając je na początku // - odwraca kolejność elementow deque<int> coll3; front_inserter(coll3)); // przeznaczenie deque<int>::const_iterator lok; for (lok = coll3.begin(); lok != coll3.end(); ++ lok) cout << *lok << ' ' ; 9 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9 // - jedyny wstawiacz działający // przekopiuj elementy z kolekcji coll1 do coll4 // - jedyny wstawiacz działający // w przypadku kolekcji asocjacyjnych set<int> coll4; copy (coll1.begin(), coll1.end(), // źródło inserter(coll4,coll4.begin())); // przeznaczenie set<int>::const_iterator pos; for (pos = coll4.begin(); pos != coll4.end(); ++ pos) cout << *pos << ' ' ; 1 2 3 4 5 6 7 8 9
Iteratory strumieniowe list<int> coll; cout << "Wpisz ciag liczb całkowitych i zakończ go .Enter \n"; copy(istream_iterator<int>(cin),// początek źródła istream_iterator<int>(), // koniec źródła .Enter, ctrl-Z Enter front_inserter(coll)); // przeznaczenie // wypisz wszystkie elementy kolekcji copy (coll.begin(), coll.end(), // źródło ostream_iterator<int>(cout," ")); // przeznaczenie Wpisz ciag liczb całkowitych i zakończ go .Enter 12 34 56 78 90 . 90 78 56 34 12
6 5 4 3 2 1 1 2 3 4 5 6 6 5 4 2 1 1 2 4 5 6 5 6 list<int> coll; for (int i=1; i<=6; ++i) { coll.push_front(i); coll.push_back(i); } // wypisz wszystkie elementy kolekcji copy (coll.begin(), coll.end(), ostream_iterator<int>(cout," ")); // usuń wszystkie elementy o wartości 3 remove (coll.begin(), coll.end(), 3); // wypisz wynikowe elementy kolekcji 6 5 4 3 2 1 1 2 3 4 5 6 6 5 4 2 1 1 2 4 5 6 5 6
liczba usunietych elementow: 2 6 5 3 2 1 1 2 3 5 6 coll.erase (coll.begin(), coll.end()); // usunięcie elementów for (int i=1; i<=6; ++i) { coll.push_front(i); coll.push_back(i); } // usuń wszystkie elementy o wartości 4 - zachowaj nową pozycję końca list<int>::iterator end = remove (coll.begin(), coll.end(), 4); // wypisz wynikowe elementy kolekcji copy (coll.begin(), end, ostream_iterator<int>(cout," ")); // wypisz liczbę elementów wynikowych cout << "liczba usuniętych elementów: " << distance(end, coll.end()) << endl; // usuń unieważnione elementy coll.erase (end, coll.end()); // wypisz wszystkie elementy zmodyfikowanej kolekcji copy (coll.begin(), coll.end(), 6 5 3 2 1 1 2 3 5 6 liczba usunietych elementow: 2 6 5 3 2 1 1 2 3 5 6
Iteratory odwrotne 9 8 7 6 5 4 3 2 1 WordHis, Trójki vector<int> coll; // wstawia elementy od 1 do 9 for (int i=1; i<=9; ++i) coll.push_back(i); // wypisz wszystkie elementy w odwrotnej kolejności copy (coll.rbegin(), coll.rend(), // źródło ostream_iterator<int>(cout," ")); // przeznaczenie 9 8 7 6 5 4 3 2 1 WordHis, Trójki
Funkcje Funkcje ogólne template <class T> inline void PRINT_ELEMENTS (const T& coll, const char* opis = "") { typename T::const_iterator pos; cout << opis; for (pos=coll.begin(); pos!=coll.end(); ++pos) cout << *pos << ' '; } cout << endl; // deque<int> coll2; PRINT_ELEMENTS(coll2,"Wartości początkowe : ");
Funkcje jako argumenty algorytmów set<int> coll1; vector<int> coll2; // wstaw do kolekcji coll1 elementy od 1 do 9 for (int i=1; i<=9; ++i) { coll1.insert(i); } PRINT_ELEMENTS(coll1,"Wartości początkowe: "); // przekształć każdy element z coll1 i umieść go w coll2 // - podnieś do kwadratu przenoszone wartości transform (coll1.begin(),coll1.end(), // źródło back_inserter(coll2), // przeznaczenie square); // zdefiniowana operacja PRINT_ELEMENTS(coll2,"Podniesione do kwadratu : "); Wartosci poczatkowe : 1 2 3 4 5 6 7 8 9 Podniesione do kwadratu : 1 4 9 16 25 36 49 64 81
Predykaty bool isPrime (int number) { if (number < 0) number = -number; // ignoruj znak ujemności if (number == 0 || number == 1) { return false; } // liczby 0 i 1 nie są pierwszymi // znajdź dzielnik, przez który liczba dzieli się bez reszty int divisor; for (divisor = number/2; number % divisor != 0; --divisor) { } return divisor == 1; // gdy jest dzielnik większy od 1, liczba nie jest liczbą pierwszą }
29 to pierwsza znaleziona liczba pierwsza list<int> coll; // wstaw elementy od 24 do 30 for (int i=24; i<=30; ++i) { coll.push_back(i); } list<int>::iterator pos; pos = find_if (coll.begin(), coll.end(), // zakres isPrime); // predykat if (pos != coll.end()) { cout << *pos << " to pierwsza znaleziona liczba pierwsza" << endl; } else { cout << "Nie znaleziono żadnej liczby pierwszej" << endl; } 29 to pierwsza znaleziona liczba pierwsza
mogą posiadać stan wewnętrzny (inicjowanie) posiadają typ Obiekty funkcyjne mogą posiadać stan wewnętrzny (inicjowanie) posiadają typ działają szybciej niż zwykłe funkcje // prosty obiekt funkcyjny, który wypisuje przekazany argument class PrintInt { public: void operator( ) (int elem) const cout << elem << ' '; } };
1 2 3 4 5 6 7 8 9 vector<int> coll; // wstaw elementy od 1 do 9 for (int i=1; i<=9; ++i) { coll.push_back(i); } // wypisz wszystkie elementy for_each (coll.begin(), coll.end(), // zakres PrintInt()); // operacja // konstruktor -> tempPrintIt ; // for_each -> tempPrintIt( *tempPos ) 1 2 3 4 5 6 7 8 9
// którą został zainicjalizowany class AddValue { private: // obiekt funkcyjny dodający wartość, // którą został zainicjalizowany class AddValue { private: int theValue; // wartość do dodania public: AddValue(int v) : theValue(v) { } // konstruktor ustala wartość do dodania void operator() (int& elem) { elem += theValue; } // wywołanie funkcji dodaje wartość };
po dodaniu pierwszego elementu: 22 23 24 25 26 27 28 29 30 list<int> coll; for (int i=1; i<=9; ++i) // wstaw elementy od 1 do 9 { coll.push_back(i); } PRINT_ELEMENTS(coll,"wartości początkowe: "); // do każdego elementu dodaj wartość 10 for_each (coll.begin(), coll.end(), // zakres AddValue(10)); // operacja PRINT_ELEMENTS(coll,"po dodaniu liczby 10: "); // do każdego elementu dodaj wartość pierwszego elementu AddValue(*coll.begin())); // operacja PRINT_ELEMENTS(coll,"po dodaniu pierwszego elementu: "); wartosci poczatkowe: 1 2 3 4 5 6 7 8 9 po dodaniu liczby 10: 11 12 13 14 15 16 17 18 19 po dodaniu pierwszego elementu: 22 23 24 25 26 27 28 29 30