STL: implementowanie algorytmów. STL: implementowanie algorytmów. STL: implementowanie algorytmów. STL: implementowanie algorytmów

Podobne dokumenty
STL: implementowanie algorytmów C++: STL. STL: implementowanie algorytmów. STL: implementowanie algorytmów. STL: implementowanie algorytmów

STL: kontenery C++: STL: kontenery. STL: kontenery. STL: kontenery. STL: kontenery. Fabryka obiektów. Fabryka obiektów. Fabryka obiektów przykład:

STL: kontenery C++: STL: kontenery. STL: kontenery. STL: kontenery. STL: kontenery. Fabryka obiektów. Fabryka obiektów

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

obiekty funkcyjne - funktory

Część 4 życie programu

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Szablony funkcji i szablony klas

Zaawansowane programowanie w C++ (PCP)

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

STL: kontenery. Typy kontenerów STL. STL: kontenery. STL: kontenery. STL: kontenery. Typy kontenerów STL. deque (double-ended queue) list

Programowanie i struktury danych

Szablony klas, zastosowanie szablonów w programach

Paradygmaty programowania

Mechanizm dziedziczenia

C++ Przeładowanie operatorów i wzorce w klasach

Projektowanie klas c.d. Projektowanie klas przykład

Wprowadzenie do szablonów szablony funkcji

Wprowadzenie do szablonów szablony funkcji

PARADYGMATY PROGRAMOWANIA Wykład 4

STL: Lekcja 1&2. Filozofia STL

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

I - Microsoft Visual Studio C++

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

STL: algorytmy sortujące. STL: algorytmy sortujące. STL: algorytmy sortujące. STL: algorytmy sortujące. STL: algorytmy sortujące

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) { zdefiniuje. Integer::operator=(ri);

Programowanie obiektowe w C++ Wykład 12

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Szablony funkcji i klas (templates)

Wstęp do programowania obiektowego. WYKŁAD 3 Dziedziczenie Pola i funkcje statyczne Funkcje zaprzyjaźnione, this

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

wykład IV uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C, a C++. wykład IV dr Jarosław Mederski Spis Język C++ - wstęp

EGZAMIN PROGRAMOWANIE II (10 czerwca 2010) pytania i odpowiedzi

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

TEMAT : KLASY DZIEDZICZENIE

Programowanie w C++ Wykład 14. Katarzyna Grzelak. 3 czerwca K.Grzelak (Wykład 14) Programowanie w C++ 1 / 27

STL: kontenery. STL: kontenery. STL: kontenery. Typy kontenerów STL. STL: kontenery. STL: kontenery. multimap. Kontener map: przykład zadanie:

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 15 kwietnia K.Grzelak (Wykład 8) Programowanie w C++ 1 / 33

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Szablony. Szablony funkcji

Typy złożone. Struktury, pola bitowe i unie. Programowanie Proceduralne 1

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Metody Metody, parametry, zwracanie wartości

STL: kontenery. STL: kontenery. STL: kontenery. STL: kontenery. STL: kontenery. STL: kontenery

Technologie cyfrowe semestr letni 2018/2019

C++ - [4-7] Polimorfizm

Rzutowanie i konwersje

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

Wydajność użycia funktorów z biblioteką STL języka C++

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Wstęp do Programowania 2

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

Programowanie obiektowe

Programowanie w C++ Wykład 6. Katarzyna Grzelak. 1 kwietnia K.Grzelak (Wykład 6) Programowanie w C++ 1 / 43

C++ wprowadzanie zmiennych

Programowanie Komponentowe Zarządzanie obiektami: kontenery

TEMAT : KLASY POLIMORFIZM

Programowanie w C++ Wykład 9. Katarzyna Grzelak. 14 maja K.Grzelak (Wykład 9) Programowanie w C++ 1 / 30

Programowanie 2. Język C++. Wykład 9.

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

Techniki programowania INP001002Wl rok akademicki 2017/18 semestr letni. Wykład 4. Karol Tarnowski A-1 p.

Zaawansowane programowanie w języku C++ Biblioteka standardowa

8. Wektory. Przykłady Napisz program, który pobierze od użytkownika 10 liczb, a następnie wypisze je w kolejności odwrotnej niż podana.

Operatory na rzecz typu TString

Podstawy programowania skrót z wykładów:

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

Zaawansowane programowanie w C++ (PCP)

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

STL Standardt Template Library (wprowadzenie)

Technologie programowania Wykład 4. Szablony funkcji Notes. Szablony funkcji Notes. Szablony funkcji Notes. Notes. Przemek Błaśkiewicz.

Technologie cyfrowe semestr letni 2018/2019

Jzyk C++ cz 3. Jarosław Gramacki Instytut Informatyki i Elektroniki ( $)*)+' *, - ( ' )*'.' '',*/ *, ','*0) 1 / ) %*+ 2'' 2" ( $%%) )'20 )*0) 1 / )

