C++ - szablony kontenerów. Kontenery i szablony kontenerów. C++ - szablony kontenerów. C++ - szablony kontenerów. C++ - szablony kontenerów

Podobne dokumenty
C++ - szablony kontenerów. Kontenery i szablony kontenerów. C++ - szablony kontenerów. C++ - szablony kontenerów. C++ - szablony kontenerów

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

STL: Lekcja 1&2. Filozofia STL

Część 4 życie programu

C++ - szablony. Szablony rozszerzenie. Szablony. Szablony rozszerzenie. Szablony rozszerzenie SZABLONY ROZSZERZENIA I UZUPEŁNIENIA

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

Szablony funkcji i szablony klas

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

Projektowanie klas c.d. Projektowanie klas przykład

ZASADY PROGRAMOWANIA KOMPUTERÓW

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

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

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

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

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

DYNAMICZNE PRZYDZIELANIE PAMIECI

I - Microsoft Visual Studio C++

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

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

Programowanie i struktury danych

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

Operator przypisania. Jest czym innym niż konstruktor kopiujący!

Szablony klas, zastosowanie szablonów w programach

PARADYGMATY PROGRAMOWANIA Wykład 4

Zmienne i struktury dynamiczne

Język C++ wykład VIII

Przeciążenie operatorów

Wskaźniki. Informatyka

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Szablony funkcji i klas (templates)

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

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

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Programowanie w C++ Wykład 7. Katarzyna Grzelak. 23 kwietnia K.Grzelak (Wykład 7) Programowanie w C++ 1 / 40

Podstawy Programowania Obiektowego

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Programowanie obiektowe w C++ Wykład 12

Wykład 4: Klasy i Metody

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

Programowanie komputerowe. Zajęcia 4

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

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

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

Lab 9 Podstawy Programowania

TEMAT : KLASY DZIEDZICZENIE

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

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Programowanie komputerowe. Zajęcia 5

Paradygmaty programowania. Paradygmaty programowania

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

Programowanie Obiektowe i C++

Operatory na rzecz typu TString

Szablony. Szablony funkcji

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

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

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy PRAWA PRZYJACIÓŁ KLASY. Dostęp z zewnątrz: Dostęp z wewnątrz:

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Język C++ część 9 szablony klas. Jarosław Gramacki Instytut Informatyki i Elektroniki. szablony funkcji

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

Mechanizm dziedziczenia

Dokumentacja do API Javy.

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

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Wprowadzenie do szablonów szablony funkcji

Stos liczb całkowitych

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

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

Wprowadzenie do szablonów szablony funkcji

Instrukcja do pracowni specjalistycznej z przedmiotu. Obiektowe programowanie aplikacji

STL Standardt Template Library (wprowadzenie)

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

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

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Programowanie w C++ Wykład 4. Katarzyna Grzelak. 19 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 37

Materiały do zajęć VII

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

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

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

Języki i techniki programowania Ćwiczenia 2

Zaawansowane programowanie w C++ (PCP)

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Obsługa wyjątków. Język C++ WW12

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

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

Konstruktor kopiujacy

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Programowanie i struktury danych

Kontenery i iteratory. Wykorzystanie kontenerów w praktyce.

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

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

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Wstęp do Programowania 2

> C++ dynamiczna alokacja/rezerwacja/przydział pamięci. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

TEMAT : KLASY POLIMORFIZM

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

Transkrypt:

