FUNKCJE WZORCOWE. Wykład 10. Programowanie Obiektowe (język C++) Funkcje wzorcowe wprowadzenie (2) Funkcje wzorcowe wprowadzenie (1)



Podobne dokumenty
Szablony funkcji i szablony klas

referencje Wykład 2. Programowanie (język C++) Referencje (1) int Num = 50; zdefiniowano zmienną Num (typu int) nadając jej wartość początkową 50.

Przekazywanie argumentów wskaźniki

Wstęp do Programowania 2

Szablony klas, zastosowanie szablonów w programach

Wprowadzenie do szablonów szablony funkcji

Wprowadzenie do szablonów szablony funkcji

Abstrakcyjny typ danych

Stos liczb całkowitych

Wstęp do programowania

Szablony funkcji i klas (templates)

Wstęp do Programowania 2

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

Wykład 1. Program przedmiotu. Programowanie Obiektowe (język C++) Literatura. Program przedmiotu c.d.:

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

Szablony. Szablony funkcji

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

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Programowanie - wykład 4

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

Programowanie i struktury danych

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

TEMAT : KLASY DZIEDZICZENIE

Wzorce funkcji (szablony)

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

Język C++ wykład VIII

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

Szablon klasy std::vector

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

Do czego służą klasy?

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

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

Programowanie obiektowe w języku C++ dr inż. Jarosław Forenc

Co nie powinno być umieszczane w plikach nagłówkowych:

Informatyka 2. Wykład nr 3 ( ) Politechnika Białostocka. - Wydział Elektryczny. dr inŝ. Jarosław Forenc

Globalne / Lokalne. Wykład 15. Podstawy programowania (język C) Zmienne globalne / lokalne (1) Zmienne globalne / lokalne (2)

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

Program 6. Program wykorzystujący strukturę osoba o polach: imię, nazwisko, wiek. W programie wykorzystane są dwie funkcje:

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

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

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

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

Wstęp do programowania

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

Techniki Programowania wskaźniki

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

Programowanie w języku C++

Programowanie obiektowe w C++ Wykład 12

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

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

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

Pola i metody statyczne

XVII. Funkcje w C Ogólna budowa funkcji Definicja funkcji Co waŝnego powinniśmy wiedzieć o funkcjach

Wprowadzenie do szablonów klas

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

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

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

Programowanie Obiektowo Zorientowane w języku C++ Klasy, pola, metody

Zaawansowane programowanie w C++ (PCP)

Operacje wejścia/wyjścia odsłona pierwsza

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Paradygmaty programowania. Paradygmaty programowania

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

Program 14. #include <iostream> #include <ctime> using namespace std;

Modelowanie numeryczne w fizyce atmosfery Ćwiczenia 3

Podstawy Programowania Obiektowego

Wykład 1. Program przedmiotu. Programowanie (język C++) Literatura. Program przedmiotu c.d.:

XII. Warunek wielokrotnego wyboru switch... case

#include <iostream> using namespace std; void ela(int); int main( ); { Funkcja 3. return 0; }

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Klasa, metody, rozwijanie w linii

ATD. Wykład 8. Programowanie (język C++) abstrakcyjny typ danych. Abstrakcyjne typy danych (ATD) Metody czysto wirtualne. Definicje i uwagi:

funkcje rekurencyjne Wykład 12. Podstawy programowania (język C) Funkcje rekurencyjne (1) Funkcje rekurencyjne (2)

Klasa, metody, rozwijanie w linii

KLASY cz.1. Dorota Pylak

Programowanie w języku C++

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

Język C++ wykład VII. uzupełnienie notatek: dr Jerzy Białkowski. Programowanie C/C++ Język C++ wykład VII. dr Jarosław Mederski. Spis.

Do czego służą klasy?

Programowanie obiektowe

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

Podstawy programowania w języku C++

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

Wykład 8: klasy cz. 4

Podstawy algorytmiki i programowania - wykład 4 C-struktury

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

Programowanie w C++ - wybrane przykłady szablonów Opracowanie: dr hab. Mirosław R. Dudek, prof. UZ

PODEJŚCIE OBIEKTOWE. Przykład 1 metody i atrybuty statyczne

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

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

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 7 maja K.Grzelak (Wykład 8) Programowanie w C++ 1 / 31

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