Algorytmy i Struktury Danych. Anna Paszyńska

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Definicja szablonu klasy. Korzystanie z szablonu. Specjalizacja metody szablonu.

Listy powiązane zorientowane obiektowo

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

Stos liczb całkowitych

Podstawy języka C++ Maciej Trzebiński. Praktyki studenckie na LHC IFJ PAN. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. M. Trzebiński C++ 1/16

Do czego służą klasy?

EGZAMIN 2 (14 WRZEŚNIA 2015) JĘZYK C++

Wstęp do wiadomości teoretycznych (nie, nie jest to masło maślane ani wstęp, wstępów proszę cierpliwie czytać)

Funkcje przeciążone, konstruktory kopiujące, argumenty domyślne

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

Zajęcia nr 5 Algorytmy i wskaźniki. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 4. Karol Tarnowski A-1 p.

Programowanie obiektowe

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

Programowanie obiektowe

Mechanizm dziedziczenia

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Kurs programowania. Wykład 9. Wojciech Macyna

Wstęp do informatyki- wykład 12 Funkcje (przekazywanie parametrów przez wartość i zmienną)

Transkrypt:

Algorytm przeszukiwania wszerz Przeszukiwanie grafu odwiedzenie wszystkich jego wierzchołków w kolejności jak na rysunku obok: Reprezentacja grafu w programie Wierzchołki są identyfikowane przez liczby całkowite. Dla każdego z wierzchołków przechowujemy listy numerów wierzchołków, z którymi jest połączony krawędzią: vector< vector<int> > Algorytm przeszukiwania wszerz Ogólna zasada działania algorytmu: Do przeszukiwania wszerz stosowana jest kolejka FIFO o nazwie Q. 1. Najpierw do Q trafia pierwszy wierzchołek z listy W 2. Następnie jest on wyjmowany z kolejki, po czym trafiają do niej wszystkie węzły sąsiadujące z węzłem początkowym. 3. Następnie ponownie pobieramy kolejny węzeł z kolejki Q i wkładamy do kolejki wszystkie jego wierzchołki sąsiednie (jeszcze nieodwiedzone). Proces jest kontynuowany póki kolejka nie jest pusta. 131 132 Algorytm przeszukiwania wszerz Deklaracje zmiennych: typedef vector<int> vi; typedef vector<vi> vvi; int N; // liczba wierzchołków vvi W; // graf 133 Algorytm przeszukiwania wszerz bool check_graph_connected_bfs() int start_vertex = 0; vi V(N, false); // flagi określające, czy wierzchołek został odwiedzony queue<int> Q; // kolejka FIFO Q.push(start_vertex); // pierwszy węzeł grafu trafia do kolejki V[start_vertex] = true; while(!q.empty()) int i = Q.front(); Q.pop(); // zdejmij element z kolejki for(vi::iterator it = W[i].begin(); it!= W[i].end(); it++) if(!v[*it]) V[*it] = true; Q.push(*it); // dodaj na koniec kolejki return (find(v.begin(), V.end(), 0) == V.end()); UKSW, WMP. SNS, Warszawa 134 Statystyka Moment centralny r-tego stopnia (r-tego rzędu) gdzie: r=1,2,.. - rząd, stopień momentu, poszczególne obserwacje, średnia, n liczba obserwacji. Moment centralny drugiego rzędu - wariancja = 1 Statystyka template<int N, class T> T nthpower(t x) T ret = x; for (int i=1; i < N; ++i) ret *= x; return ret; template<class T, int N> struct SumDiffNthPower T mean_; SumDiffNthPower(T x) : mean_(x) T operator()(t sum, T current) return sum + nthpower<n>(current - mean_); 135 136 1