Kontenery i szablony kontenerów Kontenery Kontener (lub inaczej pojemnik, ang. container, collection) to struktura danych, której zadaniem jest przechowywanie w zorganizowany sposób zbioru innych danych (obiektów). Kontener zapewnia wygodne narzędzia dostępu, w tym dodawanie, usuwanie i wyszukiwanie danej (obiektu) w kontenerze. Oczywistym kontenerem jest tablica (ale prymitywnym). Inne rodzaje kontenerów to: lista, drzewo, stos.. 119 Przykład szablon kontenera typu stos template<typename T, int rozm> class SzablonStosu T stos[rozm]; SzablonStosu() : top(0) void push(const T& i) stos[top++] = i; T pop() return stos[--top]; Przykład zastosowania kontenera stosu: int main(int argc, char *argv[]) SzablonStosu<int, 100> is; // Dodanie kilku liczb: is.push(i); // Zdjęcie ich ze stosu i jednoczesne // wypisanie na ekranie: for(int k = 0; k < 20; k++) printf("%i\n", is.pop()); 120 121 Efektywność kontenerów Jeżeli chcielibyśmy przygotować się na znaczną liczbę obiektów: 1. z których każdy miałby mieć przypisany swój stos, 2. przy czym tworząc struktury do zarządzania tymi obiektami, musielibyśmy również od razu tworzyć komplet pustych stosów gotowych do wykorzystania, to w momencie uruchomienia nastąpiłoby zajęcie dużej ilości pamięci. A jeżeli z czasem okazałoby się, że obiektów do przechowywania jest niewiele, to duża część pamięci zajętej przez stosy pozostałaby niewykorzystana. Efektywność kontenerów Należałoby pomyśleć o takim szablonie stosu, który pozwalałby na tworzenie kontenerów zajmujących początkowo niewiele pamięci, które osiągałyby swoje właściwe rozmiary dopiero w momencie pierwszego umieszczenia danych na stosie. Najprościej można opakować przykładowy szablon w inny szablon, działający tak, aby uniknąć tworzenia od razu całej struktury danych. 122 123 1

class SzablonStosu T stos[rozmiar]; SzablonStosu() : top(0) void push(const T& i) stos[top++] = i; T pop() return stos[--top]; template<typename U, int rozm> friend class SzablonSprytnegoStosu; class SzablonSprytnegoStosu SzablonStosu<T, rozmiar> *sp; SzablonSprytnegoStosu(): sp(0) void push(const T& i) if (!sp) sp = new SzablonStosu<T,rozmiar>; sp->push(i); T pop() if (sp->top) return sp->pop(); else return T(); ~SzablonSprytnegoStosu() delete sp; Wykorzystanie przykładowego sprytnego stosu SzablonSprytnegoStosu<int, 100> is; // minimalna alokacja pamięci przy tworzeniu obiektu-kontenera // Dodanie na stos kilku liczb: is.push(i); // w pierwszym wywołaniu - właściwa alokacja pamięci // Zdjęcie liczb ze stosu i wypisanie na ekranie: for(int k = 0; k < 20; k++) cout << is.pop() << endl; 124 125 Szablony i wykorzystanie relacji przyjaźni Przyjmijmy, że mamy funkcję lub szablon funkcji oraz klasę lub szablon klasy. Są cztery możliwe przypadki relacji między nimi: Jeden-do-wielu: Funkcja może być przyjacielem dla wszystkich instancji szablonu klasy (wszystkich klas, które zostały utworzone z szablonu) Wielu-do-jednego: Wszystkie instancje szablonu funkcji są przyjaciółmi regularnej klasy Jeden-do-jednego: Instancja szablonu funkcji dla określonego zbioru parametrów jest przyjacielem instancji klasy z takim samym zbiorem parametrów (identycznie jak w przypadku regularnych funkcji i klasy) Wielu-do-wielu: Wszystkie potencjalne instancje szablonu funkcji mogą być przyjaciółmi wszystkich potencjalnych instancji szablonu klasy 126 Przykład relacji między szablonami i klasami lub funkcjami // 1. szablon funkcji int g().. // 2. szablon funkcji int h().. // 3. klasa // 4. szablon klasy class A friend int f(t); // działa tylko gdy: T==double (!) int e().. // 5. funkcja UKSW, intwmp. f(double) SNS, Warszawa.. // 6. funkcja 127 int g().. int h().. class A friend int f(t); int e().. int f(double).. jeden-do-wielu: funkcja e i szablon klasy A wielu-do-jednego: szablon funkcji g i klasa B jeden-do-jednego: funkcja f i szablon klasy A, oraz szablon funkcji g i szablon klasy A wielu-do-wielu: szablon funkcji h i szablon klasy A int g().. int h().. class A friend int f(t); int e().. int f(double).. jeden-do-wielu: funkcja e i szablon klasy A wielu-do-jednego: szablon funkcji g i klasa B jeden-do-jednego: funkcja f i szablon klasy A, oraz szablon funkcji g i szablon klasy A wielu-do-wielu: szablon funkcji h i szablon klasy A 128 129 2

