Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Wzorce. Wzorce klas – dlaczego? Pisząc programy często korzystamy z abstrakcyjnych typów danych, takich jak stos, kolejka czy drzewo. Implementacje takich.

Podobne prezentacje


Prezentacja na temat: "Wzorce. Wzorce klas – dlaczego? Pisząc programy często korzystamy z abstrakcyjnych typów danych, takich jak stos, kolejka czy drzewo. Implementacje takich."— Zapis prezentacji:

1 Wzorce

2 Wzorce klas – dlaczego? Pisząc programy często korzystamy z abstrakcyjnych typów danych, takich jak stos, kolejka czy drzewo. Implementacje takich typów mogą być prawie identyczne, na przykład klasy lista_liczb i lista_znaków mogą różnić się tylko typem elementu przechowywanego na liście. Pisząc programy często korzystamy z abstrakcyjnych typów danych, takich jak stos, kolejka czy drzewo. Implementacje takich typów mogą być prawie identyczne, na przykład klasy lista_liczb i lista_znaków mogą różnić się tylko typem elementu przechowywanego na liście. Wzorzec klasy to sposób na napisanie uogólnionej. sparametryzowanej klasy klasy której parametrem będzie typ, bądź inna klasa. Można napisać wzorzec listy, a potem w zależności od tego czego aktualnie potrzebujemy zadeklarować listę znaków, bądź listę figur. Wzorzec klasy to sposób na napisanie uogólnionej. sparametryzowanej klasy klasy której parametrem będzie typ, bądź inna klasa. Można napisać wzorzec listy, a potem w zależności od tego czego aktualnie potrzebujemy zadeklarować listę znaków, bądź listę figur. Wzorce są doskonalszym i wygodniejszym sposobem (od stosowania preprocesora) tworzenia rodzin typów i funkcji. Wzorce są doskonalszym i wygodniejszym sposobem (od stosowania preprocesora) tworzenia rodzin typów i funkcji. Wzorce nazywane są również szablonami (ang. templates). Wzorce nazywane są również szablonami (ang. templates).

3 Wzorce klas – jak deklarować? template // wzorzec, którego argumentem jest typ T class stos// wzorzec klasy stos { T* v; // początek stosu T* v; // początek stosu T* p; // koniec stosu T* p; // koniec stosu int rozm; // pojemność stosu int rozm; // pojemność stosupublic: stos (int r) {v = p = new T[rozm=r];} // konstruktor z argumentem: stos (int r) {v = p = new T[rozm=r];} // konstruktor z argumentem: // rozmiar stosu // rozmiar stosu ~stos () {delete[]v;}// destruktor ~stos () {delete[]v;}// destruktor void wstaw(T a) {*p++ = a;} // wstaw na stos void wstaw(T a) {*p++ = a;} // wstaw na stos T zdejmij() {return *--p;} // zdejmij ze stosu T zdejmij() {return *--p;} // zdejmij ze stosu int rozmiar() const {return p-v;}// ile elementów typu T jest na stosie int rozmiar() const {return p-v;}// ile elementów typu T jest na stosie};

4 Wzorce klas – jak deklarować? template // deklarujemy wzorzec, // którego argumentem jest typ T class stos// wzorzec klasy stos Typu (klasy) T można używać do końca deklaracji klasy stos tak jak każdego innego dostępnego typu lub klasy. Typu (klasy) T można używać do końca deklaracji klasy stos tak jak każdego innego dostępnego typu lub klasy. W zakresie wzorca template stos używanie pełnej nazwy typu stos jest nadmiarowe zarówno przy definicji metod wewnątrz klasy jak i poza nią, wystarczy stos zarówno dla określenia klasy jak i nazw konstruktorów i destruktora. W zakresie wzorca template stos używanie pełnej nazwy typu stos jest nadmiarowe zarówno przy definicji metod wewnątrz klasy jak i poza nią, wystarczy stos zarówno dla określenia klasy jak i nazw konstruktorów i destruktora.

5 Wzorce klas – jak deklarować? Mając wzorzec klasy stos można deklarować stosy różnych elementów przekazując typ elementu jako aktualny parametr wzorca. Mając wzorzec klasy stos można deklarować stosy różnych elementów przekazując typ elementu jako aktualny parametr wzorca. Składnia nazwy typu wywiedzionego ze wzorca jest następująca: Składnia nazwy typu wywiedzionego ze wzorca jest następująca: nazwa_klasy_wzorca nazwa_klasy_wzorca Nazwa klasy stosu liczb: Nazwa klasy stosu liczb:stos Nazwa klasy stosu wskaźników do figur: Nazwa klasy stosu wskaźników do figur: stos stos