C-struktury wykład. Dorota Pylak

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

Programowanie obiektowe i C++ dla matematyków

Programowanie obiektowe, wykład nr 7. Przegląd typów strukturalnych - klasy i obiekty - c.d.

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

Języki programowania obiektowego Nieobiektowe elementy języka C++

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

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

Transkrypt:

Programowanie Obiektowe (język C++) Wykład 10. FUNKCJE WZORCOWE Funkcje wzorcowe wprowadzenie (1) Funkcje wzorcowe wprowadzenie (2) int max ( int a, int b ) return a>b? a : b; Aby mieć analogiczną funkcję działającą na danych typu double w jęz. C musimy wprowadzić dla niej odrębny identyfikator: double d_max ( double a, double b ) return a>b? a : b; W jęz. C++ mamy moŝliwość przeciąŝania identyfikatorów funkcji, więc moŝemy uŝyć tej samej nazwy: double max ( double a, double b ) return a>b? a : b; W obu językach C i C++ moŝemy się posłuŝyć w tej sytuacji, jako pewnego rodzaju alternatywą, tzw. makrodefinicją: #define MAX(a,b) (((a)>(b))? (a) : (b)) co daje nam moŝliwość posługiwania się wyraŝeniami przypominającymi wywołania 2-argumentowej funkcji o tej samej nazwie dla argumentów róŝnych typów: int i, j, k; double A, B, C;. k = MAX(2*i+5, j+i); C = MAX(3.4+A, 7*B); Ale wiąŝe się to z szeregiem niedogodności i pułapek. -1- -2- -3- -4-

Funkcje wzorcowe (1) W języku C++ moŝemy się posłuŝyć konstrukcją wzorca (szablonu) funkcji: template < class TYPE > TYPE max (TYPE a, TYPE b ) return a>b? a : b; gdzie TYPE moŝe być typem wbudowanym lub definiowanym w programie. <class TYPE> nazywa się tu opisem parametru wzorca. Mając tak zdefiniowany szablon, moŝe go wykorzystać do konkretyzacji funkcji przyjmujących argumenty potrzebnych typów, n.p.: int main ( ) int i, j, k; double A, B, C; k = max( i, j + 5 ); // skonkretyzuje int max ( int, int ); C = max( A, B + 0.5 ); // skonkretyzuje double max ( double, double ); A = max ( B, 10 ); // BŁĄD! konieczna ścisła zgodność typów Funkcje wzorcowe (2) 1. Wzorzec funkcji moŝe mieć więcej parametrów (ale nie mniej niŝ jeden!). 2. KaŜdy opis parametru wzorca składa się ze słowa kluczowego class ( lub typename ) oraz wybranego identyfikatora ( nazwy parametru ). 3. Na liście parametrów wzorca kaŝdy identyfikator moŝe wystąpić tylko raz. 4. Parametr wzorca staje się specyfikatorem typu, którego moŝna uŝywać w pozostałej części definicji funkcji wzorcowej ( n.p. w deklaracjach zmiennych lokalnych, operacjach rzutowania e.t.c. ) 5. KaŜdy parametr wzorca musi wystąpić co najmniej jeden raz w sygnaturze funkcji wzorcowej. Wychodząc od definicji funkcji: Funkcje wzorcowe (3) double Summa ( double tab[ ], int size ) double sum = 0; for ( int i = 0; i < size; i++ ) sum += tab[ i ]; return sum; MoŜemy łatwo utworzyć jednoparametrowy wzorzec: template < class T > T Summa ( T tab[ ], int size ) T sum = 0; for ( int i = 0; i < size; i++ ) sum += tab[ i ]; return sum; Funkcje wzorcowe (4) Wychodząc od definicji tej samej funkcji: double Summa ( double tab[ ], int size ) double sum = 0; for ( int i = 0; i < size; i++ ) sum += tab[ i ]; return sum; MoŜemy równie łatwo utworzyć wzorzec dwuparametrowy: template < class T, class S > T Summa ( T tab[ ], S size ) T sum = 0; for ( S i = 0; i < size; i++ ) sum += tab[ i ]; return sum; -5- -6- -7- -8-