int g().. int h().. class A friend int f(t); int e().. int f(double).. jeden-do-wielu: funkcja e i szablon klasy A wielu-do-jednego: szablon funkcji g i klasa B jeden-do-jednego: funkcja f i szablon klasy A, oraz szablon funkcji g i szablon klasy A wielu-do-wielu: szablon funkcji h i szablon klasy A int g().. int h().. class A friend int f(t); int e().. int f(double).. jeden-do-wielu: funkcja e i szablon klasy A wielu-do-jednego: szablon funkcji g i klasa B jeden-do-jednego: funkcja f i szablon klasy A, oraz szablon funkcji g i szablon klasy A wielu-do-wielu: szablon funkcji h i szablon klasy A 130 131 class SzablonStosu // 1. relacja: wielu-do-wielu template<typename U, int rozm> friend class SzablonSprytnegoStosu; albo.. // 2. relacja jeden-do-jednego: friend class SzablonSprytnegoStosu<T, rozmiar>; class SzablonSprytnegoStosu SzablonStosu<T, rozmiar> *sp; SzablonSprytnegoStosu(): sp(0) void push(const T& i) if (!sp) sp = new SzablonStosu<T,rozmiar>; sp->push(i); T pop() if (sp->top) return sp->pop(); else return T(); ~SzablonSprytnegoStosu() delete sp; Kontenery i prawo własności obiektów 1. Kontenery przechowujące obiekty (w postaci wartości) są bezwzględnie właścicielami tych obiektów. Autorzy kodu kontenerów mają obowiązek napisać kod tak, aby przy usuwaniu instancji kontenerów usunąć uprzednio wszelkie obiekty przechowywane w kontenerze. 2. Kontenery przechowujące tylko wskaźniki do obiektów muszą mieć ustalone reguły własności obiektów wskazywanych przez te wskaźniki (po czyjej stronie spoczywa obowiązek usunięcia zawartości kontenera przy okazji usuwania całego kontenera po stronie kontenera czy programisty). Nie ma Garbage Collectora, wiec nie można udawać, że raz zajęta pamięć jakoś potem sama się zwolni.. 132 133 Kontenery i prawo własności obiektów 1. Kontenery przechowujące obiekty (w postaci wartości) 2. Kontenery przechowujące wskaźniki do obiektów Najlepiej pisać kod wariantowo, tak aby programista sam np. przy wywołaniu konstruktora kontenera określał w argumencie wywołania reguły własności Można też wprowadzić atrybuty własności dla obiektów i dodać usługi zmieniające stan tych atrybutów Szablony kontenerów Iteratory 134 3

