• Nie Znaleziono Wyników

Wstęp do programowania obiektowego

N/A
N/A
Protected

Academic year: 2021

Share "Wstęp do programowania obiektowego"

Copied!
44
0
0

Pełen tekst

(1)

Wstęp do programowania obiektowego

STL - Standard Template Library

(2)

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.

(3)

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

(4)

Jak to działa?

(5)

Dlaczego STL?

 Elastyczność

 Efektywność

Dobra specyfikacja i dokumentacja

(6)

STL Containers

(zbiorniki \ kontenery)

(7)

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)

(8)

#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;

}

Zbiornik vector

(9)

#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

(10)

#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

(11)

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>

(12)

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();

(13)

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();

(14)

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.

(15)

Metody priority_queue:

 bool empty();

 size_type size();

 value_type& top();

 const value_type& top();

 void push(const value_type&);

 void pop();

(16)

Kolekcje asocjacyjne

Uogólnienie kolekcji

Najczęściej używane typ kluczy to string (napis)

Efektywna implementacja

(17)

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ć

(18)

struct ltstr{

bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0;

} };

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;

Przykład użycia map

(19)

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));

}

(20)

Alokatory

 Zawierają funkcje alokacji i dealokacji pamięci

Czarne skrzynki

(21)

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.

(22)

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

(23)

Rodzaje iteratorów

Input Iterator

Output Iterator

Forward Iterator

Bidirectional Iterator

Random Access Iterator

Const Iterator

(24)

Input Iterator

(25)

Output Iterator

(26)

Forward Iterator

(27)

Bidirectional Iterator

(28)

Random Access Iterator

(29)

Algorytmy STL

 Generyczne algorytmy oddzielają algorytm od danych

 „Nie zależą” od reprezentacji danych

 Operują na iteratorach

(30)

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>

(31)

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>

iterator_traits<InputIterator>::difference_type count

(InputIterator first, InputIterator last, const

(32)

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);

(33)

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);

(34)

Przykład (kopiowanie zawartości vectora do listy i sprawdzenie zgodności):

vector<int> V(5); // utworzenie vectora V

iota(V.begin(), V.end(), 1); //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()));

(35)

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);

(36)

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"

(37)

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);

(38)

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' };

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, " "));

Wynik:

Intersection of A1 and A2: 1 3 5 Intersection of A3 and A4: a b b f h

(39)

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);

(40)

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, " "));

pop_heap(A, A+N);

cout << endl << "After pop: ";

copy(A, A+N-1, ostream_iterator<int>(cout, " "));

Wynik:

Before pop: 6 5 3 4 2 1 After pop: 5 4 3 1 2 A[N-1] = 6

(41)

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

(42)

1 przykład – sortowanie według modułu liczby:

vector<int> V(100);

generate(V.begin(), V.end(), rand);

struct mniejszy_modul : 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(), mniejszy_modul());

(43)

2 przykład - sumator:

struct dodawacz: 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(), dodawacz());

cout << "The sum is " << result.sum << endl;

(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 -

Cytaty

Powiązane dokumenty

- Wynikiem działania tej metody będzie obiekt (referencja do obiektu) klasy Punkt będący kopią obiektu A, czyli zawierający w polach x i y takie same wartości, jakie są zapisane

patrzymy w prawo: jeśli jest tam nawias otwierający okrągły '(', to będzie to funkcja (odczytujemy liczbę i typ parametrów); jeśli będzie tam nawias otwierający kwadratowy '[',

 Przeszukiwany jest stos wywołań funkcji w poszukiwaniu takiej, która zawiera obsługę wyjątku danego typu (czyli odpowiednią instrukcję catch).. ◦ Jeżeli

• Parametrami szablonów mogą być również szablony klas, jako tak zwane szablony parametrów szablonów.. Stack&lt;int, std::vector&gt;

Model dziedziny (uzupełniony): klasy, atrybuty klas oraz

 Jednostka programu, która zadeklarowała instancję klasy (obiekt), ma dostęp do publicznych bytów tej klasy, ale tylko poprzez tę instancję.  Każda instancja klasy ma

 Symbole pojawiające się wyłącznie po prawej stronie to symbole terminalne.  Generalnie symbole terminalne to symbole z alfabetu definiowanego języka,

– osoba, która poprawia zaliczenie (końcowe lub cząstkowe), może uzyskać co najwyżej ocenę bardzo dobrą.