Statystyka template<class T, int N, class Iter_T> T nthmoment(iter_t first, Iter_T last, T mean) size_t cnt = distance(first, last); return accumulate(first, last,t(),sumdiffnthpower<t,n>(mean))/cnt; template<class T, class Iter_T> T computevariance(iter_t first, Iter_T last, T mean) return nthmoment<t, 2, Iter_T>(first, last, mean); void main() // tutaj utworzenie kontenera z danymi size_t cnt = distance(first, last); double sum = accumulate(first, last, 0.0); double mean = sum / cnt; double var = computevariance(first, last, mean); C++: STL Obiekty funkcyjne: funktory, predykaty, adaptory 137 Wprowadzenie Przy okazji prezentacji algorytmów pojawiły się na różnych slajdach gotowe funktory z biblioteki <functional>: std::transform (V.begin(), V.end(), W.begin(), V.begin(), std::plus<int>()); std::cout << std::accumulate (numbers, numbers+3, init, std::minus<int>()); std::cout << std::inner_product(series1,series1+3,series2,init, std::minus<int>(),std::divides<int>()); std::adjacent_difference (val, val+7, result, std::multiplies<int>()); Funktory arytmetyczne plus<typ> wynik = arg1 + arg2 minus<typ> wynik = arg1 - arg2 multiplies<typ> wynik = arg1 * arg2 divides<typ> wynik = arg1 / arg2 modulus<typ> wynik = arg1 % arg2 negate<typ> wynik = - arg argumenty i wynik są tego samego (podanego w deklaracji) typu działają pod warunkiem, że dla typu, dla którego funktor jest konkretyzowany, zdefiniowany jest odpowiedni operator arytmetyczny 139 140 Predykaty Przy okazji algorytmu sort zaprezentowany został też funktor greater: c1.sort( greater<int>( ) ); argumenty (jeden lub dwa) tego samego (podanego w deklaracji) typu, wynik typu bool, działa pod warunkiem, że dla typu, dla którego funktor jest konkretyzowany, zdefiniowany jest odpowiedni operator relacyjny lub logiczny, Funktory o takich cechach nazywane są predykatami. Predykaty porównania: equal_to<typ> wynik = arg1 == arg2 not_equal_to<typ> wynik = arg1!= arg2 less<typ> wynik = arg1 < arg2 greater<typ> wynik = arg1 > arg2 less_equal<typ> wynik = arg1 <= arg2 greater_equal<typ> wynik = arg1 >= arg2 141 142 2

Predykaty operacje logiczne: logical_or<typ> wynik = arg1 arg2 logical_and<typ> wynik = arg1 && arg2 logical_not<typ> wynik =! arg Adaptory funktorów Zdarza się, że potrzebujemy funktora, którego nie ma w bibliotece functional, ale jest podobny, który w znacznej części implementuje potrzebną funkcję. Do wykonania brakującej modyfikacji w działaniu można wykorzystać wtedy jeden z gotowych adaptorów. Typy adaptorów: 1. wiążące zmieniają funktor dwuargumentowy w jednoargumentowy, 2. negujące stosowane do predykatów, tworzą predykat obliczający negację pierwotnego, 3. adaptory zwykłych funkcji i metod. 143 144 Adaptory wiążące zmieniają funktor dwuargumentowy w jednoargumentowy bind1st(fun,val) - wiąże pierwszy argument z wartością val bind2nd(fun,val) - wiąże drugi argument z wartością val Przykłady: #1: skalowanie współrzędnych punktu P i zapisanie ich w R: R = P*s: transform(p.begin(), P.end(), R.begin(), bind2nd(multiplies<double>(), s)); Adaptory negujące stosowane do predykatów, tworzą predykat obliczający negację pierwotnego not1(fun) - neguje wynik funktora jednoargumentowy not2(fun) - neguje wynik funktora dwuargumentowy Przykład: Policzenie liczb parzystych w kolekcji v: liczba = count_if(v.begin(),v.end(), not1(bind2nd(modulus<int>(),2))); #2: aby znaleźć w kolekcji elementy o wartości v<7 : bind2nd(less<int>(),7) 145 146 Adaptory zwykłych funkcji i metod tworzą funktor na podstawie: ptr_fun(fun) - zwykłej funkcji lub metody statycznej mem_fun_ref(fun) - metody (niestatycznej) obiektu mem_fun(fun) - metody obiektu dostępnego przez wskaźnik istnieją jedynie adaptory metod i funkcji jednoargumentowych i dwuargumentowych ponieważ tylko takie są używane w bibliotece STL, jawna zamiana metod na funktory jest niezbędna ponieważ wywołanie metody jest składniowo inne niż zwykłej funkcji (czyli inne niż funktora), jawna zamiana zwykłej funkcji na funktor jest niezbędna aby możliwe było zastosowanie adaptorów wiążących i negujących. 147 Przykład: Mamy funkcję, która dla elementu zwraca informacje, czy należy on do pewnej klasy, czy nie (wartość true lub false). Należy policzyć elementy, które nie należą do tej klasy. bool parzysta(int a) return!(a&1); void main() int tab[] = 2, 5, 4, 9, 12, 3, 8 vector<int> v(tab,tab+sizeof(tab)/sizeof(int)); vector<int>::iterator it; //int liczba = count_if(v.begin(),v.end(),not1(parzysta)); // Źle! int liczba = count_if(v.begin(),v.end(),not1(ptr_fun(parzysta))); 148 3

