Abstrakcyjny typ danych Abstrakcyjny Typ Danych (abstract data type-adt): zbiór wartości wraz z powiązanymi z nimi operacjami; operacje są zdefiniowane w sposób niezależny od implementacji; operacje są kompletne oraz wyłączne (nie ma możliwości wykonywania na wartościach typu operacji spoza zestawu); bezpośredni dostęp do składowych takiej struktury nie jest możliwy; Termin abstrakcyjny jest związany z definicją metod niezależną od implementacji
Abstrakcyjny typ danych Jednym z prostszych abstrakcyjnych typów danych jest stos. Operacjami wykonywanymi na stosie mogą być: new:-> S - konstruktor pustego stosu push: T, S -> S - położenie elementu typu T na stos S top: S -> T - odczytanie wierzchołka stosu S popoff: S -> S - zdjęcie elementu ze stosu S (wynikiem jest stos) pop: S -> T - zdjęcie elementu ze stosu S (wynikiem jest element) isempty: S -> boolean - sprawdzenie, czy stos S jest pusty
Abstrakcyjny typ danych Pojęcie abstrakcyjnego typu danych jest bliskie pojęciu klasy w językach programowania obiektowego. Klasa może być postrzegana jako implementacja abstrakcyjnego typu danych. Klasa może przesłaniać złożoną swoją strukturę (podobnie jak ADT) i udostępniać jedynie pewien publiczny interfejs (zestaw operacji)
Klasy abstrakcyjne Klasa abstrakcyjna - klasa, która nie ma instancji; służy do wyprowadzania hierarchii dziedziczenia (służy jako nadklasa, grupująca pewne ogólne własności); nie wprowadza definicji (wszystkich) metod Klasa konkretna - klasa, która może mieć instancje; musi mieć zdefiniowane wszystkie metody Klasa jest klasą abstrakcyjną, jeżeli zawiera niezdefiniowaną funkcję wirtualną(pure virtual function) lub dziedziczy taką funkcję i nie dostarcza jej implementacji. Niezdefiniowane funkcje wirtualne są deklarowane następująco: virtual typ nazwa([parametry]) = 0; Termin abstrakcyjny jest tu związany z faktem, że zabrania się tworzenia instancji takiej klasy, a sama klasa służy jedynie do określenia pewnego wspólnego interfejsu dla zbioru jej podklas Klasa
Klasy abstrakcyjne - przykład class Instrument { public: virtual char* Identity( ) { return name; } virtual void play() = 0; // funkcja abstrakcyjna protected: char *name; }; Konkretni potomkowie klasy Instrument powinni dostarczyć implementacji wszystkich metod abstrakcyjnych
Klasy abstrakcyjne Klasy abstrakcyjne nie mogą być używane jako: zmienne lub składowe klasy typy parametrów funkcji typy wynikowe funkcji przy jawnej konwersji Jeżeli konstruktor klasy abstrakcyjnej wywołuje funkcję abstrakcyjną (bezpośrednio lub pośrednio) wynik wywołania jest nieokreślony (w VC++ błąd linkera); Funkcje abstrakcyjne mogą być wywoływane w innych metodach, niż konstruktor Konstruktory i destruktory mogą wywoływać zwykłe funkcje (nie wirtualne)
Abstrakcyjne destruktory class base { public: base() {} virtual ~base() = 0; }; base::~base() { cout<< "Base destructor\n"; } class derived : public base { public: derived() {} ~derived(){} }; // dla wirtualnych destruktorów i tak musimy //dostarczyć implementację Pusta implementacja funkcji wirtualnej zapewnia, że istnieje co najmniej 1 implementacja tej funkcji.
Szablony Szablony są mechanizmem ponownego wykorzystania kodu (reuse) W przypadku funkcji ponownie wykorzystany jest algorytm W przypadku klas ponownie wykorzystane są wszystkie składowe Deklaracja szablonu specyfikuje zbiór parametryzowanych klas lub funkcji Deklaracja szablonu ma postać: template < [typelist] [, [arglist]] >declaration Lista parametrów szablonu jest oddzieloną przecinkami listą typów (identyfikatorów klas lub nazw typów) oraz ewentualnie wartości, które mają być wykorzystane w treści szablonu Przykład szablonu funkcji: template <classt> T min( T a, T b ){ return ( a < b )? a : b; }
Szablony Użycie szablonu wymaga jego konkretyzacji (ustalenia wartości dla wszystkich generycznych parametrów) Konkretyzacja może odbywać się: Niejawnie (na podstawie typów parametrów aktualnych) Jawnie (przez podanie typów parametrów aktualnych) Niejawna konkretyzacja funkcji ma miejsce w czasie jej wywołania; wartości parametrów generycznych są ustalane na podstawie parametrów aktualnych
Szablony Przykład niejawnej konkretyzacji szablonu funkcji min: int a = 5, b = 10; double a = 5.0, b = 10.0; cout<< min(a,b); cout << min(a,b); Jawna konkretyzacja wymaga, aby, a po nazwie funkcji, w nawiasach <>, zdefiniować wartości aktualne dla parametrów generycznych Przykład jawnej konkretyzacji szablonu funkcji: char x= a ; int y = 66; cout << min<char>(x,y); // wymagana konwersja z int do char
Szablony Szablony można specjalizować, zmieniając ich działanie w zależności od typu parametrów, na którym działają Specjalizacja szablonu template<classt>void Demo( T a ) {} // definicja funkcji szablonowej template<> void Demo[<typ>](typ a) {} // specjalizacja szablonu dla //konkretnego typu Przykłady różnych specjalizacji szablonów template<classt>voidf(t t){} template<>voidf<char>(chart){} template<>voidf(doublet){} // specjalizacja funkcji f dla typu char // specjalizacja funkcji f dla typu double
Szablony Parametrem szablonu może być argument, który nie jest typem Argumenty, które nie opisują typów (non-type) podlegają następującym ograniczeniom: Typ argumentu jest typem całkowitym, wyliczeniowym, referencją lub wskaźnikiem W kodzie szablonu nie można modyfikować wartości argumentu, ani pobierać jego adresu Przy konkretyzacji szablonu wartość parametru musi być stałą Przykład szablonu funkcji z parametrem, który nie jest typem template<classt,int zakres>int szukaj(t tablica[], T co){} const int ile =sizeof(tab1)/sizeof(int); cout<< szukaj<int, ile>(tab1,2) <<endl;
Szablony funkcji Zastosowanie szablonów funkcji dla własnych typów (klas) może wymagać przeciążenia odpowiednich operatorów: template <classt> T min(t a, T b){ return( a < b )? a : b; } classx { public: X(int a=0):a(a){} int operator<(x& b); int a; }; X a,b,c; c = min(a,b);
Szablony Kolejność dopasowywania wywołań funkcji szablonowych przez kompilator (mechanizm rozstrzygania przeciążeń): Tworzona jest lista funkcji kandydujących: o tej samej nazwie Spośród funkcji kandydujących wybierane są te, które mają odpowiednią listę parametrów istnieje niejawna sekwencja konwersji, zawierająca przypadek dokładnego dopasowania każdego typu parametru aktualnego do typu odpowiedniego parametru formalnego Spośród różnych funkcji, wybierana jest taka, której argumenty są najlepiej dopasowane (zwykłe funkcje mają pierwszeństwo przed wzorcami, konwersja przez promocję,np. char lub short w int, float w double, konwersja standardowa, np. int w char, long w double, konwersja zdefiniowana przez użytkownika) Jeżeli uda się znaleźć szablon - generowana jest odpowiednia wersja funkcji; jeżeli szablonu nie zgłaszany jest błąd
Szablony klas Szablon klasy służy do tworzenia rodziny klas, które operują na pewnym typie (typach) - parametrach szablonu Notacja UML template<class T1, int i> class TempClass {} template<typedef T1, int i > class TempClass {}
Szablony klas - przykład template <class T,int i> // szablon klasy ma 2 parametry class TempClass // 1-szy typ T, 2-gi wartość typu int { // Wartością aktualną T może być dowolny typ public: // Wartością aktualną i może być stały int TempClass(void); ~TempClass(void); int MemberSet( T a, int b ); private: T Tarray[i]; int arraysize; };
Szablony klas Definicja funkcji klasy szablonowej: template<deklaracja_parametrów_szablonu> typ_wyniku nazwa_klasy<parametry_szablonu>::nazwa_składowej ([parametry]) {... } template<classt,inti> int TempClass<T, i>::memberset(t a, int b){ if (b>=0 && b<arraysize) return (Tarray[b]==a); else return-1; }
Szablony klas Konkretyzacja klasy szablonowej ma postać (konkretyzacja niejawna): klasa_szablonowa<parametry_aktualne> obiekt_klasy_konkretnej; TempClass<int, 10> klasa1; // parametry aktualne szablonu: int, 10 TempClass<char, 5> klasa2; // parametry aktualne szablonu: char, 5 Notacja UML
Szablony klas Szablony klas Konkretyzacja klasy szablonowej ma postać (konkretyzacja jawna): template class klasa_szablonowa<parametry_aktualne> obiekt_klasy_konkretnej; template classtempclass<char, 3>; // parametry aktualne szablonu: char, 3 template classklasa<int>; // parametry aktualne szablonu: int Taka deklaracja musi być umieszczona w tej samej przestrzeni nazw, co definicja szablonu; Klasa jest generowana zawsze, nawet, gdy nie tworzymy obiektów tej klasy Zostaną wygenerowane klasy: TempClass<char, 3> Klasa<int>
Szablony klas Szablon klasy może definiować domyślne parametry typu: template <classt2 = typ> deklaracja template <classt=char> class Kwadrat { void rysuj(t znak){} }; Możliwe konkretyzacje takiego szablonu: Kwadrat<int> klasa1(4); klasa1.rysuj(4); Kwadrat<> klasa2(5); klasa2.rysuj('*'); // wykorzystanie typu domyślnego
Szablony klas jako składowe
Szablony klas i dziedziczenie
Szablony klas i dziedziczenie Dziedziczymy Definiujemy Zwykłą klasę Klasą szablonową Szablon klas Zwykłą klasę TAK TAK niemożliwe Szablon klas TAK TAK TAK Klasą szablonową TAK TAK niemożliwe
Szablony klas i dziedziczenie template <class T1> class para { protected: T1 lewy; T1 prawy;.. }; template <class T2> class wspolrzedne : public para<float> { T2 atr1; int atr2; public: wspolrzedne (T2 c, float x=0, float y=0) : para<float>(x,y), atr1(c) {atr2 = 1; }. };
Szablony Szablony są często używane: Do tworzenia klas obsługujących kolekcje (np. stos), które manipulują na danych dowolnych typów Jako klasy bazowe, jak i typy obiektów składowych Zalety szablonów: łatwe do napisania łatwe do zrozumienia type-safe - kompilator wykonuje wszelkie niezbędne sprawdzenia parametrów Szablony znacząco redukują rozmiar kodu źródłowego i zwiększają jego elastyczność nie zmniejszając bezpieczeństwa związanego ze sprawdzaniem typów
Szablon funkcji korzystający z obiektów klasy szablonowej template <class param> class wieza {char *nazwa; param dana; public: wieza(char *na, param d) { nazwa = na; dana = d; } friend ostream &operator << (ostream &s, wieza<param> &ob); }; prosty szablon funkcji template <class param> void prostafun(wieza<param> obiekt) {...} template <class typ> ostream &operator<< (ostream & strum, wieza<typ> ob.) { strum << ob.nazwa <<, dana = << ob.dana << endl; return strum; }
Obiekt klasy szablonowej składnikiem innego szablonu klasy template <class typ> class kubek {...}; template <class t> class trojka { kubek<float> lewy; kubek<t> srodkowy; kubek<t> prawy; }; main() {... trojka<char> ob1; trojka<long> ob2;... }
Zagnieżdżenie definicji klasy w szablonie klas template<class typ1, class typ2> class podstawowa { typ1 skl1;... class wewnetrzna {public: typ1 lewy; typ2 prawy; void pisz() {} }; wewnetrzna pomocnik; wewnetrzna *wskpom; public: podstawowa (typ1 a, typ2 b) { skl1 = a; pomocnik.lewy = a; pomocnik.prawy = b; }... }
Przyjaźń a szablony klas Jeden wspólny przyjaciel template<int stala> class A { int skl(stala); void funpryw( ) {...}; // deklaracja przyjazni friend class zap_kl; friend void zap_fun(); };
Przyjaźń a szablony klas Każdy ma swojego przyjaciela template<class typ> class A { int skl_p; public: A (typ cos) : skl_p (cos) {... } void funpryw( ) {...}; // deklaracja przyjazni z funkcjami (parametryzowany argument) friend void fun_zap(a<typ> obj); // deklaracja przyjazni z klasami szablonowymi friend class B<typ> }; szablon funkcji zaprzyjaznionych template <class typ> void fun_zap (A<typ> obj) {...} szablon klas przyjaciół template<class typ> class B { A<typ> *skl_d; public: B(A<typ> *wsk) : skl_d (wsk) {...} };