Iteratory Iterator jest obiektem, który porusza się w obrębie kontenera po przechowywanych w nim obiektach. Udostępnia on za każdym razem pojedynczy, zawarty w kontenerze obiekt, nie zapewnia jednak bezpośredniego dostępu do implementacji tego kontenera (do metod i pól). Iteratory stanowią standardowy sposób dostępu do elementów. Klasy iteratorów najczęściej są tworzone wraz z klasami kontenerów. Iteratory Mówiąc potocznie, iteratory są sprytnymi wskaźnikami, jednak w odróżnieniu od prawdziwych wskaźników są bezpieczne w użyciu dużo trudniej jest doprowadzić do sytuacji odpowiadającej np. przekroczeniu zakresu indeksów w tablicy. Tradycyjnie obiekt-iterator jest tworzony za pomocą takiego konstruktora, który przyłącza go do pojedynczego obiektukontenera. W czasie swojego życia obiekt-iterator nie jest zazwyczaj przyłączany do żadnego innego kontenera. 136 137 Przykład stosu i jego iteratora class IntStos enum rozm = 100 int Stos[rozm]; IntStos() : top(0) void push(int i) assert(top < rozm); Stos[top++] = i; int pop() assert(top > 0); return Stos[--top]; friend class IntStosIter; class IntStosIter // klasa iteratora IntStos& s; IntStosIter(IntStos& is):s(is),index(0) int operator++() // Prefix return s.stos[++index]; int operator++(int) // Postfix return s.stos[index++]; Korzystanie z kontenera i iteratora: int main(int argc, char *argv[]) IntStos is; // obiekt lokalny typu stos // zapełniamy stos wartościami is.push(i); IntStosIter it(is); // obiekt lokalny typu iterator // przemieszczamy się po stosie za pomocą iteratora: for(int j = 0; j < 20; j++) cout << it++ << endl; 138 139 Korzystanie z kontenera i iteratora: Tak jak w przypadku wskaźnika, zadaniem obiektu typu IntStosIter jest przemieszczanie się po IntStos i zwracanie wartości. W tym prostym przykładzie IntStosIter może poruszać się tylko w przód (używając zarówno przed jak i przyrostkowej formy operatora ++). Jednak, oprócz narzuconych przez kontener, na którym pracuje, nie ma żadnych ograniczeń na to, jak iterator może być zdefiniowany. Możemy poruszać się po elementach stosu nie pobierając ich, tak jakby to była tablica, dzięki temu, że iterator zna strukturę stosu oraz ma dostęp do wszystkich składowych kontenera (jest friend) Korzystanie z kontenera i iteratora: Iterator ma za zadanie upraszczać złożony proces bezpiecznego przemieszczania się od jednego elementu kontenera do drugiego. Całkowicie akceptowalnym jest, aby iterator przemieszczał się w dowolnym kierunku "swojego" kontenera, a także by powodował zmiany wartości elementów (w ramach ograniczeń jakie narzuca dany kontener). Dąży się do tego, aby każdy iterator posiadał taki sam interfejs, niezależnie od tego, jakie elementy w jakim kontenerze wskazuje. 140 141 4

Korzystanie z kontenera i iteratora: W bibliotekach STL każdy kontener zawsze ma związaną ze sobą klasę zwaną iteratorem. Najczęściej klasa iteratora jest zadeklarowana jako zagnieżdżona wewnątrz deklaracji kontenera i ma nazwę iterator. Służy do tworzenia obiektów typu iterator dopasowanych co do zasady działania do danej klasy kontenerów. class IntStos enum ssize = 100 int stack[ssize]; IntStos() : top(0) void push(int i) assert(top < ssize); stack[top++] = i; int pop() assert(top > 0); return stack[--top]; class iterator IntStos& s; iterator(intstos& is) : s(is), index(0) int operator++() // Prefix return s.stack[++index]; int operator++(int) // Postfix return s.stack[index++]; iterator begin() return iterator(*this); // zwraca iterator friend class iterator; 142 143 class IntStos enum ssize = 100 int stack[ssize]; IntStos() : top(0) void push(int i) assert(top < ssize); stack[top++] = i; int pop() assert(top > 0); return stack[--top]; class iterator IntStos& s; iterator(intstos& is) : s(is), index(0) iterator(intstos& is, bool) : s(is), index(s.top) int operator++() // Prefix return s.stack[++index]; int operator++(int) // Postfix return s.stack[index++]; iterator begin() return iterator(*this); iterator end() return iterator(*this, true); friend class iterator; class IntStos enum ssize = 100 int stack[ssize]; IntStos() : top(0) void push(int i) assert(top < ssize); stack[top++] = i; int pop() assert(top > 0); return stack[--top]; class iterator IntStos& s; iterator(intstos& is) : s(is), index(0) iterator(intstos& is, bool) : s(is), index(s.top) int operator++() // Prefix return s.stack[++index]; int operator++(int) // Postfix return s.stack[index++]; bool operator==(const iterator& rv) const return index == rv.index; bool operator!=(const iterator& rv) const return index!= rv.index; iterator begin() return iterator(*this); iterator end() return iterator(*this, true); friend class iterator; 144 145 Korzystanie z rozszerzonych wersji klas IntStos i iterator: Korzystanie z rozszerzonych wersji klas IntStos i iterator: int main(int argc, char *argv[]) IntStos is; Przydałby się sposób pozyskiwania elementu wskazywanego przez iterator, bez jednoczesnej jego inkrementacji. W tym celu dodajmy do klasy IntStos::iterator przeciążony operator *: // zapełniamy stos wartościami is.push(i); int operator*() const return s.stack[index]; // przemieszczamy się po stosie za pomocą iteratora: IntStos::iterator it = is.begin(); while(it!= is.end()) // nie musimy znać rozmiaru stosu.. cout << it++; 146 147 5

