Konstrukcja uniwersalnych klas

Podobne dokumenty
Klasy generyczne. ZbiórLiczb. ZbiórCzegokolwiek. Zbiór

Szablony funkcji i szablony klas

Szablony funkcji i klas (templates)

Szablony klas, zastosowanie szablonów w programach

Szablony. Szablony funkcji

Abstrakcyjny typ danych

PARADYGMATY PROGRAMOWANIA Wykład 4

Języki i techniki programowania Ćwiczenia 4 Wzorce

TYPY GENERYCZNE (GENERICS)

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

Stos liczb całkowitych

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

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

Typy sparametryzowane

Metody sparametryzowane

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

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

Efekty uboczne błędów

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Wprowadzenie do szablonów szablony funkcji

Programowanie obiektowe

Wprowadzenie do szablonów szablony funkcji

Programowanie obiektowe. Wykład 5. C++: szablony

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

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

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Wprowadzenie do szablonów klas

Kurs programowania. Wykład 9. Wojciech Macyna

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

Algorytmy i Struktury Danych. Anna Paszyńska

Zaawansowane programowanie w języku C++ Funkcje uogólnione - wzorce

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Język C++ wykład VIII

Programowanie w Internecie. Java


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

Programowanie w języku C++

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

Enkapsulacja, dziedziczenie, polimorfizm

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

Definicje klas i obiektów. Tomasz Borzyszkowski

Wykład 4. Klasa List Kolejki Stosy Słowniki

Dziedziczenie. dr Jarosław Skaruz

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Klasyfikacja wyjątków

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

Programowanie obiektowe

Wykład 4: Klasy i Metody

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Wykład 5: Więcej o Klasach i Metodach

Dawid Gierszewski Adam Hanasko

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

Metody Metody, parametry, zwracanie wartości

Podstawy Programowania Obiektowego

10.1 Szablony Szablony funkcji Szablony klas Szablony jako wstęp do biblioteki STL... 10

Wykład 6: Dziedziczenie

Platformy Programistyczne Podstawy języka Java

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

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

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

Kiedy potrzebne. Struktura (rekord) Struktura w języku C# Tablice struktur. struktura, kolekcja

Polimorfizm a klasy generyczne w języku Java. Zdzisław Spławski 1

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

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Mechanizm dziedziczenia

Zaawansowane programowanie w C++ (PCP)

Programowanie obiektowe

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

Polimorfizm. dr Jarosław Skaruz

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

Zad.30. Czy można utworzyć klasę, która implementuje oba interfejsy?

Microsoft IT Academy kurs programowania

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

Języki i metody programowania Java. Wykład 2 (część 2)

Programowanie obiektowe

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

Kurs programowania. Wykład 13. Wojciech Macyna. 14 czerwiec 2017

Dziedziczenie. Tomasz Borzyszkowski

Podstawy i języki programowania

Programowanie obiektowe - 1.

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

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

Programowanie i struktury danych

Podejście obiektowe. Tablice obiektów Przykład 1 metody i atrybuty statyczne oraz niestatyczne

Technologie i usługi internetowe cz. 2

Wykład 3 Składnia języka C# (cz. 2)

Programowanie obiektowe w C++ Wykład 12

Język ludzki kod maszynowy

Języki i metody programowania Java INF302W Wykład 2 (część 1)

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

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

Definiowanie własnych klas

Technologie cyfrowe semestr letni 2018/2019

Programowanie i projektowanie obiektowe

Programowanie, część I

Programowanie obiektowe

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

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

Programowanie obiektowe

TEMAT : KLASY DZIEDZICZENIE

Transkrypt:

Klasy generyczne

