36
1 Dzisiejszy wykład Standardowa biblioteka języka C+ + STL (Standard Template Library)

Dzisiejszy wykład

  • Upload
    kaia

  • View
    54

  • Download
    3

Embed Size (px)

DESCRIPTION

Dzisiejszy wykład. Standardowa biblioteka języka C++ STL ( Standard Template Library ). STL – ogólne spojrzenie. Kolekcje. Klasy które zawierają inne obiekty. Iteratory. „Wskaźniki” do elementów kolekcji, używane do indeksowania kolekcji. Adaptery. - PowerPoint PPT Presentation

Citation preview

Page 1: Dzisiejszy wykład

1

Dzisiejszy wykład

Standardowa biblioteka języka C++ STL (Standard Template Library)

Page 2: Dzisiejszy wykład

2

STL – ogólne spojrzenie

STL – biblioteka komponentów, których można wielokrotnie używać

Dostarcza programiście C++ kolekcje, algorytmy, iteratory itd. Łatwa w użyciu, o bardzo dużych możliwościach i efektywna Programowanie uogólnione, nie obiektowe

http://www.sgi.com/tech/stl

Obiekty alokujące pamięćAlokatory

Klasy które dostosowują do siebie inne klasy

Adaptery

„Wskaźniki” do elementów kolekcji, używane do indeksowania kolekcji

Iteratory

Klasy które zawierają inne obiektyKolekcje

Page 3: Dzisiejszy wykład

3

Ważniejsze kolekcje w STL

Kolekcja wartości typu T indeksowana przez unikatowe wartości typu Key

map<Key,T>

Kolekcja unikatowych wartości typu Keyset<Key>

Typowa implementacja stosu.stack<T>

Liniowy czas dostępu, zmienna długość, stały czas wstawiania/usuwania w dowolnym miejscu listy

list<T>

Dostęp swobodny, zmienna długość, stały czas wstawiania/usuwania elementów na obu końcach

deque<T>

Dostęp swobodny, zmienna długość, stały czas wstawiania/usuwania elementów na końcu

vector<T>

Page 4: Dzisiejszy wykład

4

Elementy wspólne dla wszystkich kolekcji

Niektóre funkcje składowe występują we wszystkich kolekcjach, np.:

size() zwraca liczbę elementów w kontenerze push_back() dodaje obiekt „na końcu” kontenera

Dostęp bezpośredni do danych w kolekcjach poprzez operator[] lub składową at()

Iteratory metoda dostępu do elementów kolekcji, z

użyciem pętli i „indeksu” wiele różnych iteratorów: jednokierunkowy,

odwrotny, stały itd.

Page 5: Dzisiejszy wykład

5

STL vector

Wzorzec vector zachowuje się jak dynamicznie alokowana tablica, ale dodatkowo umożliwia dynamiczną zmianę rozmiaru (przy dodawaniu elementów przez insert() lub push_back())

vectordeklaracje:

vector<int> iVector;vector<int> jVector(100);vector<int> kVector(Size); // Size is int var

vectordostęp do elementów:

jVector[23] = 71; // set memberjVector[41]; // get memberjVector.at(23); // get memberjVector.front(); // get first memberjVector.back(); // get last member

vectorobserwatorzy:

jVector.size(); // num elements in containerjVector.capacity(); // capacity of containerjVector.max_capacity(); // max capacity of elementsjVector.empty();

Page 6: Dzisiejszy wykład

6

Konstruktory wzorca vector

Wzorzec vector posiada kilka konstruktorówvector<T> V; //empty vector

vector<T> V(n,value); //vector with n copies of value

vector<T> V(n); //vector with n copies of default for T

Wzorzec vector posiada również przeciążony konstruktor kopiujący i operator przypisania wykonujące głębokie kopiowanie

Page 7: Dzisiejszy wykład

7

Wzorzec vector - przykład

Uwaga: pojemność wektora nie zwiększa się automatycznie przy dostępie przy pomocy operatora indeksowania. Użycie metod insert() oraz push_back() do dodania elementów tablicy powiększy ją w miarę potrzeby.