Korzystanie z rozszerzonych wersji klas IntStos i iterator: Teraz chcielibyśmy móc napisać w programie tak: IntStos::iterator it; for (it = is.begin(); it!=is.end(); it++) printf("%i\n",*it); To jednak wymaga utworzenia najpierw konstruktora domyślnego w klasie IntStos::iterator. Dopisujemy konstruktor domyślny: ale.. iterator() 1>------ Rebuild All started: Project: kontenerstosu, Configuration: Debug Win32 ------ 1> main.cpp 1>d:\zpodemo2014\kontenerstosu\intstos.h(25): error C2758: 'IntStos::iterator::s' : must be initialized in constructor base/member initializer list 1>d:\zpodemo2014\kontenerstosu\intstos.h(22) : see declaration of 'IntStos::iterator::s' ========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========.. dowiadujemy się, że składowej referencyjnej w obiekcie nie wolno zostawiać bez inicjalizacji. Rezygnujemy więc z iteratorów, które nie są przypisane do żadnego stosu. 148 149 Ostatecznie dodajemy do klasy IntStos::iterator tylko przeciążony operator przypisania: iterator& operator=(const iterator& rv) index = rv.index; return *this; Dlatego nasz kod korzystający ze stosu wygląda tak: IntStos::iterator it(is); // tworzymy iterator i.. // robimy pętlę for po elementach stosu: for (it = is.begin(); it!=is.end(); it++) printf("%i\n",*it); Suplement jeżeli nadal....jest to dla nas niekomfortowe, że musimy deklarować iteratory dopiero po zadeklarowaniu obiektu reprezentującego stos, to: aby uniknąć problemów z konstruktorem domyślnym zamiast zmiennej referencyjnej możemy używać w klasie iterator wskaźnika. class iterator IntStos* s; Teraz już bez problemu dodajemy konstruktor domyślny oraz odpowiednio dostosowujemy kod metod składowych iteratora. 150 151 Dlaczego iteratory są takie ważne przy korzystaniu z kontenerów? Jeżeli połączymy iteratory z szablonami, dziedziczeniem i polimorfizmem dostajemy zupełnie nowe możliwości programowania. Przykład: Przyjmijmy, że mamy kilka różnych typów kontenerów, każdy ze swoim typem iteratora. Na podstawie każdego z nich został zadeklarowany inny obiekt. Przyjmijmy, że mamy kilka różnych klas dziedziczących po jednej wspólnej klasie bazowej, reprezentujących np. figury na płaszczyźnie. Każdy obiekt przechowuje współrzędne położenia figury na płaszczyźnie oraz inne jej atrybuty. Każdy posiada metodę rysuj, która jest metodą polimorficzną Obiekty różnych typów są przechowywane w kontenerach różnych typów. Liczba typów kontenerów i typów obiektów może się zmieniać. 152 153 6

Chcielibyśmy dla dowolnego podzbioru tych obiektów wywołać ich metodę polimorficzną rysuj, jednak kontenery nie mają wspólnego typu bazowego, mają natomiast różne metody dostępu do danych, które przechowują. Korzystamy więc z ich iteratorów: template<typename Iter> void wypisz(iter start, Iter end) while (start!=end) (*start)->rysuj(); start++; Szablon wypisz można nazywać algorytmem ponieważ określa, jak zrobić coś z wykorzystaniem danych iteratorów, opisujących zakres elementów. Jest to możliwe przy założeniu, że iteratory te mogą być wyłuskiwane, porównywane i inkrementowane. Takie właściwości mają wszystkie iteratory biblioteki STL. Uzyskanie takiej ogólności bez pośrednictwa iteratorów nie byłoby możliwe kontenery mogą mieć różne właściwości i ich bezpośrednie używanie nie pozwala na jednorodne ich stosowanie. Tylko iteratory pozwalają na: napisanie kodu ogólnego przeznaczenia bez znajomości budowy i zasady działania kontenerów 154 155 Lekko zmodyfikowany szablon wypisz może się przydać również w przypadku klasykontenera: IntStos template<typename Iter> void wypisz(iter start, Iter end) while (start!=end) cout << *start << endl; start++; int main(int argc, char *argv[]) IntStos is; is.push(i); double tab[20]; tab[i] = 20-i; wypisz(is.begin(), is.end()); wypisz(tab, tab+20); SZABLONY ROZSZERZENIA I UZUPEŁNIENIA 156 157 Szablony rozszerzenia i uzupełnienia: Szablon jako parametr szablonu Zagnieżdżanie Specjalizacja jawna Częściowe uporządkowanie szablonów klas Cechy charakterystyczne Rekurencyjny wzorzec szablonu Szablony szablon jako parametr szablonu 158 7

