STL
Transkrypt
STL
Wstęp do programowania
obiektowego
STL - Standard Template Library
1
STL – z ang. Standard Template Library,
(pol. standardowa biblioteka szablonów) –
biblioteka C++ zawierająca pojemniki,
iteratory, algorytmy, oraz inne konstrukcje
w formie szablonów, do używania w
programach.
2
Najważniejsze pojęcia STL
Pojemniki – ugrupowania elementów
takich samych typów. Najprostszym
pojemnikiem jest (nam znana) tablica.
Iteratory – konstrukcje umożliwiające
(najczęściej sekwencyjny) dostęp do
elementów pojemnika
Algorytmy generyczne – algorytmy
przetwarzające dane w pojemnikach
(często z użyciem iteratorów)
3
Jak to działa?
4
Dlaczego STL?
Elastyczność
Efektywność
Dobra specyfikacja i dokumentacja
5
STL Containers
(zbiorniki \ kontenery)
6
Zbiorniki sekwencyjne i operacje na nich
tablica
(standardowa)
vector
deque
list
Dodanie/usunięcie na
początku
brak
O(n)
O(1)
O(1)
Dodanie/usunięcie z końca
brak
O(1)
O(1)
O(1)
Dodanie/usunięcie ze
środka
brak
O(n)
O(n)
O(1)
Dostęp do pierwszego
elementu
O(1)
O(1)
O(1)
O(1)
Dostęp do ostatniego
elementu
O(1)
O(1)
O(1)
O(1)
Dostęp do elementu w
środku
O(1)
O(1)
O(1)
O(n)
7
Zbiornik vector
#include < iostream.h >
#include < vector.h >
int main () {
vector <double> 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:
32.1 40.5
8
#include <deque.h>
int main (){
deque<int> 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
9
#include < iostream.h >
#include < list.h >
int array1 [] = { 9, 16, 36 };
int array2 [] = { 1, 4 };
int main () {
list<int> l1 (array1, array1 + 3);
list<int> l2 (array2, array2 + 2);
list<int>::iterator i1 = l1.begin ();
l1.splice (i1, l2);
list< int >::iterator i2 = l1.begin ();
while (i2 != l1.end ())
cout << *i2++ << endl;
return 0;
}
1
4
9
16
36
10
Adaptacje kontenerów.
Stos i kolejkę można zaimplementować z
użyciem trzech podstawowych kontenerów
sekwencyjnych (vector, deque, list).
Adaptacja kolekcji dostarcza ograniczony
interfejs do kolekcji.
Adaptacje nie zawierają iteratorów.
Deklaracje:
stack <vector<int >> s;
stack <int> s;
//domyślnie deque<T>
11
Stack (stos)
Najlepiej używać z vector lub deque, można też z list,
ale jest to niepolecane. Operacje:
bool empty();
size_type size();
const value_type& top();
void push(const value_type&);
void pop();
12
Queue (kolejka)
Najlepiej używać z deque lub list, można też użyć vector’a, ale
jest to nieefektywne (do kolejki powinniśmy mieć dostęp z obu
stron). Operacje:
bool empty();
size_type size();
value_type& front();
const value_type& front();
value_type& back();
const value_type& back();
void push(const value_type&);
void pop();
13
priority_queue (kolejka priorytetowa)
Wstawianie odbywa się na miejscu określonym przez priorytet
elementu
Jako argument bierze typ sekwencyjny oraz funkcję
porównującą elementy
Najlepiej używać z vector’em lub deque (jeśli rozmiar jest
mocno dynamiczny). Nie można używać list, bo niezbędny
jest operator [] (indeksowanie).
Używa implementacji algorytmu kopcowego.
14
Metody priority_queue:
bool empty();
size_type size();
value_type& top();
const value_type& top();
void push(const value_type&);
void pop();
15
Kolekcje asocjacyjne
Uogólnienie kolekcji
Najczęściej używane typ kluczy to string
(napis)
Efektywna implementacja
16
Czym różnią się między sobą kolekcje
asocjacyjne?
Set: zawiera tylko klucze, operacje jak na
zbiorze
Multiset: jak set, tyle że może być wiele kopii
kluczy
Map: zbiór par (klucz, wartość)
Multimap: klucze mogą się powtarzać
17
Przykład użycia map
int main(){
map<const char*, int, ltstr> 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;
struct ltstr{
bool operator()(const char* s1, const char* s2) const {
return strcmp(s1, s2) < 0;
}
};
}
18
Przykład użycia multimap
struct ltstr {
bool operator()(const char* s1, const char* s2) const {
return strcmp(s1, s2) < 0;
}
};
int main(){
multimap<const char*, int, ltstr> m;
m.insert(pair<const char* const, int>("a", 1));
m.insert(pair<const char* const, int>("c", 2));
m.insert(pair<const char* const, int>("b", 3));
m.insert(pair<const char* const, int>("b", 4));
}
19
Alokatory
Zawierają funkcje alokacji i dealokacji
pamięci
Czarne skrzynki
20
Rodzaje alokatorów.
alloc
Domyślny alokator. Zazwyczaj ma najlepsze
charakterystyki, jest thread-safe.
pthread_alloc
Dla każdego wątku jest oddzielna pula pamięci.
Można tego używać wyłącznie jeśli system
operacyjny wspiera wielowątkowość. Jest
zazwyczaj 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.
21
Iteratory
Obiektowe uogólnienie wskaźników
Służą do iteracji po elementach
Są pośrednikami pomiędzy kolekcjami i
algorytmami
Możliwość pisania generycznych algorytmów
Bardzo przydatna jest arytmetyka wskaźników
(wyliczanie adresu za pomocą przesunięcia,
różnicy adresów, itp.)
22
Rodzaje iteratorów
Input Iterator
Output Iterator
Forward Iterator
Bidirectional Iterator
Random Access Iterator
Const Iterator
23
Input Iterator
24
Output Iterator
25
Forward Iterator
26
Bidirectional Iterator
27
Random Access Iterator
28
Algorytmy STL
Generyczne algorytmy oddzielają algorytm od
danych
„Nie zależą” od reprezentacji danych
Operują na iteratorach
29
Non-mutating (nie zmieniające
zawartości pojemnika)
template <class InputIterator, class UnaryFunction>
UnaryFunction for_each (InputIterator first, InputIterator last, UnaryFunction f);
template <class InputIterator, class EqualityComparable>
iterator_traits<InputIterator>::difference_type count (InputIterator first, InputIterator
last, const EqualityComparable& value);
template<class InputIterator, class EqualityComparable>
InputIterator find (InputIterator first, InputIterator last, const EqualityComparable&
value)
template <class InputIterator1, class InputIterator2>
bool equal (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2);
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1,
ForwardIterator2 first2, ForwardIterator2 last2);
30
Przykład (zliczanie wystąpień zera):
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;
}
template<class InputIterator, class EqualityComparable>
InputIterator find (InputIterator first, InputIterator last,
const EqualityComparable& value)
31
Przykład (wypisanie każdego
elementu tablicy):
template<class T> struct print : public unary_function<T, void>{
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<int> P = for_each(A, A + N, print<int>(cout));
cout << endl << P.count << " objects printed." << endl;
}
32
Mutating (zmieniające zawartość pojemnika)
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result);
void swap (Assignable& a, Assignable& b);
ForwardIterator2 swap_ranges(ForwardIterator1 first1, ForwardIterator1
last1, ForwardIterator2 first2);
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator
result, UnaryFunction op);
void replace(ForwardIterator first, ForwardIterator last, const T& old_value,
const T& new_value)
void fill(ForwardIterator first, ForwardIterator last, const T& value);
ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T&
value);
void reverse(BidirectionalIterator first, BidirectionalIterator last);
OutputIterator remove_copy(InputIterator first, InputIterator last,
OutputIterator result, const T& value);
33
Przykład (kopiowanie zawartości
vectora do listy):
vector<int> V(5);
iota(V.begin(), V.end(), 1);
// utworzenie vectora V
//wypełnienie rosnącymi wartościami
list<int> L(V.size());
copy(V.begin(), V.end(), L.begin());
assert(equal(V.begin(), V.end(), L.begin()));
34
Algorytmy sortowania:
void sort (RandomAccessIterator first, RandomAccessIterator last);
void stable_sort (RandomAccessIterator first,
RandomAccessIterator last);
bool is_sorted (ForwardIterator first, ForwardIterator last,
StrictWeakOrdering comp)
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2, OutputIterator result);
bool binary_search (ForwardIterator first, ForwardIterator last, const
LessThanComparable& value);
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator
last, const LessThanComparable& value);
35
Przykład (sortowanie tablicy):
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<int>(cout, " "));
wynik: " 1 2 4 5 7 8"
36
Algorytmy operujące na zbiorach
(zawieranie, suma, część wspólna, różnica)
bool includes (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2);
OutputIterator set_union (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2, OutputIterator result);
OutputIterator set_intersection (InputIterator1 first1, InputIterator1
last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
OutputIterator set_difference (InputIterator1 first1, InputIterator1
last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
37
Przykład (znajdowanie części wspólnej):
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' };
Wynik:
Intersection of A1 and A2: 1 3 5
Intersection of A3 and A4: a b b 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<int>(cout, " "));
cout << endl << "Intersection of A3 and A4: ";
set_intersection(A3, A3 + N3, A4, A4 + N4, ostream_iterator<char>(cout, " "), lt_nocase);
cout << endl;
}
38
Algorytmy kopcowe
void push_heap (RandomAccessIterator first,
RandomAccessIterator last);
//dodanie
void pop_heap (RandomAccessIterator first,
RandomAccessIterator last);
//zdjęcie
void make_heap (RandomAccessIterator first,
RandomAccessIterator last);
void sort_heap (RandomAccessIterator first,
RandomAccessIterator last);
bool is_heap (RandomAccessIterator first,
RandomAccessIterator last);
39
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<int>(cout, " "));
Wynik:
Before pop: 6 5 3 4 2 1
After pop: 5 4 3 1 2
A[N-1] = 6
pop_heap(A, A+N);
cout << endl << "After pop: ";
copy(A, A+N-1, ostream_iterator<int>(cout, " "));
cout << endl << "A[N-1] = " << A[N-1] << endl;
}
40
Obiekty funkcyjne (funktory)
Obiekty, które mogą być wołane jak funkcje
Zwykłe funkcje to też obiekty funkcyjne
Operator ()
Modele: Generator, Unary Function, Binary
Function
Predicate, Binary Predicate
Adaptacyjne obiekty funkcyjne
41
Przykłady:
vector<int> V(100);
generate(V.begin(), V.end(), rand);
struct less_mag : public binary_function<double, double, bool> {
bool operator()(double x, double y) { return fabs(x) < fabs(y); }
};
vector<double> V;
...
sort(V.begin(), V.end(), less_mag());
42
Przykład:
struct adder : public unary_function<double, void>
{
adder() : sum(0) {}
double sum;
void operator()(double x) { sum += x; }
};
vector<double> V;
...
adder result = for_each(V.begin(), V.end(), adder());
cout << "The sum is " << result.sum << endl;
43
Przykład:
list<int> L;
...
list<int>::iterator new_end =
remove_if(L.begin(), L.end(),
compose2(logical_and<bool>(),
bind2nd(greater<int>(), 100),
bind2nd(less<int>(), 1000)));
L.erase(new_end, L.end());
44
Dokumentacja i przykłady STL
http://www.sgi.com/tech/stl/ - implementacja STL firmy Silicon
Graphics, Inc. (SGI)
http://www.informatik.hs-bremen.de/~brey/stlbe.html - książka
"Designing Components with the C++ STL"
http://www.xraylith.wisc.edu/~khan/software/stl/STL.newbie.html
- strona o STL z 1995 roku
http://www.cs.brown.edu/people/jak/proglang/cpp/stltut/tut.html prosty tutorial
http://www.cs.rpi.edu/~wiseb/xrds/ovp2-3b.html - krótki opis STL'a
http://pages.cpsc.ucalgary.ca/~kremer/STL/1024x768/index.html strona o STL'u
45