Przykład: Należy policzyć długości słów przechowywanych w wektorze. Przykład: Należy policzyć długości słów przechowywanych w wektorze. vector<string> numbers; // wektor słów vector<string*> numbers; // wektor słów numbers.push_back("raz"); numbers.push_back("dwa"); numbers.push_back("trzy"); numbers.push_back("cztery"); numbers.push_back("raz"); numbers.push_back("dwa"); numbers.push_back("trzy"); numbers.push_back("cztery"); vector <int> lengths (numbers.size()); // wektor długości słów vector <int> lengths (numbers.size()); // wektor długości słów transform (numbers.begin(), numbers.end(), lengths.begin(), mem_fun_ref(&string::length)); transform (numbers.begin(), numbers.end(), lengths.begin(), mem_fun(&string::length)); 149 150 Adaptory podsumowanie: 1. bind1st(fun,val)(arg) fun(val,arg) 2. bind2nd(fun,val)(arg) fun(arg,val) 3. not1(fun)(arg)!fun(arg) 4. not2(fun)(arg1,arg2)!fun(arg1,arg2) 5. ptr_fun(fun)(arg) fun(arg) 6. ptr_fun(fun)(arg1,arg2) fun(arg1,arg2) 7. mem_fun(fun)(arg) arg->fun() 8. mem_fun(fun)(arg1,arg2) arg1->fun(arg2) 9. mem_fun_ref(fun)(arg) arg.fun() 10. mem_fun_ref(fun)(arg1,arg2) arg1.fun(arg2) Adaptory podsumowanie adaptory funktorów są szablonami funkcji, których argumentami są inne funkcje lub funktory funktory standardowe są szablonami klas, dlatego przy ich użyciu musimy podać parametry szablonu (dla adaptorów jako szablonów funkcji nie musimy) adaptory funktorów rzadko wywołujemy jawnie, zwykle przekazujemy jako parametr do algorytmów, dlatego wywołania z dwoma parami nawiasów praktycznie się nie zdarzają (poza samym kodem biblioteki STL) 151 152 Tworzenie własnych funktorów definiowane jako publiczne klasy (struktury) pochodne od struktur 1. unary_function jednoargumentowe 2. binary_function dwuargumentowe #include <functional> template <class Arg, class Result> struct unary_function typedef Arg argument_type; typedef Result result_type; template <class Arg1, class Arg2, class Result> struct binary_function typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; Tworzenie własnych funktorów przykład 1 jednoargumentowy, dziedziczy po unary_function template<class TYPE> struct modulo2 : public unary_function<type, void> void operator() (TYPE& x) x = x%2; 153 154 4