Funkcje wzorcowe (5) RozróŜnianie przeciąŝonych funkcji wzorca i innych funkcji o tej samej nazwie jest realizowane wg schematu: 1. JeŜeli istnieje funkcja o deskryptorze dokładnie pasującym do wywołania, to ją wywołaj. 2. JeŜeli istnieje wzorzec pozwalający zrealizować (skonkretyzować) funkcję o dokładnie pasującym deskryptorze, to uŝyj tego wzorca. 3. JeŜeli istnieje funkcja przeciąŝona, którą moŝna dopasować wg zwykłych reguł (tzn. z zastosowaniem konwersji), to jej uŝyj. 4. JeŜeli Ŝadnego z punktów 1., 2., 3. nie dało się zastosować, to wywołanie zostanie uznane za błędne. Rozpatrzmy przykład: template < class T > T max ( T a, T b ) return a>b? a : b; void test ( ) int a, b; char c, d; Funkcje wzorcowe (6) //int A = max( a, c ); // BŁĄD! nie moŝna wygenerować int max(int,char); int B = max( a, b ); // int max(int, int); - niejawne utworzenie egzemplarza char C = max( c, d ); // char max(char,char); niejawne utworzenie egz. int D = max( c, d ); // char max(char, char); typ zwracanej wartości nie // nie naleŝy do deskryptora Funkcje wzorcowe (7) Funkcje wzorcowe (8) Ale: template < class T > T max ( T a, T b ) return a>b? a : b; int max ( int, int ); void test ( ) int a, b; char c, d; // deklaracja funkcji (być moŝe zewnętrznej) int A = max( a, c ); // O.K.! uŝyte będzie int max(int,int); // zgodnie z regułą 3. Inne moŝliwości: template < class T > T max ( T a, T b ) return a>b? a : b; template int max ( int, int ); template< > char max ( char a, char b ) return a>b? a : 0; // wymuszone (jawne) utworzenie egzemplarza // (tylko w zasięgu definicji szablonu) // dostarczenie szczególnej definicji egzemplarza! // (tylko w zasięgu definicji szablonu) -9- -10- -11- -12-

Klasy wzorcowe - wprowadzenie (1) W język C++ moŝemy się posłuŝyć równieŝ konstrukcją wzorca (szablonu) klasy. Podobnie jak w przypadku wzorców funkcji najprościej jest przyjąć za punkt wyjścia jakąś konkretną klasę (dobrze wcześniej sprawdzoną w praktyce). #define CSTACKSIZE 100 WZORCE KLAS ( SZABLONY KLAS ) class CharStack char tab [ CSTACKSIZE ]; int size, top; CharStack ( ) size = CSTACKSIZE; top = 0; void Push ( char e ) tab [ top++ ] = e; char Pop ( ) return tab [ --top ]; char Top ( ) return tab [ top - 1 ]; int Size ( ) const return size; int Used ( ) const return top; int Place ( ) const return size - top; void Display ( ) const cout << endl; for ( int i = 0; i < top; ++i ) cout << tab [ i ] << " "; Przy okazji (1) ZauwaŜmy, Ŝe dotychczas w definicji klasy podawaliśmy zwykle jedynie deklaracje metod. Ich definicje umieszczaliśmy na zewnątrz, najczęściej w tzw. pliku implementacyjnym. Tym razem definicje metod zostały podane od razu w definicji klasy. Jaka róŝnica? 1. Metoda definiowana w ciele definicji klasy otrzymuje domyślnie atrybut inline. Metody (i zwykłe funcje) z takim atrybutem nie mają jednokrotnie wygenerowanego kodu o określonym adresie, który jest uruchamiany przy kaŝdym odwołaniu do metody (funkcji). Zamiast tego kompilator moŝe (ale nie musi!) generować kod metody (funkcji) w kaŝdym miejscu jej wywołania. MoŜe to dać zysk na czasie wykonania programu, chociaŝ zwykle zwiększa jego objętość. UWAGA: Funkcja z atrybutem inline nazywa się teŝ funkcją rozwijalną albo funkcją otwartą Przy okazji (2) 2. Atrybut inline moŝe być podany jawnie a treść metody na zewnątrz definicji klasy, ale wtedy naleŝy ją podać w pliku header'owym (.h) klasy, a nie w pliku implementacyjnym. Wynika to z faktu, Ŝe treść takiej metody potrzebna jest kompilatorowi w kaŝdym miejscu jej wywołania. 3. UŜycie odrębnego (niezaleŝnie kompilowanego) pliku zawierającego definicje metod ( posiadających atrybut extern ) pozwala ukryć szczegóły implementacyjne. UŜytkownik naszej klasy będzie korzystał tylko z pliku nagłówkowego w postaci źródłowej (.h) i skompilowanego pliku zawierającego kod metod (.obj). A więc np. wcale nie musi wiedzieć, jaki algorytm zastosowaliśmy do realizacji konkretnych obliczeń numerycznych. -13- -14- -15- -16-