Konstrukcja uniwersalnych klas Problemem do rozwiązania jest tworzenie uniwersalnego kodu, który w ten sam sposób - te same algorytmy przetwarza różne typy danych. Specyfikacja wąsko specjalizowanej klasy ZbiórLiczb. Dany zbiór może zawierać jedynie liczby typu Integer. Klasa weryfikuje poprawność elementów zbioru. ZbiórLiczb licznik: Integer tablica [0..*] : Integer dodaj(integer) : bool usuń(integer) : bool znajdź(integer) : bool Specyfikacja uniwersalnej klasy Zbiór. Dany zbiór może posiadać elementy dowolnego typu. Klasa nie weryfikuje typów elementów zbioru!!! ZbiórCzegokolwiek licznik: Integer tablica [0..*] : Any dodaj(any) : bool usuń(any) : bool znajdź(any) : bool Specyfikacja uniwersalnej generycznej klasy Zbiór. Dany zbiór może zawierać jedynie elementy określonego typu, który jest weryfikowany przez klasę. Zbiór licznik: Integer tablica [0..*] : T dodaj(t) : bool usuń(t) : bool znajdź(t) : bool T

Uniwersalność, a poprawność przetwarzania programów Wąsko specjalizowane klasy dzięki kontroli typów - duża niezawodność programów mała użyteczność modułów programowych redundancja kodu ZbiórLiczb zl; //osobne klasy dla każdego typu elementów ZbiórOsób zo; ZbiórNapisów zn;... zl.add(1250); zo.add(kowalski); zn.add("ala ma kota"); zl.test(3200); zo.test(127); // wykrycie błędu, niepoprawny typ danej Klasy uniwersalne błędy w czasie wykonania, w wyniku braku kontroli typów większa użyteczność modułów programowych brak redundancji kodu, bardziej złożony kod class Zbiór { public: int add(void *element); int remove(void *element); int test(void *element); ; Osoba kowalski("kowalski"); Figura trójkąt(28, 15, 23);... Zbiór pracownicy; Pracownicy.add(kowalski); Pracownicy.add(trójkąt); //potencjalne błędy czasu wykonania

