Transcript STL
C++
wykład 13,14,15
(16/23/30.05.2012)
STL
STL
STL (ang. Standard Template Library) to
standardowa biblioteka wzorców w C++.
Wszystkie składniki STL są wzorcami.
STL jest rdzeniem biblioteki standardowej C++.
Wszystkie identyfikatory w bibliotece standardowej
C++ zdefiniowane są w przestrzeni nazw std.
Zaleta STL – mnóstwo gotowych szablonów klas
pozwalających programować na wyższym poziomie
abstrakcji.
Wada STL – brak oczywistości (koniecznie czytaj
dokumentację)
STL
Biblioteka STL składa się z kilku modułów:
kontenery (sekwencyjne, asocjacyjne),
iteratory (dwukierunkowe, z dostępem
swobodnym),
algorytmy,
a także funktory (obiekty funkcyjne, predykaty),
łańcuchy, strumienie.
Pary
Szablon struktury pair<> (zdefiniowany w <utility>) umożliwia
potraktowanie dwóch wartości jako pojedynczego elementu.
Para posiada dwa pola: first i second.
Para posiada konstruktor dwuargumentowy oraz domyślny i kopiujący.
Pary można porównywać (operatory == i <).
Istnieje szablon funkcji make_pair() do tworzenia pary.
Przykłady:
void f (std::pair<int, const char *>);
void g (std::pair<const int, std::string>);
…
std::pair<int, const char *> p(44,”witaj”);
f(p); // wywołuje domyślny konstruktor kopiujący
g(p); // wywołuje konstruktor wzorcowy
g(std::make_pair(44,”witaj”)); // przekazuje dwie
// wartości jako parę z wykorzystaniem konwersji
// typów
Pary są wykorzystywane w kontenerach map i multimap.
Ograniczenia liczbowe
Typy numeryczne posiadają ograniczenia zależne od platformy i
są zdefiniowane w szablonie numeric_limits<>
(zdefiniowany w <limits>, stałe preprocesora są nadal
dostępne w <climits> i <cfloat>).
Wybrane składowe statyczne szablonu numeric_limits<>:
is_signed, is_integer, is_exact, is_bounded,
is_modulo, has_infinity, has_quiet_NaN,
min(), max(), epsilon().
Przykłady:
numeric_limits<char>::is_signed;
numeric_limits<short>::is_modulo;
numeric_limits<long>::max();
numeric_limits<float>::min();
numeric_limits<double>::epsilon();
Minimum i maksimum
Obliczanie wartości minimalnej oraz maksymalnej:
template <class T>
inline const T& min (const T &a, const
{ return b<a ? b : a; }
template <class T>
inline const T& max (const T &a, const
{ return a<b ? b : a; }
Istnieją też wersje tych szablonów z komparatorami
funkcyjny):
template <class T, class C>
inline const T& min (const T &a, const
{ return comp(b,a) ? b : a; }
template <class T>
inline const T& max (const T &a, const
{ return comp(a,b) ? b : a; }
T &b)
T &b)
(funkcja lub obiekt
T &b, C comp)
T &b, C comp)
Minimum i maksimum
Przykład 1:
bool int_ptr_less (int *p, int *q) {
return *p<*q; }
…
int x = 33, y = 44;
int *px = &x, *py = &y;
int *pmax = std::max(px,py,int_ptr_less);
Przykład 2:
int i;
long l;
…
l = max(i,l); // BŁĄD
// niezgodne typy argumentów
l = std::max<long>(i,l); // OK
Zamiana wartości
Zamiana dwóch wartości:
template <class T>
inline void swap (T &a, T &b)
{ T tmp(a); a = b; b = tmp; }
Przykład:
int x = 33, y = 44;
…
std::swap(x,y);
Operatory porównywania
Cztery funkcje szablonowe (zdefiniowane w <utility>) na podstawie
operatorów == i < definiują operatory porównań !=, <=, >= i >.
Funkcje te są umieszczone w przestrzeni nazw std::rel_ops.
Przykład:
class X {
…
public:
bool operator== (const X &x) const throw();
bool operator< (const X &x) const throw();
…
};
…
void foo () {
using namespace std::rel_ops;
X x1, x2;
…
if (x1!=x2) { … }
…
}
Kontenery
Kontenery służą do przechowywania i
zarządzania kolekcjami danych.
Rodzaje kontenerów:
Kontenery sekwencyjne, gdzie każdy element ma
określoną pozycję. Na przykład: vector, deque,
list.
Kontenery uporządkowane (w tym asocjacyjne),
gdzie pozycja elementu zależy od jego wartości.
Na przykład: set, multiset, map, multimap.
Elementy kontenerów
Elementy kontenerów muszą spełniać wymagania
podstawowe:
element musi być kopiowalny (konstruktor kopiujący),
element musi być przypisywalny (przypisanie kopiujące),
element musi być zniszczalny (publiczny destruktor).
W pewnych sytuacjach elementy kontenerów muszą
spełniać wymagania dodatkowe:
konstruktor domyślny (utworzenie niepustego kontenera),
operator porównywania == (wyszukiwanie),
operator porównywania < (kryterium sortowania).
Semantyka wartości
a semantyka referencji
Kontenery STL realizują semantykę wartości:
tworzą wewnętrzne kopie swoich elementów
oraz zwracają kopie tych elementów.
Semantykę referencji można
zaimplementować samodzielnie za pomocą
inteligentnych wskaźników – wskaźniki te
mają umożliwiać zliczanie referencji dla
obiektów, do których odnoszą się wskaźniki.
Kontenery sekwencyjne
– wektory
Wektor vector<> (zdefiniowany w <vector>) przechowuje
swoje elementy w tablicy dynamicznej.
Uzyskujemy szybki dostęp do każdego elementu za pomocą
indeksowania.
Dołączanie i usuwanie elementów na końcu wektora jest bardzo
szybkie, ale wstawienie lub usunięcie elementu ze środka
zabiera więcej czasu.
Przykład:
vector<int> coll;
…
for (int i=1; i<=6; ++i)
coll.push_back(i);
…
for (int i=0; i<coll.size(); ++i)
cout << coll[i] << ’ ’;
cout << endl;
Kontenery sekwencyjne
– kolejki o dwóch końcach
Kolejka o dwóch końcach deque<> (zdefiniowana w <deque>)
przechowuje swoje elementy w tablicy dynamicznej, która może rosnąć
w dwie strony.
Uzyskujemy szybki dostęp do każdego elementu za pomocą
indeksowania.
Dołączanie i usuwanie elementów na końcu i na początku kolejki jest
bardzo szybkie, ale wstawienie lub usunięcie elementu ze środka
zabiera więcej czasu.
Przykład:
deque<float> coll;
…
for (int i=1; i<=6; ++i)
coll.push_front(i*1.234);
…
for (int i=0; i<coll.size(); ++i)
cout << coll[i] << ’ ’;
cout << endl;
Kontenery sekwencyjne
– listy
Lista list<> (zdefiniowana w <list>) przechowuje swoje
elementy w liście dwukierunkowej.
W listach nie ma swobodnego dostępu do elementów kolekcji.
Dołączanie i usuwanie elementów na końcu i na początku listy
jest bardzo szybkie, ale dostanie się do elementu ze środka
zabiera dużo czasu.
Przykład:
list<char> coll;
…
for (char c=’a’; c<=’z’; ++c)
coll.push_back(c);
…
while (!coll.empty()) {
cout << coll.front() << ’ ’;
coll.pop_front(); }
cout << endl;
Kontenery sekwencyjne
– łańcuchy i tablice
Obiektów klas łańcuchowych, czyli
basic_string<>, string i wstring,
można używać jak kontenerów
sekwencyjnych. Są one podobne w
zachowaniu do wektorów.
Innym rodzajem kontenera może być tablica.
Nie jest to klasa i nie ma żadnych metod ale
konstrukcja STL umożliwia uruchamianie na
tablicach różnych algorytmów (tak jak na
kontenerach).
Kontenery uporządkowane
Kontenery uporządkowane wykonują automatycznie sortowanie
swoich elementów.
Asocjacyjne kontenery uporządkowane przechowują pary kluczwartość (odpowiednio first i second) i sortowanie następuje
po kluczach.
Domyślnie elementy lub klucze są porządkowane przy pomocy
operatora <.
Kontenery uporządkowane są implementowane w postaci
zrównoważonych drzew BST (drzewa czerwono-czarne).
Wszystkie kontenery uporządkowane posiadają domyślny
parametr wzorca służący sortowaniu (domyślnym jest operator
<).
Rodzaje kontenerów: zbiory set<>, wielozbiory multiset<>,
mapy map<> i multimapy multimap<>.
Adaptatory kontenerów
Adaptatory kontenerów to kontenery
wykorzystujące ogólną strukturę innych
kontenerów do realizacji pewnych
specyficznych potrzeb.
Adaptatorami kontenerów są stosy
stack<>, kolejki queue<> i kolejki
priorytetowe priority_queue<>.
Iteratory
Iterator to specjalny obiekt, który potafi iterować po elementach
kolekcji.
Iterator ma zaimplementowaną semantykę wskaźnika – posiada
operator wyłuskania elementu *, operatory przechodzenia do
elementówsąsiednich ++ i -- oraz operatory porównywania
pozycji == i !=.
Wszystkie kontenery udostępniają funkcje tworzące iteratory do
nawigowania po ich elementach – funkcja begin() zwraca
iterator wskazujący na pozycję z pierwszym elementem w
kolekcji a funkcja end() zwraca iterator wskazujący pozycję za
ostatnim elementem.
Każdy kontener definiuje dwa typy iteratorów –
kontener::iterator przeznaczony do iterowania po
elementach z możliwością odczytu i zapisu oraz
kontener::const_iterator przeznaczony do iterowania po
elementach tylko z możliwością odczytu.
Iteratory
Przykład 1:
list<char> coll;
…
list<char>::const_iterator pos;
for (pos=coll.begin(); pos!=coll.end(); ++pos)
cout << *pos << ’ ’;
cout << endl;
Przykład 2:
list<char> coll;
…
list<char>::iterator pos;
for (pos=coll.begin(); pos!=coll.end(); ++pos)
*pos = toupper(*pos);