#include <iostream> #include <algorithm> #include <vector> template<class TYPE> struct modulo2 : public unary_function<type, void> void operator() (TYPE& x) x = x%2; int main(int argc, char *argv[]) int myints[] = 1, 2, 3, 4, 5, 6, 7, 8 vector<int> v(myints,myints+8); cout << "before: "; copy( v.begin(), v.end(), ostream_iterator<int>( cout, " " ) ); cout << endl; for_each( v.begin(), v.end(), modulo2<int>() ); cout << "after : "; copy( v.begin(), v.end(), ostream_iterator<int>( cout, " " ) ); cout << endl; return 0; 155 Tworzenie własnych funktorów przykład 2 jednoargumentowy, dziedziczy po unary_function, przyjmuje w argumentach szablonu informacje potrzebne do poprawnego działania, przyjmijmy, że jest zapisany w pliku Przedzial.h template <class T,T dol, T gora> struct Przedzial: unary_function<t,bool> bool operator()(t x) const return x>=dol && x<=gora; 156 Tworzenie własnych funktorów przykład 2 #include <iostream> #include <vector> #include <algorithm> #include "przedzial.h" using namespace std; void main() int tab[] = 2, 5, 4, 9, 12, 3, 8 vector<int> v(tab,tab+sizeof(tab)/sizeof(int)); int c; c=count_if(v.begin(),v.end(),przedzial<int,5,9>()); cout << c << endl ; Tworzenie własnych funktorów przykład 3 jednoargumentowy, dziedziczy po unary_function, Przyjmuje w konstruktorze i przechowuje w polach informacje potrzebne do poprawnego działania, przyjmijmy, że jest zapisany w pliku Przedzial.h template <class T> class Przedzial: public unary_function<t,bool> T dol, gora; Przedzial(T d, T g) : dol(d), gora(g) bool operator()(t x) const return x>=dol && x<=gora; 157 158 Tworzenie własnych funktorów przykład 3 #include <iostream> #include <vector> #include <algorithm> #include "przedzial.h" using namespace std; void main() int tab[] = 2, 5, 4, 9, 12, 3, 8 vector<int> v(tab,tab+sizeof(tab)/sizeof(int)); int c; c=count_if(v.begin(),v.end(),przedzial<int>(5,9)); cout << c << endl ; Tworzenie własnych funktorów dziedziczenie funktorów po unary_function i binary_function nie dodaje im żadnej nowej funkcjonalności, jedynym zadaniem dziedziczenia jest nadanie uniwersalnych etykiet (za pomocą typedef) typom danych, jakie są przekazywane do funktora; informacja ta jest wykorzystywana w kodzie biblioteki functional. 159 160 5

Tworzenie własnych funktorów Przyjmijmy, że mamy funkcję i nie chcemy z niej korzystać poprzez ptr_fun, ale zrobić z niej funktor. Zasada tworzenia funktora z funkcji jest identyczna dla unary_function i binary_function : Krok 1 (początek: Zamieniamy dwuargumentową funkcję na funktor): Przyjmijmy, że mamy funkcję dwuargumentową, która przyjmuje dwie wartości typu string i int i zwraca bool. class MyOtherFunction bool operator() (const string& str, int val) const /* Do something, return a bool. */ Tworzenie własnych funktorów Ponieważ funkcja jest dwuargumentowa, sięgamy do szablonu binary_function, którego nagłówek wygląda następująco: template <typename Param1Type, typename Param2Type, typename ResultType> class binary_function; Krok 2 (ostatni): dodajemy dziedziczenie po binary_function class MyOtherFunction: public binary_function<string, int, bool> bool operator() (const string& str, int val) const /* Do something, return a bool. */ 161 162 Fabryka obiektów C++: Fabryka obiektów Klasa, której obiekty pośrednicza przy tworzeniu innych obiektów. Pomagają tworzyć obiekty, jeżeli informacja o typie odnosi się do konkretnego typu, znanego w momencie kompilacji, ale forma jest nieodpowiednia dla kompilatora. Fabryka ukrywa przed użytkownikiem mechanizm zamiany identyfikatora na literał dostarczany do operatora new, upraszczając tworzenie obiektów. 164 Klasa bazowa Figure dostarcza identyfikatorów dla klas konkretnych: class Figure enum Type SQUARE, CIRLCE, TRIANGLE virtual void write(ostream& os) const = 0; virtual void read(istream& is) = 0; 165 Klasa pochodna nadpisuje metody wirtualne służące do zapisania obiektu do pliku oraz odczytania z pliku: class Square : public Figure void write(ostream& os) const os << SQUARE; /* zapis pól */ void read(istream& is) /* odczyt pól */ 166 6