Statycznie i dynamicznie typowane języki programowania Bezpieczeństwo: języki statycznie typowane Elementami kolekcji są obiekty ściśle określonego typu class SortedCollectionOfInteger { protected: unsigned rozmiar; int tab[ ]; public: SortedCollectionOfInteger( ); add(int el);... ; class SortedCollectionOfFloat {...; class SortedCollectionOfString {...; SortedCollectionOfFloat scf(10); SortedCollectionOfString scs(10); scf.add(12.495); // kontrola typu scs.add("ala ma kota"); // kontrola typu Elastyczność: języki dynamiczne typowane Elementami kolekcji są obiekty dowolnego typu napis String new: 'Boguś jest OK'. liczba 3.1415. tablica #(1, 'dwa', $3). kolekcja SortedCollection new. kolekcja add: napis. "brak kontroli typów" kolekcja add: liczba. kolekcja add: tablica

Klasy generyczne Klasy generyczne są to klasy o parametryzowanych typach danych. Klasy generyczne posiadają kompletną implementację, jednak nie definiują typów danych wykorzystanych w tej implementacji. Klasy generyczne pozwalają na jednoczesne osiągnięcie elastyczności i bezpieczeństwa programów komputerowych. Pozwalają one tworzyć uniwersalne klasy o statycznie typowanych danych. class Lista [T] T- parametryzacja typu class ListaLiczba class ListaOsób class ListaNapisów Dziedziczenie (generalizacja klas) Parametryzacja typów (klasy generyczne) kolekcja[t] (add, remove, count, select) Dziedziczenie lista[t] (add, remove, count, select, first, next) lista_posortowana[t] (add, remove, count, select, first, next) Klasy generyczne pojazdy książki osoby

Klasy generyczne - Java Klasa generyczna jest klasą, której definicja zawiera sparametryzowane typy danych. Zaimplementowany poniżej stos będzie poprawnie obsługiwał stosy jednorodnych elementów dowolnego typu podstawionego pod parametr T. class Stos<T> { // Stos jest klasą generyczną private int index = 0; public static final int max = 10; private T[] data; // tablica elementów typu T public Stos(int rozmiar) { data = (T[])new Object[max]; public void push(t obj) {//wstaw element typu T data[index++] = obj; public T pop() { // pobierz element typu T return data[--index];

Tworzenie wystąpień klas generycznych Klasa generyczna pozwala tworzyć różniące się między sobą wystąpienia. Tworząc wystąpienie klasy generycznej należy podać wartość parametru typu. // wartość parametru T równa int Stos<Integer> si = new Stos<Integer>(12); // wartość parametru T równa float Stos<Float> sf = new Stos<Float>(50); // wartość parametru T równa String Stos<String> ss = new Stos<String>(100); // wartość parametru T równa Point Stos<Point> sp = new Stos<Point>(100); si.push(17); si.push(21); sf.push(934.128f); ss.push("potężne kaczory"); ss.push("brzydkie kaczątko"); Point p1 = new Point(2.13F, 1.47F); sp.push(p1); float f = sf.pop();

Kontrola typów Klasy generyczne gwarantują statyczne typowanie danych. Kompilator będzie weryfkował poprawność elementów wstawianych na generyczny stos. Stos<Integer> si = new Stos<Integer>(); Stos<Point> sp = new Stos<Point>(); Stos<Figure> sf = new Stos<Figure>(); int i, j; Point p; Figure f; Wielokąt w; Czworokąt c;... sf.push(f); // OK sf.push(908); // kontrola typów - błąd kompilacji int i = sf.pop(); // kontrola typów - błąd kompilacji Dopuszczalne są polimorficzne podstawienia pod zmienne typu. sf.push(w); // OK; wielokąt jest podtypem figury sf.push(c); // OK; czworokąt jest podtypem figury f = sf.pop(); // OK; c = sf.pop(); // błąd typu, nie każda figura jest czworokątem

Ograniczona generyczność klas W pewnych sytuacjach jest niezbędne odwoływania się do określonej funkcjonalności wystąpień sparametryzowanych typów danych. Czy implementacja poniższej klasy generycznej jest poprawna dla dowolnego typu parametru? class Stos<T> { private int index = 0; private T[] data; public Stos(int rozmiar) { data = (T[])new Object[rozmiar]; public void push(t obj) { data[index++]=obj; public T pop() {return data[--index]; double average() { double sum = 0.0; for(int i=0; i < data.length; i++) // odwołanie do funkcjonalności elementów // tylko elementy, które można dodawać sum += data[i]; return sum / data.length;

Ograniczona generyczność klas Dla zagwarantowania poprawności przetwarzania, wartości parametrów aktualnych klasy generycznej muszą być ograniczone do klas o pożądanej funkcjonalności. class Stos<T extends Number> { private int index = 0; private T[] data;... double średnia() { double sum = 0.0; // odwołanie do funkcjonalności zdefiniowanej // w klasie Number (doublevalue()) for(int i=0; i < data.length; i++) sum += data[i].doublevalue(); return sum / data.length; Metody i operacje zdefiniowane w klasie Number

Dynamiczne wiązanie wystąpień klas generycznych (Wildcard Type) Co jest nadklasą wszystkich wystąpień klasy generycznej? Stos<String> ss = new Stos<String>(); Stos<Integer> si = new Stos<Integer>(); Stos<Osoba> so = new Stos<Osoba>(); Stos<Object> sob = new Stos<Object>(); sob = ss; // błąd sob = si; // błąd sob = so; // błąd Nadklasą jest klasa o nieokreślonym typie argumentu: Stos<?> Dzięki temu są możliwe podstawienia polimorficzne dla wystąpień klas generycznych. Stos<?> sa = new Stos<Osoba>(); Nieokreślony typ argumentu klasy generycznej może być ograniczony do wybranej podhierarchii klas. Stos<? extends Shapes> ss = new Stos<Circle>();

Wymazywanie typu W kodzie wynikowym programów napisanych w języku Java, typ generyczny jest zastępowany wymazywany (ang. type eraser). Nieograniczony symbol typu T jest zastępowany przez typ Object, a nie przez wartość sparametryzowanego typu. class Stos<T> {...... Stos<Osoba> so = new Stos<Osoba>(); Parametrowi T jest przypisywana wartość Object, nie Osoba. Ograniczony symbol typu T extends S jest zastępowany przez typ S. class Stos<T extends Number> {...... Stos<Float> so = new Stos<Float>(); Parametrowi T jest przypisywana wartość Number, nie Float. W kodzie wynikowym klasy generyczne są zwykłymi klasami. Ten sam kod jest współdzielony przez wszystkie wystąpienia klasy generycznej.

Wymazywanie typu To kompilator jest odpowiedzialny za poprawną weryfikację typów danych, a nie kod wynikowy. Wystąpienia klas generycznych, nie przechowują informacji o typie danych. Stos<Float> sf = new Stos<Float>(50); Stos<String> ss = new Stos<String>(100); sf.push(934.128f); ss.push("potezne kaczory"); ss.push("brzydkie kaczatko"); Stos<?> gs = ss; // podstawienie polimorficzne Stos<Float> ns = (Stos<Float>)o; // brak błędu ns.push(934.128f); // niemożność weryfikacji typu float f = ns.pop(); // poprawne f = ns.pop(); // błąd rzutowania String na float // to jest błąd czasu wykonania

Ograniczenia mechanizmu generyczności Nagłówki metod zawierających parametry typu muszą być rozróżnialne dla wszystkich wartości parametrów. class KlasaGeneryczna<X,Y> { X ob1; Y ob2; // Wymazywanie typu spowoduje utworzenie // dwóch identycznych metod void set(x o) { // set(object o) ob1 = o; void set(y o) { // set(object o) ob2 = o; Kod klasy generycznej nie może zawierać wywołań konstruktorów sparametryzowanych typów danych. Wystąpienie klasy generycznej zawiera tylko jeden kod. class KlasaGeneryczna2<X> { X ob; KlasaGeneryczna2() { ob = new X(); // Tylko jeden konstruktor

Ograniczenia mechanizmu generyczności Kolejne ograniczenia dotyczą metod i zmiennych klasowych klas generycznych. Klasa generyczna ma jeden zestaw zmiennych i metod niezależnie od liczby wystąpień tej klasy o różnej wartości sparametryzowanych typów. public class KlasaGeneryczna3<X> { // Atrybuty klasy generycznej nie są generyczne // Jest tylko jedna zmienna ob static X ob; // Metody klasy generycznej nie są generyczne // Jest tylko jedna wersja metody getob() static X getob() { return ob; Uwaga: Język Java umożliwia generowanie statycznych metod generycznych w klasach niegenerycznych. Metody te mogą być używane jako generyczne funkcje nie powiązane z klasami obiektów.

Szablony klas C++ Szablon klasy opisuje grupę potencjalnych klas zgodnych z tym szablonem. Definicja szablonu: template <class T> class Tablica { protected: unsigned rozmiar; T *tp; public: Tablica (unsigned roz = 12); T &operator[](unsigned indeks) {return *(tp + indeks); ~ Tablica ( ) { delete [] tp; ; template <class T> Tablica<T>:: Tablica ((unsigned roz) { tp = new T[rozmiar = roz]; for (int i=0; i<rozmiar; i++) *(tp+i) = 0; Powyższy kod źródłowy nie zostanie skompilowany. Kompilator nie posiada informacji wystarczających dla utworzenia kodu wynikowego dla szablonu.

Definicja wystąpień szablonu Definicje wystąpień szablonu są rozwijane przez kompilator do postaci źródłowej (jedna wersja dla każdej wartości parametru aktualnego typu!!!). Może to prowadzić to do nadmiernego rozrastania kodu wynikowego. Jednak wydajność kodu jest taka sama jak dla klas nie-generycznych. Tablica<char> ct(25); // kod programisty // kod źródłowy generowany podczas kompilacji Tablica<char>::Tablica (unsigned roz) { tp = new char[rozmiar = roz]; for (int i=0; i<rozmiar; i++) *(tp+i) = 0; Tablica<double> dt(1024); // ==> Tablica<double>::Tablica (unsigned roz) { tp = new double [rozmiar = roz]; for (int i=0; i<rozmiar; i++) *(tp+i) = 0; ct[0] = 'a'; // ==> char& operator[](unsigned indeks) { return *(tp + indeks); dt[0] = 1034905.79; // ==> double& operator[](unsigned indeks) { return *(tp + indeks);

Implementacja szablonu klasy Język C++ nie wspiera ograniczonej generyczności klas. Możliwe jest za to implementowanie przez programistę kodu dla poszczególnych wartości parametrów szablonu. template <class T> class Tablica { protected: unsigned rozmiar; T *tp; public:... Tablica& sortuj( ); ; template<class T> Tablica& Tablica<T>::sortuj () { for (int i=0; i<rozmiar-1; i++) for (int j=rozmiar-1; i < j; j--) if ( tp[j] < tp[j-1] ) { T pom = tp[j]; tp[j] = tp[j-1]; tp[j-1] = pom; Implementacja metody sortuj() dla wystąpienia szablonu dla parametru typu char*: Tablica& Tablica<char*>::sortuj () { for (int i=0; i<rozmiar-1; i++) for (int j=rozmiar-1; i < j; j--) if ( strcmp(tp[j], tp[j-1] )) < 0 { T pom = tp[j]; tp[j] = tp[j-1]; tp[j-1] = pom;

Inne parametry szablonów W języku C++ klasy wzorce mogą być parametryzowane nie tylko za pomocą typów danych. Parametrami mogą być również dowolne inne zmienne. W poniższym przykładzie dodatkowym parametrem klasy wzorca jest liczba reprezentująca wielkość tablicy. template <class X, int rozmiar> class Tablica { protected: X tp[rozmiar]; public: Tablica ( ); X& operator[](unsigned indeks) { return tp[indeks]; ; Tablica<char, 1024> t1; // są to definicje dwóch Tablica<char, 24> t2; // różnych klas Tablica<Complex, 24> t3; Tablica<char, 24> t4; t2 = t3; t2 = t4; t1 = t2; // błąd typu // OK // błąd typu

Generyczne metody i zmienne klasy w języku C++ Ponieważ implementacja szablonów klas polega na kreowaniu osobnych klas dla wszystkich różnych wartości parametru typu, w języku C++ można korzystać z generycznych zmiennych i metod klasy (nie tylko obiektów). Dla każdej klasy będącej implementacją szablonu, jest tworzona osobna kopia generycznych zmiennych i metod klasy. template <class T>class Obiekt { private: T typ; int wielkosc; public: static int l_ob; Obiekt(int, T); T Typ() {return typ; static int PodajLiczbeObiektow() {return l_ob; ; template <class T> int Obiekt<T>:: l_ob = 0; template <class T> Obiekt<T>::Obiekt(int w, T t) { ++ l_ob; typ = t; wielkosc = w; int main() { Obiekt<char> o1(7,'k'); Obiekt<char> o2(1,'m'); Obiekt<float> o3(7,5.5); int i=obiekt<char>::podajliczbeobiektow(); int j=obiekt<float>::podajliczbeobiektow(); cout<<"char = "<<i<<", float = "<<j;

Szablony funkcji Definicja szablonu funkcji: Template<class x25> x25 max(x25 a, x25 b) { return a > b? a : b; Utworzenie implementacji szablonu funkcji: int a, b; char c, d;... int max1 = max (a, b); // max (int, int); char max2 = max (c, d); // max (char, char); int max3 = max (a, c); // błąd typu Rozróżnianie przeciążonych funkcji szablonu: int a, b; short c, d;... int max1 = max(a, b); // max (int, int); short max2 = max(c, d); // max (short, short); int max3 = max(int(c),int(d)); // max (int, int);

Kontenery: Biblioteka szablonów Standard Template Library Vectors vector<int> V; V.insert(V.begin(), 3); assert(v.size() == 1 && V.capacity() >= 1 && V[0] == 3); Lists, Double-Ended Queues Maps, Multimaps Sets, Multisets Algorytmy binary_search, sort, for_each Iteratory list<int> L; L.push_front(3); insert_iterator<list<int>> ii(l,l.begin()); *ii++ = 0; *ii++ = 1; *ii++ = 2; copy(l.begin(), L.end(), ostream_iterator<int>(cout, " "));