Są dwie odmiany szablonów: 1. klas, 2. funkcji. Każdy parametr szablonu może być: 1. typem (wbudowanym lub zdefiniowanym przez użytkownika), class ElementListy; 2. stałą w chwili kompilacji, class SzablonStosu 3. innym szablonem.. Przykład Przyjmijmy, że mamy szablon reprezentujący kontener typu stos, o liczbie komórek tablicy dostosowującej się adaptacyjnie do potrzeb: template<class T> class DynamicznyStos enum INIT = 10 T* data; size_t capacity; size_t count; DynamicznyStos(); ~DynamicznyStos(); void push_back(const T& t); void pop_back(); T* begin() return data; T* end() return data + count; 160 161 template<class T> void push_back(const T& t) class DynamicznyStos if(count == capacity) enum INIT = 10 size_t newcap = 2 * capacity; T* data; T* newdata = new T[newCap]; size_t capacity; for(size_t i = 0; i < count; ++i) size_t count; newdata[i] = data[i]; delete [] data; DynamicznyStos() data = newdata; count = 0; capacity = newcap; data = new T[capacity = INIT]; data[count++] = t; ~DynamicznyStos() delete [] data; void pop_back() if(count > 0) --count; T* begin() return data; T* end() return data + count; Przyjmijmy, że potrzebujemy teraz zadeklarować uniwersalny kontener, który będzie miał jednorodny zbiór metod i operatorów udostępniających zasoby, innych niż to, co posiada DynamicznyStos. Zdefiniujmy teraz szablon reprezentujący kontener, którego parametrami są typ przechowywanych danych oraz inny, dowolny kontener (!) 162 163 template<typename T, template<typename> class Seq> class Kontener Seq<T> seq; DynamicznyStos jest zwykłym szablonem klasy Kontener jest szablonem klasy, który przechowuje dane w strukturze budowanej na podstawie innego szablonu kontenera Kluczowa linijka kodu w Kontener to: Seq<T> seq; W nagłówku Kontener nie jest konieczne podawanie symbolu dla typu używanego przez Seq: template<typename T, template<typename> class Seq> Tak też można (wersja dla purystów): template<typename T, class Seq> ale U jest to informacja nieistotna ważniejsze jest to, że Seq jest jednoparametrowy. 164 165 8

template<typename T, template<typename> class Seq> class Kontener Seq<T> seq; void append(const T& t) seq.push_back(t); T* begin() return seq.begin(); T* end() return seq.end(); int main() Kontener<int, DynamicznyStos> C; C.append(0); C.append(1); C.append(2); int* p = C.begin(); while(p!= C.end()) cout << *p++ << endl; system("pause"); return EXIT_SUCCESS; ///:~ Inny przykład: dany jest pewien szablon ze stałą.. template<typename T, size_t N> class Stosik T data[n]; size_t count; Stosik() count = 0; void push_back(const T& t) if(count < N) data[count++] = t; void pop_back() if(count > 0) --count; T* begin() return data; T* end() return data + count; 166 167 Inny przykład - zastosowanie template<typename T, size_t N, template<typename,size_t> class Seq > class Kontenerek Seq<T,N> seq; void append(const T& t) seq.push_back(t); T* begin() return seq.begin(); T* end() return seq.end(); int main() const size_t N = 10; Kontenerek<int, N, Stosik> C; C.append(0); C.append(1); C.append(2); int* p = C.begin(); while(p!= C.end()) cout << *p++ << endl; ///:~ 168 9