Podczas odczytu danych z pliku musimy polegać na informacji dostarczanej w trakcie działania, tj. na identyfikatorze obiektu zapisanym w pliku (zapis do pliku zaczyna się od podania identyfikatora, dopiero potem są zapisywane zawartości pól obiektu) Taki identyfikator to nie jest argument dla new. Potrzebny mechanizm, który tworzy obiekty na podstawie identyfikatora.. Figure* createobj(figure::type type) switch (type) case Figure::SQUARE: return new Square(); case Figure::CIRCLE: return new Circle(); case Figure::TRIANGLE: return new Triangle(); default: return NULL; 167 168 Za pomocą tej funkcji oraz metody read można dostarczyć funkcję, która pozwala odczytywać obiekty ze strumienia: Figure* create(ifstream& is) Figure::Type type; unsigned int t = 0; if(is >> t) // odczyt wartości typu int type = static_cast<figure::type>(t); // rzutowanie t na Type Figure* obj = createobj(type); obj->read(is); return obj; else return NULL;..i to już, wszystko? 169 To rozwiązanie ma wady: kiedy dodamy nowy typ danych, musimy też zmodyfikować funkcję createobj Brakuje kontroli poprawności wiązania identyfikatora z typem. Zestaw identyfikatorów jest zasobem wspólnym dla całej hierarchii, przy modyfikacji zbioru klas musi też podlegać modyfikacji. Potrzeba czegoś więcej.. 170 Fabryka skalowalna obiektów przykład: Fabryka najpierw rejestruje pary: identyfikatory typu i przypisane im funkcje tworzące. Następnie jest gotowa do tworzenia obiektów na podstawie identyfikatora typu. class FigFactory typedef Figure* (*CreateFig)(); // nowy typ: wskaźnik na funkcję tworzącą void registerfig(int id, CreateFig fun); // rejestruje nowy typ Figure* create(int id); // tworzy obiekt na podstawie identyfikatora private: typedef map<int, CreateFig> Creators; // nowy typ: kontener mapa par Creators creators_; // pole: kontener do przechowywania par Fabryka skalowalna obiektów przykład: void FigFactory::registerFig(int id, CreateFig fun) creators_.insert(map<int,createfig>::value_type(id, fun)); // typedef pair<const Key, Type> value_type; Figure* FigFactory::create(int id) //tworzy obiekt danego typu Creators::const_iterator i = creators_.find(id); if(i!= creators_.end() ) //jeżeli znalazł odpowiedni wpis return (i->second)(); //woła metodę fabryczną return 0L; //zwraca pusty wskaźnik, gdy nieznany identyfikator 171 172 7

Fabryka skalowalna obiektów przykład: Autor klasy konkretnej musi też dostarczyć funkcję tworzącą obiekt danej klasy. Funkcja ta ma sygnaturę zdefiniowaną w fabryce i jest dostarczana do fabryki podczas rejestracji typu. Figure* CreateSquare() return new Square(); Figure* CreateCircle() return new Circle(); Figure* CreateTriangle() return new Triangle(); 173 Jeszcze raz tworzymy funkcję, która pozwala odczytywać obiekty ze strumienia: Figure* create(ifstream& is, FigFactory& factory) unsigned int t = 0; if(is >> t) Figure* obj = factory.create(t); obj->read(is); return obj; else return NULL; 174..i to już, wszystko? Trzeba jeszcze pamiętać, aby na początku programu stworzyć fabrykę i zarejestrować odpowiednie typy int main() FigFactory factory; factory.registerfig(figure::square, CreateSquare); factory.registerfig(figure::circle, CreateCircle); factory.registerfig(figure::triangle, CreateTriangle); /*.. */ To wszystko. Chociaż, może nie.. 175 Fabryka obiektów i wzorzec prototypu: wzorzec prototypu pozwala na tworzenie kopii obiektu, jeżeli mamy dostępny wskaźnik lub referencję do klasy bazowej. Wykorzystuje mechanizm funkcji wirtualnych przenosi odpowiedzialność za tworzenie obiektów do klas konkretnych. Klasa bazowa definiuje tylko czysto wirtualną metodę. 176 Fabryka obiektów i wzorzec prototypu: Dodajemy czysto wirtualną metodę clone do klasy bazowej: class Figure enum Type SQUARE, CIRCLE, TRIANGLE virtual void write(ostream& os) const = 0; virtual void read(istream& is) = 0; virtual Figure* clone() const = 0; 177 Fabryka obiektów i wzorzec prototypu: Implementujemy metodę clone w klasach konkretnych: class Square : public Figure void write(ostream& os) const os << SQUARE; /* zapis pól */ void read(istream& is) /* odczyt pól */ Figure* clone() const return new Square(*this); Teraz zadaniem fabryki jest przechowywać obiekty wzorcowe, a nie funkcje fabryczne. Można w niej umieścić np. po kilka obiektów tego samego typu, ale w różnym stanie. 178 8

Fabryka obiektów i wzorzec prototypu: class FigFactory2 typedef map<int,figure*> Prototypes; Prototypes prototypes_; void registerfig(int id, Figure* f) prototypes_.insert(map<int,figure*>::value_type(id, f)); Figure* create(int id) Prototypes::const_iterator i = prototypes_.find(id); if(i!= prototypes_.end() ) //jeżeli znalazł odpowiedni wpis return (i->second)->clone(); //woła metodę clone return 0L; //zwraca pusty wskaźnik, gdy nieznany identyfikator Teraz to już naprawdę wszystko o fabrykach obiektów. 179 9