Biblioteka STL - literatura Biblioteka STL - wstęp STL - Standard Template Library - Standardowa Biblioteka Szablonów Nicolai M. Josuttis - "C Biblioteka standardowa. Podręcznik programisty", Helion, 2003 Ray Lischner - "STL. Leksykon kieszonkowy", Helion, 2004 Bjarne Stroustrup - "Język C", WNT, 2002 Scott Meyers - "STL w praktyce. 50 sposobów efektywnego wykorzystania", Helion, 2004 realizuje paradygmat programowania uogólnionego (generic programming) ogólne algorytmy działające na danych dowolnego (prawie dowolnego) typu, np. ogólny algorytm sortowania (wystarczy, Ŝe umiemy porównać dwa elementy) ogólne struktury danych przechowujące obiekty dowolnego (prawie dowolnego) typu, np. stos "czegokolwiek" (wystarczy, Ŝe przechowywane obiekty moŝna kopiować) paradygmat ten jest całkowicie odrębny i częściowo niezgodny z paradygmatem programowania obiektowego www.sgi.com/tech/stl Jan Bródka Wydział MiNI PW 1 Jan Bródka Wydział MiNI PW 2 Biblioteka STL - przegląd Biblioteka STL - kwestie techniczne Kontenery - przechowują kolekcje obiektów wskazanego (prawie dowolnego) typu, zapewniają efektywną implementację (z gwarantowaną wydajnością odpowiednich operacji) takich konstrukcji jak np. wektory (dynamicznie rozszerzalne tablice), listy, kolejki i zbiory Algorytmy - dostarczają efektywnych sposobów przetwarzania danych (równieŝ z gwarantowaną wydajnością) przechowywanych w kontenerach. Są niezaleŝne od kontenerów, ten sam algorytm (np. wyszukiwanie, sortowanie) moŝna zastosować do róŝnych kontenerów Iteratory - uogólniają pojęcie wskaźnika, umoŝliwiają "przechodzenie" przez elementy kolekcji (kontenera). Stanowią element pośredniczący miedzy algorytmem a kontenerem. Dla kaŝdego kontenera istnieje współpracujący z nim typ iteratora. Algorytmy "nie wiedzą" na jakim kontenerze działają, operują jedynie na iteratorach. Funktory - obiekty (klasy) z przedefiniowanym operatorem wywołania funkcji, dzięki temu w algorytmach mogą być stosowane wszędzie tam gdzie oczekiwane są funkcje nie tylko STL, ale znaczna część biblioteki standardowej C to szablony w rzeczywistości zamiast klasy iostream, jest deklaracja typedef basic_iostream<char, char_traits<char> > iostream; w rzeczywistości zamiast klasy string, jest deklaracja typedef basic_string<char, char_traits<char>, allocator<char> > string; opisy błędów kompilacji będą zawierały zamiast nazw uproszczonych ich w pełni rozwinięte wersje elementy biblioteki STL (podobnie jak cała biblioteka standardowa) znajdują się w przestrzeni nazw std jeśli nie napiszemy deklaracji using namespace std; to dostaniemy "bardzo dziwne" komunikaty o błędach Jan Bródka Wydział MiNI PW 3 Jan Bródka Wydział MiNI PW 4
Kontenery - przegląd Iteratory kontenery sekwencyjne wektor <vector> lista <list> kolejka o dwóch końcach <deque> napis <string> kontenery asocjacyjne zbiór i multizbiór <set> mapa i multimapa <map> "prawie" kontenery adaptory kontenerów stos <stack> kolejka <queue> kolejka priorytetowa <queue> zbiór bitowy <bitset> "zwykła" tablica uogólniają pojęcie wskaźnika, dzięki nim na kontenery moŝna patrzeć jak na ciągi, których elementy moŝna przeglądać kolejno kaŝdy z kontenerów definiuje swój typ iteratora np. dla wektora, którego elementami są liczby double typem iteratora jest vector<double>::iterator iteratory, podobnie jak kontenery, nie tworzą hierarchii klas (nie ma bazowej klasy "ogólny iterator" ani "ogólny kontener") odpowiednie iteratory są zdefiniowane w plikach nagłówkowych dla kontenerów, pewne dodatkowe klasy i funkcje związane z iteratorami są zdefiniowane w pliku <iterator> iteratory często słuŝą do określania zakresów, np. algorytmy działają na zakresach określonych przez parę iteratorów zakres [first,last) oznacza, Ŝe first jest pierwszym elementem z zakresu, natomiast last jest bezpośrednio po ostatnim elemencie zakresu, czyli element wskazywany przez last nie naleŝy do zakresu Jan Bródka Wydział MiNI PW 5 Jan Bródka Wydział MiNI PW 6 Iteratory - cd. Kategorie iteratorów nieprawidłowe korzystanie z iteratorów powoduje podobne problemy jak nieprawidłowe korzystanie ze wskaźników, w szczególności problemy pojawią się jeśli iterator nie jest poprawnie zainicjowany wyszedł poza ostatni element kontenera wskazuje na usunięty element lub zlikwidowany kontener dodatkowo naleŝy zwracać uwagę na tak zwane uniewaŝnianie iteratorów, które moŝe nastąpić w wyniku wykonania operacji na kontenerze. Zasady uniewaŝniania iteratorów są róŝne dla róŝnych kontenerów, np. dla wektora operacja wstawienia elementu uniewaŝnia wszystkie iteratory związane z tym wektorem nie ma czegoś takiego jak "zerowy" iterator wejściowy wyjściowy postępujący dwukierunkowy swobodny odczyt v=*p p->x zapis *p=v inkrementacja p p dekrementacja --p p-- porównania p1==p2 p1!=p2 przypisanie p1=p2 dodatkowe Jan Bródka Wydział MiNI PW 7 Jan Bródka Wydział MiNI PW 8
Iteratory wejściowe i wyjściowe Iteratory postępujące i dwukierunkowe iteratory wejściowe słuŝą do jednorazowego odczytywania informacji kaŝdy element moŝna odczytać dokładnie raz, tzn. jeśli utworzymy kopię iteratora, a potem oba będziemy przesuwać otrzymamy inne wyniki iteratorami wejściowmi są iteratory związane ze strumieniami wejściowymi iteratory wyjściowe słuŝą do zapisywania informacji dwukrotny zapis wartości bez przesuwania iteratora nie powoduje nadpisania poprzedniej informacji, a zapis w nowym miejscu nie ma Ŝadnej kontroli poprawności wykonanego zapisu iteratorami wyjściowymi są iteratory związane ze strumieniami wyjściowymi oraz insertery (wstawiacze) dla iteratorów wejściowych i wyjściowych praktycznie nie moŝna rozdzielić operacji odczytu/zapisu od inkrementacji iteratory postępujące są kombinacją iteratorów wejściowych i wyjściowych, za ich pomocą moŝna zarówno odczytywać jak i zapisywać elementy kolekcji kaŝdy z elementów moŝna przetwarzać wielokrotnie w przypadku zapisu następuje nadpisanie poprzedniej wartości, a nie dodanie nowej jak dla iteratorów wyjściowych nie są kategorią związaną z Ŝadną standardową kolekcją iteratory dwukierunkowe mają wszystkie cechy iteratorów postępujących i dodatkową moŝliwość cofania się do poprzedniego elementu iteratorami dwukierunkowymi są iteratory związane z listami i wszystkimi kontenerami asocjacyjnymi Jan Bródka Wydział MiNI PW 9 Jan Bródka Wydział MiNI PW 10 Iteratory o dostępie swobodnym Iteratory - uzupełnienie są dokładnym odpowiednikiem wskaźników dodatkowe operację (oprócz wszystkich dla dwukierunkowych) indeksowanie - swobodny dostęp do n-tego elementu kolekcji iter[n] przesunięcie o n elementów itern, niter, iter-n, iter=n, iter-=n odejmowanie iteratorów (wskazujących elementy tej samej kolekcji) iter1-iter2 relacje pomiędzy iteratorami (wskazujących elementy tej samej kolekcji) iter1<iter2, iter1>iter2, iter1<=iter2, iter1>=iter2 iteratorami o dostępie swobodnym są iteratory związane z wektorami, kolejkami o dwóch końcach, napisami (string) wskaźniki do elementów tablic moŝna traktować jako iteratory o dostępie swobodnym wszystkie kategorie iteratorów udostępniają konstruktor kopiujący wszystkie kategorie iteratorów oprócz wyjściowych udostępniają konstruktor bezparametrowy wszystkie operacje na iteratorach wykonują się w stałym (tzn. niezaleŝnym od rozmiaru kolekcji) czasie (dlatego np. indeksowanie dostępne jest jedynie dla iteratorów swobodnych, dla innych nie da się go zrealizować w czasie stałym) ale to nie znaczy Ŝe wszystkie w takim samym czasie np. p jest szybsze (i dla tego zalecane) niŝ p kaŝdy z kontenerów oprócz odpowiedniego typu iterator udostępnia równieŝ typ const_iterator - za jego pośrednictwem nie moŝna zmienić wartości elementów (lepiej korzystać ze zwykłych iteratorów) np. vector<int>::iterator it1 // zwykły iterator vector<int>::const_iterator it2 // stałe elementy const vector<int>::iterator it3 // stały iterator Jan Bródka Wydział MiNI PW 11 Jan Bródka Wydział MiNI PW 12
Iteratory wstawiające - wstęp Iteratory wstawiające - przykład "zwykłe" iteratory związane z kolekcjami nie nadają się do wstawiania (dodawania) nowych elementów do kolekcji #include <list> using using namespace std; std; int int tab[] tab[] = 1, 2, 3 ; ; list<int> l; l; list<int>::iterator it it = l.begin(); for for ( int int i=0 i=0 ; i<3 i<3 ; i,it ) *it=tab[i]; // // błąd! błąd! - próba próba nadpisania // // na na nieistniejącym elemencie do dodawania nowych elementów do kolekcji słuŝą iteratory wstawiające #include <list> using using namespace std; std; int int tab[] tab[] = 1, 2, 3 ; ; list<int> l; l; back_insert_iterator<list<int>> it(l); // // zmiana! for for ( int int i=0 i=0 ; i<3 i<3 ; i) i) // // it it pominięte *it=tab[i]; // // moŝna moŝna napisać it=tab[i] Jan Bródka Wydział MiNI PW 13 Jan Bródka Wydział MiNI PW 14 Iteratory wstawiające - szczegóły (1) Iteratory wstawiające - szczegóły (2) operacje it oraz it są dla iteratorów wstawiających operacjami pustymi i zaleca się ich nie pisać operacja *it=v (i równowaŝny zapis it=v) wstawia element o wartości v do kolekcji wywołując odpowiednią metodę kontenera, dlatego w deklaracji iteratora wstawiającego musi być podany typ kontenera (parametr szablonu), a w konstruktorze wskazany konkretny kontener powyŝszy sposób implementacji iteratorów wstawiających spełnia wymagania iteratorów wyjściowych deklaracje back_insert_iterator<tc<te>> iter(cont) // wstawiacz końcowy front_insert_iterator<tc<te>> iter(cont) // wstawiacz początkowy insert_iterator<tc<te>> iter(cont,pos) // wstawiacz ogólny gdzie Tc<Te> - kontener Tc (np. vector) o elementach Te (np. int) iter - deklarowany wstawiacz cont - konkretny kontener, pos - pozycja (zwykły iterator) wstawiacze końcowe (back inserters) wykorzystują metodę kontenera push_back(v) są dostępne dla wszystkich kontenerów sekwencyjnych (bo mają one metodę push_back) wstawiacze początkowe (front inserters) wykorzystują metodę kontenera push_front(v) są dostępne dla list i kolejek o dwóch końcach (bo tylko one mają metodę push_front) wstawiacze ogólne (inserters) wykorzystują metodę kontenera insert(v,it) są dostępne dla wszystkich kontenerów standardowych, ale dla kontenerów sekwencyjnych wstawiają element na wskazaną przez it pozycję dla kontenerów asocjacyjnych wstawiają zgodnie z uporządkowaniem Jan Bródka Wydział MiNI PW 15 Jan Bródka Wydział MiNI PW 16
Iteratory strumieniowe Iteratory strumieniowe - przykład iteratory strumieniowe wyjściowe są w pełni analogiczne do iteratorów wstawiających, z tym Ŝe "kolekcją", do której wstawiamy jest strumień wyjściowy deklaracja (T - typ elementu) ostream_iterator<t> iter(os); // iterator związany z os ostream_iterator<t> iter(os,delim); // iterator związany z os gdzie delim - separator elementów (char *) iteratory strumieniowe wejściowe "kolekcją", z której czytamy jest strumień wejściowy deklaracja (T - typ elementu) istream_iterator<t> iter(is); // iterator związany z is istream_iterator<t> iter(); // iterator "końca strumnienia" mogą być zaimplementowane w róŝny sposób wczytanie danych odbywa się w chwili wykonania *iter wczytanie danych odbywa się w chwili wykonania iter (iter) oraz deklaracji (!), *iter jest operacją pustą vector<int> v; v; back_insert_iterator<vector<int>> ins(v); istream_iterator<int> we(cin); istream_iterator<int> we_end; do do ins=*we; we; we; while while ( we!=we_end ); ); ostream_iterator<int> wy(cout,", "); "); copy(v.begin(),v.end(),wy); Jan Bródka Wydział MiNI PW 17 Jan Bródka Wydział MiNI PW 18 Iteratory wsteczne Iteratory wsteczne - przykład kaŝdy z kontenerów standardowych dostarcza typ iteratora umoŝliwiający przeglądanie elementów kolekcji w kolejności odwrotnej begin() end() vector<int> v; v; back_insert_iterator<vector<int>> ins(v); for for ( int int i=1 i=1 ; i<=5 i<=5 ; i i ) ins=i; rend() rbegin() vector<int>::iterator it1(v.begin()2); cout cout << << *it1 *it1 << << endl; endl; // // wynik: 3 deklaracja iteratora wstecznego (odwrotnego) rit jest następująca Tc<Te>::reverse_iterator rit; Tc<Te>::reverse_iterator rit(it); // inicjowany iteratorem it gdzie Tc<Te> - kontener Tc (np. vector) o elementach Te (np. int) np. vector<int>:: reverse_ iterator r_iter; Jan Bródka Wydział MiNI PW 19 vector<int>::reverse_iterator rit(it1); cout cout << << *rit *rit << << endl; endl; // // wynik: 2 vector<int>::iterator it2; it2; it2=rit.base(); // // base() zwraca iterator bazowy // // dla dla odwrotnego cout cout << << *it2 *it2 << << endl; endl; // // wynik: 3 Jan Bródka Wydział MiNI PW 20