#include <iostream>#include <vector> // for vector template definitionusing namespace std;

int main() {int MaxCount = 100;vector<int> iVector(MaxCount);for (int Count = 0; Count < MaxCount; Count++) {

iVector[Count] = Count;}

}

Początkowy rozmiar wektora

Dostęp, jak do tablicy

Page 8: Dzisiejszy wykład

8

Indeksowanie wzorca vector

W najprostszym przypadku, obiekt typu vector może być używany jako prosta tablica z dynamiczną alokacją pamięci

Brak sprawdzania zakresu indeksu Brak dynamicznego rozszerzania. Błędny indeks spowoduje błąd

dostępu (jeśli mamy szczęście)

Użycie składowej at() spowoduje wyjątek out_of_range w tej sytuacji

int MaxCount = 100;vector<int> iVector(MaxCount);...for (int Count = 0; Count < 2*MaxCount; Count++) {

cout << iVector[Count];

int MaxCount = 100;vector<int> iVector(MaxCount);...for (int Count = 0; Count < 2*MaxCount; Count++) {

cout << iVector.at(Count);

Wydajność

Bezpieczeństwo

Page 9: Dzisiejszy wykład

9

Iteratory STL

Iterator obiekt, który przechowuje lokalizację wewnątrz odpowiadającej mu

kolekcji STL, umożliwiający przechodzenie po wszystkich elementach kolekcji (inkrementacja/dekrementacja), wyłuskanie oraz wykrywanie osiągnięcia krańców zakresu w kolekcji

Iterator jest deklarowany dla konkretnego typu kolekcji i jego implementacja jest zależna od tego typu oraz nie ma szczególnego znaczenia dla użytkownika

Iteratory są podstawą wielu algorytmów STL i są niezbędnym narzędziem do dobrego wykorzystania kolekcji STL

Każda kolekcja STL zawiera składowe begin() i end(), które zwracają wartości iteratorów wskazujących odpowiednia na pierwszy element, oraz element "następny za końcowym"

Page 10: Dzisiejszy wykład

10

Iterator wektora

Iterator wektora zachowuje się jak wskaźnik do dynamicznie zaalokowanej tablicy

vector<T> v;

vector<T>::iterator idx;

for (idx = v.begin(); idx != v.end(); ++idx)

do something with *idx

deklaracja iteratora:

vector<int>::iterator idx;vector<int> jVector;

uzyskanie iteratoraz wektora:

jVector.begin(); // gets iteratorjVector.end(); // gets sentinel (iterator)

dostęp do elementów wektora poprzez iterator:

idx[i]; // access ith element*idx; // access to element pointed by idxidx++; // moves pointer to next elementidx--; // moves pointer to previous element

Page 11: Dzisiejszy wykład

11

Typy iteratorówRóżne kolekcje posiadają różne rodzaje iteratorów

iteratory jednokierunkowe - definiują tylko ++ iteratory dwukierunkowe - definiują ++ i -- iteratory o dostępie swobodnym - definiują ++, -- oraz:

dodawanie i odejmowanie liczby całkowitej r+n, r-n zmianę o liczbę całkowitą r+=n, r-=n odejmowanie iteratorów r-s zwraca liczbę całkowitą kolekcja posiada operator indeksowania []

Iteratory do stałej i zmiennej iteratory do stałej - *p nie pozwala na zmianę elementu kolekcji iteratory do zmiennej - umożliwiają zmiany w kolekcji

for (p = v.begin(); p != v.end(); ++p)

*p = new value

Iteratory odwrotne - pozwalają na przeglądanie elementów kolekcji od końca do początku

reverse_iterator rp;

for (rp = v.rbegin(); rp != v.rend(); ++rp)

process *rp

Page 12: Dzisiejszy wykład

12

Iteratory do stałej

Iteratory do stałej muszą być użyte dla stałych kontenerów - przeważnie przekazywanych jako parametry

Typ jest zdefiniowany w konenerze: vector<T>::const_iterator

void ivecPrint(const vector<int>& V, ostream& Out) {vector<int>::const_iterator It; // MUST be

const

for (It = V.begin(); It != V.end(); ++It) {cout << *It;

}cout << endl;

}

Page 13: Dzisiejszy wykład

13

Przykład zastosowania iteratora

Poniższy przykład tworzy kopię wektora BigInt

Wektor Copy ma początkowo zerowy rozmiar. push_back() powiększy docelowy wektor do odpowiednich rozmiarówStosujemy przedrostkowy, a nie przyrostkowy operator inkrementacji iteratora

string DigitString = "45658228458720501289";vector<int> BigInt;

for (int i = 0; i < DigitString.length(); i++) {BigInt.push_back(DigitString.at(i) - '0');

}vector<int> Copy;vector<int>::iterator It;for (It = BigInt.begin(); It != BigInt.end(); ++It) {

Copy.push_back(*It);}

Inicjalizacja iteratora Wartość pozwalająca na zakończenie operacji

Przesunięcie iteratora do następnego elementu

Page 14: Dzisiejszy wykład

14

Operacje na iteratorach

Każdy iterator zapewnia pewne usługi za pośrednictwem standardowego interfejsu

string DigitString = "45658228458720501289";vector<int> BigInt;

for (int i = 0; i < DigitString.length(); i++) {BigInt.push_back(DigitString.at(i) - '0');

}

vector<int>::iterator It;

It = BigInt.begin();int FirstElement = *It;

It++;

It = BigInt.end();

It--;int LastElement = *It;

Stwórz iterator do obiektu typu vector<int>

Wskaż na pierwszy element BigInt i skopiuj go

Przesuń się do drugiego elementu BigInt

Teraz It nie wskazuje na żaden element BigInt.Próba wyłuskania może spowodować błąd dostępu.

Cofnij się do ostatniego elementu BigInt

Page 15: Dzisiejszy wykład

15

Wstawianie elementów do wektorów

Wstawianie elementów na końcu wektora (z użyciem push_back()) jest najbardziej efektywne.

Wstawianie w innym miejscu wymaga przesuwania danych w pamięci

Wektor zachowuje się jak tablica o zmiennej długości Wstawianie elementów do wektora powiększa

zaalokowany obszar np. dwa razy

Wstawienie elementu unieważnia wszystkie iteratory wskazujące na elementy po punkcie wstawianiaRealokacja (powiększenie) wektora unieważnia wszystkie iteratory do elementów danego wektoraMożna ustawić minimalny rozmiar wektora V przy pomocy metody V.reserve(n)

Page 16: Dzisiejszy wykład

16

Składowa insert()

W dowolnym miejscu wektora można wstawić element używając iteratora i składowej insert()

Jest to najgorszy przypadek;element jest wstawiany zawsze napoczątku sekwencji, co maksymalniezwiększa liczbę kopiowanych elementów

Istnieją inne wersje metody insert(),wstawiające dowolną liczbę kopii danej wartości i wstawiające sekwencję elementów innego wektora

vector<int> Y;for (int m = 0; m < 100; m++) {

Y.insert(Y.begin(), m);

cout << setw(3) << m<<

setw(5) << Y.capacity()<<

endl;}

Index Cap0 11 22 43 44 8

. . .8 16

. . .15 1616 32

. . .31 3233 6463 64

. . .64 128

Page 17: Dzisiejszy wykład

17

Usuwanie elementów wektorów

Tak samo jak wstawianie, kasowanie elementów wektora wymaga przesunięć jego elementów (poza szczególnym przypadkiem ostatniego elementu)

Usuwanie ostatniego elementu: V.pop_back()

Usuwanie elementu wskazywanego przez iterator It: V.erase(It)

Usuwanie unieważnia iteratory do elementów po punkcie usuwania, a więc

j = V.begin();

while (j != V.end())

V.erase(j++);

nie działa

Usuwanie grupy elementów:V.erase(Iter1, Iter2)

Page 18: Dzisiejszy wykład

18

Porównywanie kolekcji

Dwie kolekcje są równe, jeżeli: mają ten sam rozmiar elementy na wszystkich pozycjach są równe

Klasa elementu wektora musi posiadać operator porównania

Dla innych porównań klasa elementu musi posiadać odpowiedni operator (<, >, ...)

Page 19: Dzisiejszy wykład

19

Kolekcja deque

deque double-ended queue

Umożliwia wydajne wstawianie na obu końcach

Umożliwia wstawianie/usuwanie dowolnych elementów posługując się iteratorami

Oprócz metod klasy vector posiada dodatkowo metody push_front() i pop_front()

Większość metod i konstruktorów identyczna jak dla wektora

Wymaga pliku nagłówkowego <deque>

Page 20: Dzisiejszy wykład

20

Lista

Dwukierunkowa lista z dowiązaniami

Nie umożliwia dostępu swobodnego, ale umożliwia wstawianie i usuwanie elementów na dowolnej pozycji w stałym czasie

Pewne różnice w metodach w stosunku do wektora i deque (np. brak operatora indeksowania)

Wstawianie i usuwanie elementów nie unieważnia iteratorów

Page 21: Dzisiejszy wykład

21

Kolekcje asocjacyjne

Standardowa tablica jest indeksowana wartościami numerycznymi

A[0],...,A[Size-1] gęste indeksowanie

Tablica asocjacyjna może być indeksowana dowolnym typem

A["alfred"], A["judy"] rzadkie indeksowanie

Asocjacyjne struktury danych umozliwiają bezpośrednie wyszukiwanie ("indeksowanie") za pomocą złożonych kluczySTL zawiera wzorce kilku kolekcji asocjacyjnych

Page 22: Dzisiejszy wykład

22

Posortowane kolekcje asocjacyjne

Wartości (obiekty) w kolekcji są przechowywane w porządku posortowanym pod względem typu klucza

wartości Key mogą się powtarzaćmultimap<Key,T>

kolekcja wartości typu T indeksowanych unikatowymi wartościami Key

map<Key,T>

wartości Key mogą się powtarzaćmultiset<Key>

kolekcja unikatowych wartości typu Key

set<Key>

Page 23: Dzisiejszy wykład

23

Zbiory i wielozbiory

Zarówno wzorzec set jak i multiset przechowuje wartości typu key, który musi mieć zdefiniowane uporządkowanie

set umożliwia przechowywanie jedynie pojedynczych obiektów, natomiast multiset umożliwia przechowywanie duplikatów

typ klucza musi definiować operator <

set<int> iSet; // fine, built-in type has < operatorset<Employee> Payroll; // class Employee did not

// implement a < operator

bool Employee::operator<(const Employee& Other) const {return (ID < Other.ID);

}

Page 24: Dzisiejszy wykład

24

Przykład zbioru

#include <functional>#include <set>using namespace std;#include "employee.h"

void EmpsetPrint(const set<Employee> S, ostream& Out);void PrintEmployee(Employee toPrint, ostream& Out);

int main() {Employee Ben("Ben", "Keller", "000-00-0000");Employee Bill("Bill", "McQuain", "111-11-1111");Employee Dwight("Dwight", "Barnette", "888-88-8888");set<Employee> S;S.insert(Bill);S.insert(Dwight);S.insert(Ben);EmpsetPrint(S, cout);

}void EmpsetPrint(const set<Employee> S, ostream& Out) {

set<Employee>::const_iterator It;for (It = S.begin(); It != S.end(); ++It)

Out<<*It<<endl;}

000-00-0000 Ben Keller111-11-1111 Bill McQuain888-88-8888 Dwight Barnette

Page 25: Dzisiejszy wykład

25

Wybór typu kolekcji

vector może być użyty zamiast dynamicznie alokowanej tablicylist umozliwia dynamiczną zmianę rozmiaru przy dostępie sekwencyjnymset może być użyty w przypadku potrzeby posortowania danych i braku konieczności dostępu swobodnegomap powinien być użyty, kiedy dane muszą być indeksowane przy pomocy unikatowego kluczamultiset i multimap powinny być użyte w sytuacjach analogicznych jak set i map oraz powtarzających się wartościach klucza

Page 26: Dzisiejszy wykład

26

Rozważmy poniższy program...

#include <iostream>#include <vector>using namespace std;

intmain (){ vector < int >v; vector < int >::iterator idx; int i, total; cout << "Enter numbers, end with ^D" << endl; cout << "% "; while (cin >> i) { v.push_back (i); cout << "% "; } cout << endl << endl; cout << "Numbers entered = " << v.size () << endl; for (idx = v.begin (); idx != v.end (); ++idx) cout << *idx << endl; total = 0; for (idx = v.begin (); idx != v.end (); ++idx) total = total + *idx; cout << "Sum = " << total << endl;};

Powtórzona sekwencja dostępu do wszystkich elementów kolekcji

Page 27: Dzisiejszy wykład

27

Po uproszczeniu...

#include <iostream>#include <vector>#include <numeric>using namespace std;

void print (int i) { cout << i << endl;};int main (){ vector < int >v; vector < int >::iterator idx; int i, total; cout << "Enter numbers, end with ^D" << endl; cout << "% "; while (cin >> i) { v.push_back (i); cout << "% "; } cout << endl << endl; cout << "Numbers entered = " << v.size () << endl; for_each (v.begin (), v.end (), print); total = accumulate (v.begin (), v.end (), 0); cout << "Sum = " << total << endl;}

Z użyciem STL

Page 28: Dzisiejszy wykład

28

Programowanie uogólnione

Popularne algorytmy operujące na kolekcjach

Implementują sortowanie, wyszukiwanie i inne podstawowe operacje

Trzy typy algorytmów operujących na kolekcjach sekwencyjnych

modyfikujące algorytmy na ciągach fill(), fill_n(), partition(),

random_shuffle(), remove_if(), ... niemodyfikujące algorytmy na ciągach

count(), count_if(), find(), for_each()

algorytmy numeryczne (<numeric>) accumulate()

Page 29: Dzisiejszy wykład

29

Algorytmy modyfikujące

Funkcje modyfikujące kolekcję w różny sposób

Dostęp do kolekcji uzyskiwany za pośrednictwem iteratora

Założenievector<char> charV;

void fill(iterator, iterator, T)

charV.fill(charV.begin(), charV.end(), 'x')umieszcza 'x' we wszystkich elementach wektora

i terator fi l l _n( i terator, i nt, T)

charV.fill_n(charV.begin(), 5, 'a')umieszcza 'a' w pięciu pierwszych elementach wektora

voi d generate(i terator, i terator, functi on)

char nextLetter() { static char letter = 'A'; return letter++;}charV.generate(charV.begin(), charV.end(), nextLetter);wypełnia tablicę rezultatami wywołania funkcji 'nextLetter' kolejno dla każdego elementu

Page 30: Dzisiejszy wykład

30

Algorytmy niemodyfikujące

Założenievector<int> v;

T mi n_el ement(i terator, i terator)

m in_elem ent(v.begin(), v.end())zwraca najmniejszy element w kolekcji

functi on for_each ( i terator, i terator, functi on)

void put(int val) { cout << val << endl; }for_each(v.begin(), v.end(), put); wywołuje funkcję put() dla każdego elementu tablicy; w tym przypadku wypisuje wartości wszystkich elementów

i nt count(i terator, i terator, T)

v.count(v.begin(), v.end(), 5) zwraca liczbę wystąpień liczby 5 w kolekcji

i nt count_i f ( i terator, i terator, functi on)

bool GT10(int val){ return val > 10; }v.count_if(v.begin(), v.end(), GT10);zwraca liczbę elementów większych od 10 w kolekcji

Page 31: Dzisiejszy wykład

31

Inne użyteczne algorytmy

Założenievector<int> v;

Jak wyżej, ale do porównania używa funkcji

Binarne wyszukiwanie w kolekcji.

iterator find(iterator, iterator, T)

iterator r =find(v.begin(), v.end(), 25);if (r == v.end()) cout << "Not found" << endl;else cout << "Found at " << (r - v.begin());

i terator fi nd(i terator, i terator, functi on)bool bi nary_search(i terator, i terator, T)

i terator copy(i terator, i terator, i terator)

Kopia z jednej kolekcji do drugiej.Użyteczne w połączeniu z ostream_iteratorostream_iterator<int> output(cout, " ");copy(v.begin(), v.end(), output);

Page 32: Dzisiejszy wykład

32

I wiele, wiele innych

STL posiada wiele innych operacji oraz kilka innych kolekcjiStyl programowania zaprezentowany tutaj nazywany jest programowaniem uogólnionym (generic programming)

Pisanie funkcji zależnych od pewnych operacji zdefiniowanych na przetwarzanych typach

Na przykład, find() polega na operatorze == dostępnym dla typu danych

Dla poszczególnych funkcji, mówimy o zbiorze typów które mogą być używane z daną funkcją

np. find() może być użyta dla wszystkich typów, dla których jest zdefiniowany operator ==

Brak związku z programowaniem obiektowym. Zbiór typów które definiują pewne operacje i mogą być zastosowane w konkretnej funkcji uogólnionej nie musi być spokrewniony przez dziedziczenie i polimorfizm nie jest używany.

Page 33: Dzisiejszy wykład

33

Wskaźniki w STL

STL jest bardzo elastyczna, umożliwia przechowywanie danych dowolnych typów w kolekcjach

Kolekcja nie zwalnia pamięci zaalokowanej na obiekty, do których przechowuje wskaźniki

vector< int > v;vector< int >::iterator vi;v.push_back( 45 );for (vi = v.begin(); vi != v.end(); vi++) {

int av = *vi;}

vector< Foo * > v;vector< Foo * >::iterator vi;v.push_back( new Foo( value) );for (vi = v.begin(); vi != v.end(); vi++) {

Foo * av = *vi;}

Page 34: Dzisiejszy wykład

34

Obiekty funkcyjne w STL

Obiekt funkcyjny to obiekt, który ma zdefiniowany operator wywołania funkcji (), tak że w poniższym przykładzie

wyrażenie fo() jest wywołaniem operatora () obiektu fo, a nie wywołaniem funkcji fo

Obiektów funkcyjnych możemy użyć we wzorcach STL wszędzie tam, gdzie dopuszczalny jest wskaźnik do funkcji

FunctionObjectType fo;// ...fo(...);

void fo(void) {//

statements}

class FunctionObjectType { public: void operator() (void){

// statements

} };

Zamiastpiszemy

Page 35: Dzisiejszy wykład

35

Obiekty funkcyjne - po co ta komplikacja?

Obiekty funkcyjne mają następujące zalety w porównaniu ze wskaźnikami do funkcji

Obiekt funkcyjny może posiadać stan. Można mieć dwa egzemplarze obiektu funkcyjnego, które mogą być w różnych stanach. Nie jest to możliwe w przypadku funkcji

Obiekt funkcyjny jest przeważnie bardziej wydajny niż wskaźnik do funkcji

Page 36: Dzisiejszy wykład

36

Przykład zastosowania obiektu funkcyjnego

#include <iostream>#include <vector>#include <algorithm>#include <stdlib.h>#include <time.h>using namespace std;

bool GTRM(long val){ return val > (RAND_MAX >> 1);}

int main (){ srandom(time(NULL)); vector < long > v(10); generate(v.begin(),v.end(), random); cout << count_if(v.begin(), v.end(),GTRM); cout <<endl;};

#include <iostream>#include <vector>#include <algorithm>#include <stdlib.h>#include <time.h>using namespace std;

template <class T> class greater_than{ T reference;public: greater_than (const T & v): reference (v) {} bool operator() (const T & w) { return w > reference; }};int main (){ srandom (time (NULL)); vector < long >v (10); generate (v.begin (), v.end (), random); cout << count_if (v.begin (), v.end (), greater_than<long> (RAND_MAX >> 1)); cout << endl;};