Przy okazji (3) // charstack.h class CharStack void Push ( char e ) tab [ top++ ] = e; inline char Pop ( ); char Top ( ) const; inline char CharStack :: Pop ( ) return tab [ --top ];..... end of charstack.h // charstack.cpp char CharStack :: Top ( ) const return tab [ top - 1 ]; W powyŝszym przykładzie metody: Push i Pop mają atybut inline ( Push domyślnie, Pop jawnie ) metoda Top ma atrybut extern. Klasy wzorcowe - wprowadzenie (2) Poszukajmy 'kandydatów' na parametry. Zaznaczyłem je na kolorowo. Oczywiście nie wszystkie moŝliwości musimy wykorzystać. #define CSTACKSIZE 100 class CharStack char tab [ CSTACKSIZE ]; int size, top; CharStack ( ) size = CSTACKSIZE; top = 0; void Push ( char e ) tab [ top++ ] = e; char Pop ( ) return tab [ --top ]; char Top ( ) return tab [ top - 1 ]; int Size ( ) const return size; int Used ( ) const return top; int Place ( ) const return size top; void Display ( ) const cout << endl; for ( int i = 0; i < top; ++i ) cout << tab [ i ] << " "; A tak moŝe wyglądać nasz szablon: #define STACKSIZE 100 Klasy wzorcowe (1) T tab [ S ]; int size, top; Stack ( ) size = S; top = 0; void Push ( T e ) tab [ top++ ] = e; T Pop ( ) return tab [ --top ]; T Top ( ) return tab [ top - 1 ]; int Size ( ) const return size; int Used ( ) const return top; int Place ( ) const return size - top; void Display ( ) const cout << endl; for ( int i = 0; i < top; ++i ) cout << tab [ i ] << " "; #undef STACKSIZE I funkcja main: Klasy wzorcowe (2) int main ( ) const int k = 10; Stack<> S0; Stack<int> S1, S2; Stack<int,10> S3; Stack<double> S4; Stack<double,55> S5; Stack<double,k+5> S6; Stack<double,k+45> S7; cout << endl << S0.Size() << endl << S1.Size() << endl << S3.Size(); cout << endl << S4.Size() << endl << S5.Size() << endl << S6.Size(); S1.Push(10); S1.Push(11); S1.Push(12); S1.Push(13); S1.Push(14); S1.Display(); S2 = S1; S1.Pop(); S1.Pop(); S1.Display(); S2.Display(); //S3 = S1; // BŁĄD! //S4 = S1; // BŁĄD! ale S7 = S5; O.K. while ( S1.Place() && S2.Used() ) S1.Push( S2.Pop() ); S1.Display(); -17- -18- -19- -20-