6 Wzorce klas Deklaracja: Deklaracja: stos liczby(100); stos liczby(100); to deklaracja obiektu o nazwie liczby, to deklaracja obiektu o nazwie liczby, należącego do klasy stos, należącego do klasy stos, oraz wywołanie konstruktora stos (100). oraz wywołanie konstruktora stos (100). Nazwy klasy utworzonej ze wzorca można używać tak samo jak nazwy każdej innej klasy, różnica to inna składnia nazwy. Nazwy klasy utworzonej ze wzorca można używać tak samo jak nazwy każdej innej klasy, różnica to inna składnia nazwy.

7 stos spf(200); // stos wskaźników do figur zdolny // pomieścić 200 wskaźników // pomieścić 200 wskaźników stos sp(400); // stos punktów o pojemności 400 void f(stos &sc) // funkcja f, której argumentem jest // referencja do stosu liczb zespolonych // referencja do stosu liczb zespolonych{ sc.wstaw(complex(1,2)); // wstaw do stosu przekazanego jako argument sc.wstaw(complex(1,2)); // wstaw do stosu przekazanego jako argument // funkcji liczbę zespoloną // funkcji liczbę zespoloną complex z = 2.5 * sc.zdejmij(); // zdejm liczbe ze stosu, complex z = 2.5 * sc.zdejmij(); // zdejm liczbe ze stosu, // pomnóż ją i przypisz // pomnóż ją i przypisz stos *p=0; // deklaracja wskaźnika do stos *p=0; // deklaracja wskaźnika do // stosu liczb całkowitych // stosu liczb całkowitych p=new stos (800); // konstrukcja stosu 800 liczb całkowitych p=new stos (800); // konstrukcja stosu 800 liczb całkowitych for (int i=0; i<400; i++) // 400 razy for (int i=0; i<400; i++) // 400 razy { p->wstaw(i); // wstaw liczbe i do stosu liczb p->wstaw(i); // wstaw liczbe i do stosu liczb sp.wstaw(Punkt(i,i+400)); // i punkt do stosu punktów sp.wstaw(Punkt(i,i+400)); // i punkt do stosu punktów } delete p; // destrukcja stosu liczb delete p; // destrukcja stosu liczb}

8 Wzorce Kompilator sprawdza poprawność wzorca w momencie jego użycia, a więc błędy w samej deklaracji wzorca mogą pozostać niezauważone aż do momentu próby wykorzystania wzorca. Kompilator sprawdza poprawność wzorca w momencie jego użycia, a więc błędy w samej deklaracji wzorca mogą pozostać niezauważone aż do momentu próby wykorzystania wzorca. Poprawna kompilacja pliku zawierającego wzorzec nie oznacza że wzorzec nie zawiera błędów. Poprawna kompilacja pliku zawierającego wzorzec nie oznacza że wzorzec nie zawiera błędów. Częstą praktyką jest najpierw uruchomienie konkretnej klasy, np. stos_znaków, a potem przekształcenie jej w klasę ogólną - wzorzec stos. Częstą praktyką jest najpierw uruchomienie konkretnej klasy, np. stos_znaków, a potem przekształcenie jej w klasę ogólną - wzorzec stos.

