STL - Standard Template Library Autor: Błażej Chodarcewicz rainbow.mimuw.edu.pl/~bc189380/STL/
Dlaczego STL? zElastyczność zEfektywność zŁatwość nauki i prostota zDobra specyfikacja i dokumentacja
Jak to działa?
Containers
Sequence Containers Tablica Cvectordequelist Dodanie/usunięcie na początku N/aO(n)O(1) Dodanie/usunięcie z końca N/aO(1) Dodanie/usunięcie ze środka N/aO(n) O(1) Dostęp do pierwszego elementu O(1) Dostęp do ostatniego elementu O(1) Dostęp do elementu w środku O(1) O(n)
#include int main () { vector v1; // Empty vector of doubles. v1.push_back (32.1); v1.push_back (40.5); for (int i = 0; i < v1.size (); i++) cout << v1[i] << " "; cout << endl; } Zobaczymy na ekranie:
#include int main (){ deque d; d.push_back(4); d.push_back(9); d.push_back(16); d.push_front(1); for (int i = 0; i < d.size (); i++) cout << "d[" << i << "] = " << d[i] << endl; cout << endl; d.pop_front (); d[2] = 25; for (i = 0; i < d.size (); i++) cout << "d[" << i << "] = " << d[i] << endl; return 0; } d[0] = 1 d[1] = 4 d[2] = 9 d[3] = 16 d[0] = 4 d[1] = 9 d[2] = 25
#include int array1 [] = { 9, 16, 36 }; int array2 [] = { 1, 4 }; int main () { list l1 (array1, array1 + 3); list l2 (array2, array2 + 2); list ::iterator i1 = l1.begin (); l1.splice (i1, l2); list ::iterator i2 = l1.begin (); while (i2 != l1.end ()) cout << *i2++ << endl; return 0; }
Adaptacje kontenerów. zStack i queue można zaimplementować z użyciem trzech podstawowych ciągów. zAdaptacja kolekcji dostarcza ograniczony interfejs do kolekcji. zAdaptacje nie zawierają iteratorów. zDeklaracja: stack > s; stack s; //domyślnie deque
Stack zNajlepiej używać z vector lub deque, można też z list, ale jest to nie najlepszy pomysł zbool empty(); zsize_type size(); zvalue_type& top(); zconst value_type& top(); zvoid push(const value_type&); zvoid pop();
queue zNajlepiej używać z deque lub list, można też użyć vectora, ale jest to nie efektywne zbool empty(); zsize_type size(); zvalue_type& front(); zconst value_type& front(); zvalue_type& back(); zconst value_type& back(); zvoid push(const value_type&); zvoid pop();
priority_queue zJako argument bierze typ sekwencyjny oraz funkcję porównującą elementy zNajlepiej używać z vectorem lub deque (jeśli rozmiar jest mocno dynamiczny). Nie można używać list, bo niezbędny jest operator[]. zUżywa implementacji algorytmu kopcowego.
priority_queue interfejs: zbool empty(); zsize_type size(); zvalue_type& top(); zconst value_type& top(); zvoid push(const value_type&); zvoid pop();
Kolekcje asocjacyjne. zUogólnienie kolekcji zNajczęściej używane typ kluczy to String zImplementacja jest efektywna
Czym różnią się kolekcje asocjacyjne? zSet: zawiera tylko klucze, operacje jak na zbiorze zMultiset: jak set tylko, że może być wiele kopii elementów zMap: zbiór par klucz, wartość zMultimap: klucze mogą się powtarzać
struct ltstr{ bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; int main(){ map months; months["january"] = 31; months["february"] = 28; months["march"] = 31; months["april"] = 30; months["may"] = 31; months["june"] = 30; months["july"] = 31; months["august"] = 31; months["september"] = 30; months["october"] = 31; months["november"] = 30; months["december"] = 31; } Przykład użycia map
Przykład użycia multimap struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; int main(){ multimap m; m.insert(pair ("a", 1)); m.insert(pair ("c", 2)); m.insert(pair ("b", 3)); m.insert(pair ("b", 4)); }
Alokatory zZawierają funkcje alokacji i dealokacji pamięci zCzarne skrzynki
Rodzaje alokatorów. alloc Domyślny alokator. Za zwyczaj ma najlepsze charakterystyki, jest thread-safe. pthread_alloc Dla każdego wątku jest oddzielna pula pamięci. Można tego używać tylko jeśli system operacyjny wspiera wielowątkowość. Jest za zwyczaj szybszy od alloc. Problem fragmentacji. single_client_alloc Alokator dla programów jedno wątkowych. Nie jest thread-safe. malloc_alloc Alokator używający standardowej funkcji malloc. Jest thread-safe, ale dość powolny.
Iteratory zUogólnienie wskaźników zSłużą do iteracji po elementach zSą pośrednikami pomiędzy kolekcjami i algorytmami zMożliwość pisania generycznych algorytmów
Rodzaje iteratorów zInput Iterator zOutput Iterator zForward Iterator zBidirectional Iterator zRandom Access Iterator zConst Iterator
Input Iterator
Output Iterator
Forward Iterator
Biderictional Iterator
Random Access Iterator
Algorytmy STL zGeneryczne algorytmy oddzielają algorytm od danych zNie zależą od reprezentacji danych zOperują na iteratorach
Non-mutating ztemplate InputIteratorUnaryFunction UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f); ztemplate InputIteratorEqualityComparable iterator_traitsiterator_traits ::difference_type count(InputIterator first, InputIterator last, const EqualityComparable& value); ztemplate InputIteratorEqualityComparable InputIterator find(InputIterator first, InputIterator last, const EqualityComparable& value) ztemplate InputIterator bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2); ztemplate ForwardIterator ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2);
Przykład: int main() { int A[] = { 2, 0, 4, 6, 0, 3, 1, -7 }; const int N = sizeof(A) / sizeof(int); cout << "Number of zeros: " << count(A, A + N, 0) << endl; }
Przykład: template struct print : public unary_function { print(ostream& out) : os(out), count(0) {} void operator() (T x) { os << x << ' '; ++count; } ostream& os; int count; }; int main(){ int A[] = {1, 4, 2, 8, 5, 7}; const int N = sizeof(A) / sizeof(int); print P = for_each(A, A + N, print (cout)); cout << endl << P.count << " objects printed." << endl; }
Mutating zOutputIterator copy(InputIterator first, InputIterator last, OutputIterator result); zvoid swap(Assignable& a, Assignable& b); zForwardIterator2 swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2); zOutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op); zvoid replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value) zvoid fill(ForwardIterator first, ForwardIterator last, const T& value); zForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value); zvoid reverse(BidirectionalIterator first, BidirectionalIterator last); zOutputIterator remove_copy(InputIterator first, InputIterator last, OutputIterator result, const T& value);
Przykład: vector V(5); iota(V.begin(), V.end(), 1); list L(V.size()); copy(V.begin(), V.end(), L.begin()); assert(equal(V.begin(), V.end(), L.begin()));
Algorytmy sortowania: zvoid sort(RandomAccessIterator first, RandomAccessIterator last); zvoid stable_sort(RandomAccessIterator first, RandomAccessIterator last); zbool is_sorted(ForwardIterator first, ForwardIterator last, StrictWeakOrdering comp) zOutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result); zbool binary_search(ForwardIterator first, ForwardIterator last, const LessThanComparable& value); zForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const LessThanComparable& value);
Przykład: int A[] = {1, 4, 2, 8, 5, 7}; const int N = sizeof(A) / sizeof(int); sort(A, A + N); copy(A, A + N, ostream_iterator (cout, " ")); wynik: " "
Algorytmy operujące na zbiorach bool includes(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2); zOutputIterator set_union(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result); zOutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result); zOutputIterator set_difference(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
Przykład: inline bool lt_nocase(char c1, char c2) { return tolower(c1) < tolower(c2); } int main() { int A1[] = {1, 3, 5, 7, 9, 11}; int A2[] = {1, 1, 2, 3, 5, 8, 13}; char A3[] = {'a', 'b', 'b', 'B', 'B', 'f', 'h', 'H'}; char A4[] = {'A', 'B', 'B', 'C', 'D', 'F', 'F', 'H' }; const int N1 = sizeof(A1) / sizeof(int); const int N2 = sizeof(A2) / sizeof(int); const int N3 = sizeof(A3); const int N4 = sizeof(A4); cout << "Intersection of A1 and A2: "; set_intersection(A1, A1 + N1, A2, A2 + N2, ostream_iterator (cout, " ")); cout << endl << "Intersection of A3 and A4: "; set_intersection(A3, A3 + N3, A4, A4 + N4, ostream_iterator (cout, " "), lt_nocase); cout << endl; } Wynik: Intersection of A1 and A2: Intersection of A3 and A4: a b b f h
Algorytmy kopcowe void push_heap(RandomAccessIterator first, RandomAccessIterator last); zvoid pop_heap(RandomAccessIterator first, RandomAccessIterator last); zvoid make_heap(RandomAccessIterator first, RandomAccessIterator last); zvoid sort_heap(RandomAccessIterator first, RandomAccessIterator last); zbool is_heap(RandomAccessIterator first, RandomAccessIterator last);
Przykład: int main(){ int A[] = {1, 2, 3, 4, 5, 6}; const int N = sizeof(A) / sizeof(int); make_heap(A, A+N); cout << "Before pop: "; copy(A, A+N, ostream_iterator (cout, " ")); pop_heap(A, A+N); cout << endl << "After pop: "; copy(A, A+N-1, ostream_iterator (cout, " ")); cout << endl << "A[N-1] = " << A[N-1] << endl; } Wynik: Before pop: After pop: A[N-1] = 6
Obiekty funkcyjne (funktory) zObiekty, które mogą być wołana jak funkcje zZwykłe funkcje to też obiekty funkcyjne zOperator () zModele: Generator, Unary Function, Binary Function zPredicate, Binary Predicate zAdaptacyjne obiekty funkcyjne
Przykłady: vector V(100); generate(V.begin(), V.end(), rand); struct less_mag : public binary_function { bool operator()(double x, double y) { return fabs(x) < fabs(y); } }; vector V;... sort(V.begin(), V.end(), less_mag());
Przykład: struct adder : public unary_function { adder() : sum(0) {} double sum; void operator()(double x) { sum += x; } }; vector V;... adder result = for_each(V.begin(), V.end(), adder()); cout << "The sum is " << result.sum << endl;
Przykład: list L;... list ::iterator new_end = remove_if(L.begin(), L.end(), compose2(logical_and (), bind2nd(greater (), 100), bind2nd(less (), 1000))); L.erase(new_end, L.end());
Gdzie szukać informacji? zhttp:// - implementacja STL firmy Silicon Graphics, Inc. (SGI) zhttp:// - książka "Designing Components with the C++ STL" zhttp:// - strona o STL z 1995 rokuhttp:// zhttp:// - prosty tutorialhttp:// zhttp:// - krótki opis STL'ahttp:// zhttp://pages.cpsc.ucalgary.ca/~kremer/STL/1024x768/index.html - strona o STL'uhttp://pages.cpsc.ucalgary.ca/~kremer/STL/1024x768/index.html
Dziękuję za uwagę!