Klasy wzorcowe (3) Program wyświetli (po zakomentowaniu wierszy z błędami): 100 100 10 100 55 15 10 11 12 13 14 10 11 12 10 11 12 13 14 10 11 12 14 13 12 11 10 Klasy wzorcowe (4) CharStack St; // obiekt St jest typu CharStack Stack<> S0; // obiekt S0 jest typu Stack<char, 100> Stack<int> S1, S2; // obiekty S1 i S2 są typu Stack<int, 100> Stack<int,10> S3; // obiekt S3 jest typu Stack<int, 10> Stack<double> S4; // obiekt S4 jest typu Stack<double, 100> Stack<double,k+5> S6; // obiekt S6 jest typu Stack<double, 15> Stack<CMPLX> SC; // obiekt SC jest typu Stack<CMPLX, 100> Kusząca (niebezpieczna) alternatywa: Klasy wzorcowe (1a) T tab [ S ]; T *p; int size; Stack ( ) p = tab; size = S; void Push ( T e ) *p++ = e; T Pop ( ) return *--p; T Top ( ) return *(p 1); int Size ( ) const return size; int Used ( ) const return p tab; int Place ( ) const return size (p tab); void Display ( ) const T *r = tab; while ( r < p ) cout << *r++ << " "; Na czym polega niebezpieczeństwo i jak mu zaradzić? Klasy wzorcowe (5) 1. Wzorzec klasy moŝe mieć więcej parametrów (ale nie mniej niŝ jeden!). 2. Opis parametru wzorca składa się ze słowa kluczowego class (ew. typename) lub nazwy typu wbudowanego oraz wybranego identyfikatora (nazwy parametru). Dla parametrów moŝna określać wartości domyślne. 3. Na liście parametrów wzorca kaŝdy parametr moŝe wystąpić tylko raz. 4. Parametr wzorca poprzedzony słowem kluczowym class (ew. typename) staje się specyfikatorem typu, którego moŝna uŝywać w pozostałej części definicji klasy wzorcowej (n.p. w deklaracjach pól, specyfikacjach parametrów metod et c.). 5. Parametr wzorca poprzedzony nazwą typu wbudowanego staje się stałą, której moŝna uŝywać np. do zapisu rozmiarów tablic, inicjowania wartości zmiennych et c. -21- -22- -23- -24-

Klasy wzorcowe (6) Zobaczmy inny wariant zapisu szablonu. // stack.h #include <iostream> using namespace std; #define STACKSIZE 1000 void Display ( ) const; Klasy wzorcowe (7) W dalszym ciągu pliku stack.h podajemy szablony metod: Stack< T, S > :: Stack ( ) p = q = tab; size = S; Stack< T, S >& Stack< T, S > :: operator= ( const Stack& rhs ) p = q; for ( T* r = rhs.q; r < rhs.p; *p++ = *r++ ); return *this; void Stack< T, S > :: Push ( T e ) *p++ = e; T Stack< T,S > :: Pop ( ) return *--p; T Stack< T, S > :: Top ( ) const return *(p - 1); int Stack< T, S > :: Size ( ) const return size; i dalej w pliku stack.h: Klasy wzorcowe (8) int Stack< T, S > :: Used ( ) const return p - q; int Stack< T, S > :: Place ( ) const return size - (p - q); void Stack< T, S > :: Display ( ) const T* r = q; while ( r < p ) cout << *r++ << " "; funkcje zaprzyjaźnione we wzorcach klas #undef STACKSIZE UWAGA! UWAGA! To wszystko powinno być podane w pliku stack.h. Dla klas wzorcowych nie tworzymy odrębnych plików implementacyjnych *.cpp. -25- -26- -27- -28-

Klasy wzorcowe (6) W rozpatrywanym wcześniej wzorcu klasy void Display ( ) const; chcemy zastąpić metodę Display zaprzyjaźnionym operatorem <<. Wzorce i friend (1) Próba zrealizowania tego zadania w następujący sposób: friend ostream& operator << (ostream&, const Stack<T,S>&); template < class T, int S> ostream& operator << (ostream& out, const Stack<T,S>& st) T* r = st.q; while ( r < st.p ) cout << *r++ << " "; return out; nie da oczekiwanego efektu. Operator << nie zostanie wygenerowany. Poprawny efekt otrzymamy pisząc: Wzorce i friend (2) template < class Q, int R> friend ostream& operator << (ostream&, const Stack<Q,R>&); template < class T, int S> ostream& operator << (ostream& out, const Stack<T,S>& st) T* r = st.q; while ( r < st.p ) out << *r++ << " "; return out; a nawet: Wzorce i friend (3) template < class Q, int R> friend ostream& operator << (ostream&, const Stack&); template < class T, int S> ostream& operator << (ostream& out, const Stack<T,S>& st) T* r = st.q; while ( r < st.p ) out << *r++ << " "; return out; -29- -30- -31- -32-

Jeszcze innym rozwiązaniem jest: Wzorce i friend (4) friend ostream& operator << (ostream& out, const Stack& st) T* r = st.q; while ( r < st.p ) out << *r++ << " "; return out; Koniec wykładu 10. -33- -34-