9 Wzorce W wcześniejszej wersji wzorca wszystkie metody są inline zdefiniowano je wewnątrz deklaracji klasy. Można we wzorcu nie definiować metod: W wcześniejszej wersji wzorca wszystkie metody są inline zdefiniowano je wewnątrz deklaracji klasy. Można we wzorcu nie definiować metod: template template class stos { T* v; // początek stosu T* v; // początek stosu T* p; // koniec stosu T* p; // koniec stosu int rozm; // pojemność stosu int rozm; // pojemność stosupublic: stos (int r); // deklaracja: konstruktor z argumentem: rozmiar stosu stos (int r); // deklaracja: konstruktor z argumentem: rozmiar stosu ~stos (); ~stos (); void wstaw(T a);// deklaracja: wstaw na stos void wstaw(T a);// deklaracja: wstaw na stos T zdejmij();// deklaracja: zdejmij ze stosu T zdejmij();// deklaracja: zdejmij ze stosu int rozmiar(); // deklaracja: ile elementów typu T jest na stosie int rozmiar(); // deklaracja: ile elementów typu T jest na stosie};

10 Wzorce Jeżeli metody wzorca definiujemy poza definicją klasy wzorca to musimy użyć dla każdej z metod słowa kluczowego template: Jeżeli metody wzorca definiujemy poza definicją klasy wzorca to musimy użyć dla każdej z metod słowa kluczowego template: // definicja metody wstaw template template void stos ::wstaw(T a) { *p++ = a; *p++ = a;}; // konstruktor template template stos ::stos(int r) { v = p = new T[rozm=r]; v = p = new T[rozm=r];};

11 Wzorce Przypomnienie: W zakresie wzorca Przypomnienie: W zakresie wzorca template stos template stos używanie pełnej nazwy typu stos jest nadmiarowe zarówno przy definicji metod wewnątrz klasy jak i poza nią. używanie pełnej nazwy typu stos jest nadmiarowe zarówno przy definicji metod wewnątrz klasy jak i poza nią. Wystarczy stos zarówno dla określenia klasy jak i nazw konstruktora i destruktora ( jest domyślne). Wystarczy stos zarówno dla określenia klasy jak i nazw konstruktora i destruktora ( jest domyślne). Poniższy wzorzec jest błędny: Poniższy wzorzec jest błędny: // template // template // stos ::stos (int r) // to jest traktowane jako błąd, // // powinno być stos :: stos(int r) //{ // v = p = new T[rozm=r]; //};

12 Rozbudowywanie klas-wzorców Wzorca który jest już napisany i wykorzystywany nie należy modyfikować modyfikacje te będą dotyczyły wszystkich klas stworzonych w oparciu o ten wzorzec. Wzorca który jest już napisany i wykorzystywany nie należy modyfikować modyfikacje te będą dotyczyły wszystkich klas stworzonych w oparciu o ten wzorzec. Gdy dodamy zmienne klasowe to powiększą się obiekty wszystkich tych klas wywiedzionych ze wzorca. Gdy dodamy zmienne klasowe to powiększą się obiekty wszystkich tych klas wywiedzionych ze wzorca. Gdy zmienimy definicje metod to zmiany będą dotyczyły wsystkich klas wywiedzionych ze wzorca. Gdy zmienimy definicje metod to zmiany będą dotyczyły wsystkich klas wywiedzionych ze wzorca. Zatem, zamiast modyfikacji wzorca danej klasy należy utworzyć wzorzec klasy pochodnej, o nowych właściwościach. Zatem, zamiast modyfikacji wzorca danej klasy należy utworzyć wzorzec klasy pochodnej, o nowych właściwościach.

13 Rozbudowywanie klas-wzorców Np.: potrzebujemy stosu łańcuchów z możliwością zapisu i odczytu do pliku Np.: potrzebujemy stosu łańcuchów z możliwością zapisu i odczytu do pliku template template class stos_plik: public stos class stos_plik: public stos { char * nazwa_pliku; char * nazwa_pliku;public: /* konstruktor, parametry: rozmiar i nazwa pliku */ /* konstruktor, parametry: rozmiar i nazwa pliku */ stos(int rozmiar, char * nazwa = NULL) stos(int rozmiar, char * nazwa = NULL) :stos (rozmiar) // konstrukcja rodzica :stos (rozmiar) // konstrukcja rodzica { // tutaj, lub za pomocą listy inicjalzacyjnej zachowaj nazwę pliku { // tutaj, lub za pomocą listy inicjalzacyjnej zachowaj nazwę pliku } void zapisz_stos(); void zapisz_stos(); void wczytaj_stos(); void wczytaj_stos();};

14 Wzorzec szczegółowy Jeżeli wzorzec działa niepoprawnie dla jakiegoś szczególnego parametru, to można zdefiniować inną wersję wzorca dla konkretnego parametru. Np. klasa która służy do porównywania elementów danego typu: Jeżeli wzorzec działa niepoprawnie dla jakiegoś szczególnego parametru, to można zdefiniować inną wersję wzorca dla konkretnego parametru. Np. klasa która służy do porównywania elementów danego typu: template template class porównywacz// wzorzec ogólny {public: static mniejszy(T &a, T &b) static mniejszy(T &a, T &b) { return a

15 Wzorzec szczegółowy Dla łańcuchów (char *) porównywane by były nie łańcuchy, ale ich adresy, więc definiujemy szczególną postać wzorca klasy porównywacz dla łańcuchów: Dla łańcuchów (char *) porównywane by były nie łańcuchy, ale ich adresy, więc definiujemy szczególną postać wzorca klasy porównywacz dla łańcuchów: class porównywacz // wzorzec szczegółowy {public: static mniejszy(const char * a, const char * b) static mniejszy(const char * a, const char * b) { return strcmp(a, b)<0; return strcmp(a, b)<0; }}; Kompilator wykorzysta szczególną postać wzorca, jeżeli w miejscu gdzie będzie potrzebna, będzie widoczna (czyli zadeklarowana wcześniej). W przeciwnym przypadku zostanie rozwinięty wzorzec ogólny. Kompilator wykorzysta szczególną postać wzorca, jeżeli w miejscu gdzie będzie potrzebna, będzie widoczna (czyli zadeklarowana wcześniej). W przeciwnym przypadku zostanie rozwinięty wzorzec ogólny.

16 Argumenty wzorca Argumentów wzorca może być wiele, oprócz klas i typów mogą to być napisy, nazwy funkcji lub wyrażenia stałe. Argumentów wzorca może być wiele, oprócz klas i typów mogą to być napisy, nazwy funkcji lub wyrażenia stałe. Np. wzorzec bufora, którego parametrem będzie rozmiar: Np. wzorzec bufora, którego parametrem będzie rozmiar: template template class bufor { T w[rozm]; T w[rozm]; //... //...} taki wzorzec bufora wykorzystujemy np. tak: taki wzorzec bufora wykorzystujemy np. tak: bufor tbf; // deklaracja obiektu f będącego // buforem na 250 figur // buforem na 250 figur bufor tbc; // bufor na 100 znaków

17 Wzorce Dwa typy wygenerowane ze wspólnego wzorca są identyczne jeżeli identyczne są argumenty wzorca, w przeciwnym przypadku są różne i nie wiąże ich pokrewieństwo. Dwa typy wygenerowane ze wspólnego wzorca są identyczne jeżeli identyczne są argumenty wzorca, w przeciwnym przypadku są różne i nie wiąże ich pokrewieństwo. Na przykład dla następujących deklaracji tylko obiekty tbc0 i tbc1 należą do tej samej klasy (klasy bufor ) pozostałe obiekty do obiekty różnych klas. Na przykład dla następujących deklaracji tylko obiekty tbc0 i tbc1 należą do tej samej klasy (klasy bufor ) pozostałe obiekty do obiekty różnych klas. bufor tbc0; bufor tbf0; bufor tbc1; bufor tbf1;

18 Wzorce funkcji template // jesteśmy poza deklaracją klasy void zamień(T &x, T &y)// nie metoda, a funkcja { T t=x; T t=x;x=y;y=t;} int a=7,b=8; zamień(a,b); // kompilator rozwinie wzorzec //(jeżeli jest widoczny)

19 Wzorce funkcji - przykład napisać rodzinę funkcji zwiększających wartość swojego pierwszego argumentu aktualnego o wartość drugiego argumentu (oba to typy liczbowe) napisać rodzinę funkcji zwiększających wartość swojego pierwszego argumentu aktualnego o wartość drugiego argumentu (oba to typy liczbowe) template template void zwieksz(t &i, double d)// zadzaiala dla wszystkich typów liczbowych {// ale jak ktos wywola zwieksz(1, 1) i+=t(d);// to będą 2 automatyczne konwersje i+=t(d);// to będą 2 automatyczne konwersje };// nieekologiczne --- marnotrawstwo czasu template template void zwieksz_szybciej (t &i, const d delta) // const nie zaszkodzi {// a może się przyda i+=t(delta); i+=t(delta);};

20 Wzorce funkcji - przykład napisać rodzinę funkcji zwiększających wartość swojego pierwszego argumentu aktualnego o wartość drugiego argumentu (oba to typy liczbowe), lub o 1 gdy nie podano drugiego argumentu. napisać rodzinę funkcji zwiększających wartość swojego pierwszego argumentu aktualnego o wartość drugiego argumentu (oba to typy liczbowe), lub o 1 gdy nie podano drugiego argumentu. // template // template // void zwieksz_1 (t &i, const d delta=1) // … Pułapka: po napotkaniu wywołania Pułapka: po napotkaniu wywołaniazwieksz_1( ); kompilator nie ma podstaw do określenia typu d! kompilator nie ma podstaw do określenia typu d!

21 Wzorce funkcji - przykład napisać rodzinę funkcji zwiększających wartość swojego pierwszego argumentu aktualnego o wartość drugiego argumentu (oba to typy liczbowe), lub o 1 gdy nie podano drugiego argumentu. napisać rodzinę funkcji zwiększających wartość swojego pierwszego argumentu aktualnego o wartość drugiego argumentu (oba to typy liczbowe), lub o 1 gdy nie podano drugiego argumentu. template template void zwieksz_1(t &i, const d delta) { i+=t(delta); i+=t(delta);}; template template void zwieksz_1(t &i) { i+=t(1); i+=t(1);};


Pobierz ppt "Wzorce. Wzorce klas – dlaczego? Pisząc programy często korzystamy z abstrakcyjnych typów danych, takich jak stos, kolejka czy drzewo. Implementacje takich."

Podobne prezentacje


Reklamy Google