C++ i wzorce w klasach Andrzej Przybyszewski numer albumu: 89810 14 listopada 2009
Ogólnie Przeładowanie (przeciążanie) operatorów polega na nadaniu im nowych funkcji. Przeładowanie operatora dokonuje się definiując swoją własną funkcję która:
Ogólnie Przeładowanie (przeciążanie) operatorów polega na nadaniu im nowych funkcji. Przeładowanie operatora dokonuje się definiując swoją własną funkcję która: nazywa się operator@ gdzie znaczek @ to symbol operatora, o którego przeładowanie nam chodzi (np. +,-,* itp.)
Ogólnie Przeładowanie (przeciążanie) operatorów polega na nadaniu im nowych funkcji. Przeładowanie operatora dokonuje się definiując swoją własną funkcję która: nazywa się operator@ gdzie znaczek @ to symbol operatora, o którego przeładowanie nam chodzi (np. +,-,* itp.) jako co najmniej jeden z argumentów, przyjmuje obiekt danej klasy lub referencję do takiego obiektu.
Ogólnie Przeładowanie (przeciążanie) operatorów polega na nadaniu im nowych funkcji. Przeładowanie operatora dokonuje się definiując swoją własną funkcję która: nazywa się operator@ gdzie znaczek @ to symbol operatora, o którego przeładowanie nam chodzi (np. +,-,* itp.) jako co najmniej jeden z argumentów, przyjmuje obiekt danej klasy lub referencję do takiego obiektu. Nie możemy przeładowywać operatorów dla typów wbudowanych (int, char, float).
Konstrukcja Konstrukcja: typ_zwracany operator@ (argumenty) { // operacje }
Operatory Operatory które można przeładować: + - * / % & ^ << << ~ &&! ==!= < <= > >= += -= *= /= %= &= = ^= <<= >>= ++ -- = -> ->* () [] new delete, Trzeba zapamiętać że można przeciążać TYLKO te operatory. Próba przeciążania innych (jak i tworzenia nowych jak np @) skończy się błędem.
Operatory Operatory które można przeładować: + - * / % & ^ << << ~ &&! ==!= < <= > >= += -= *= /= %= &= = ^= <<= >>= ++ -- = -> ->* () [] new delete, Trzeba zapamiętać że można przeciążać TYLKO te operatory. Próba przeciążania innych (jak i tworzenia nowych jak np @) skończy się błędem. NIE można zmieniać priorytetu wykonywania tych operatorów! np. a + b + c zostanie wykonane zgodnie z matematycznym punktem widzenia (a + b) + c, za to a + b * c jako a + (b * c) w tym wypadku.
Przykład - liczby zespolone 1 Przykład: class LZESPOLONA{ public: LZESPOLONA(); LZESPOLONA(float, float); LZESPOLONA operator+(lzespolona); LZESPOLONA operator-(lzespolona); LZESPOLONA operator*(lzespolona); LZESPOLONA operator+=(lzespolona); LZESPOLONA operator-=(lzespolona); LZESPOLONA operator*=(lzespolona); private: float a; float b; };
Przykład - liczby zespolone 2 Mamy daną klasę definiującą liczbę zespoloną, gdzie a to część rzeczywista a b to część urojona. Jak widać mamy podane w sekcji public funkcje przeładowujące operatory arytmetyczne. Przykład: //konstruktory LZESPOLONA::LZESPOLONA(float a1, float b1) {a = a1; b = b1;} LZESPOLONA::LZESPOLONA() {a = 0; b = 0;} Chcemy teraz wykonywać działania na tych liczbach bez wielokrotnego dobierania się do składowych a i b i wykonywania nieco żmudnego podstawiania do wzorów.
Przykład - liczby zespolone 3 Przykład: LZESPOLONA LZESPOLONA::operator+( LZESPOLONA B) { return LZESPOLONA(a+B.a, b+b.b); } LZESPOLONA LZESPOLONA::operator-( LZESPOLONA B) {return LZESPOLONA(a-B.a, b-b.b); } W prosty sposób przeładowaliśmy operatory + i - dzięki czemu można wykonać dodawanie i odejmowanie liczb zespolonych. Przedstawiony sposób odnosi się do funcji operatorowych jako niestatycznych składowych klasy, przyjmuje ona wtedy tylko jeden parametr dla operatorów dwuargumentowych.
Przykład - liczby zespolone 4 Możemy także mieć funkcje operatorowe jako funkcje nie-składowe(globalne) wtedy potrzebujemy dwóch argumentów: Przykład: LZESPOLONA operator+(lzespolona A, LZESPOLONA B) { return LZESPOLONA(A.a+B.a, A.b+B.b); } LZESPOLONA operator*(lzespolona A, LZESPOLONA B) { return LZESPOLONA((A.a*B.a-B.b*A.b),(A.b*B.a+A.a*B.b); } Taką funkcję możemy dołączyć do klasy za pomocą friend, lub nie musi być ona składową klasy, jeżeli potrafi wykonywać operacje na obiektach danej klasy.
Przykład - liczby zespolone 5 Kożystając z operatorów arytmetycznych warto zdefiniować sobie operatory wykonujące operacje+przypisanie(dopisanie) czyli np. +=, -=, *=, działają one tak jak a = a + b, jak widać zamiast przepisywać a do którego chcemy dopisać coś, można użyć a+=b, definiując nową klasę takie operatory trzeba również zdefiniować (np. jeżeli przeładujemy operator+ to nie stworzy nam się domyślnie działający += co mogłoby się czasem wydawać). Przykład: LZESPOLONA LZESPOLONA::operator+=( LZESPOLONA B) {a = a + B.a; b = b + B.b;} LZESPOLONA LZESPOLONA::operator-=( LZESPOLONA B) {a = a - B.a; b = b - B.b;}
Przykład - liczby zespolone 6 Jak wcześniej pokazano przeładować możemy wiele operatorów (niektórych nie można ale są one raczej mało istotne), każdemu operatorowi możemy nadać nowe właściwości, niekoniecznie musi on robić coś pochodnego od macierzystego przeznaczenia (+ niekoniecznie musi coś dodawać, wszystko zależy od nas). Mały przykład: int main(){ LZESPOLONA a(3,2), b(8, 12.3),c; c = a + b; c = a - b; c += a; //itp... }
Ogólnie Wzorce są jednym z najsilniejszych narzędzi. Zgodnie ze swoim przeznaczeniem, powinny służyć do tego, aby maksymalnie skrócić konieczność pisania i powtarzania tych samych sekwencji. Wzorce ściśle się trzymają konwencji języka, czego konsekwencją jest to, że mamy do dyspozycji dwa podstawowe rodzaje wzorców: wzorzec funkcji i wzorzec klasy. Definicja wzorca: template < parametry-wzorca > definicja-wzorca
Wzorce we wzorcach Parametry wzorca są wyjątkowo w nawiasach ostrych. Parametry te przypominają właściwie argumenty funkcji, z tym, że na liście parametrów może wystąpić parametr typu, który zazwyczaj nazywa się class. Oczywiście parametr wzorca może być również np. typu size_t, ale class jest to niewątpliwie wzorców najmocniejsza strona. Jeśli chodzi o kwestie składniowe to: jeśli jednym z parametrów wzorca jest jakiś inny konkretyzowany wzorzec, to NIGDY nie wolno pisać: Definicja wzorca: wz1<costam,wz2<int>> Należy pisać pamiętając żeby rozdzielać ostre nawiasy: > >.
Wzorce funkcji i klas Wzorce(szablony) można zastosować do funckji jak i klas(struktur). Wzorzec funkcji: template <class T> inline const T& Min( const T& t1, const T& t2 ) { if ( t1 < t2 ) return t1; return t2; } Wzorzec funkcji inline Min która zwraca element mniejszy. Powinniśmy dostać z tego wzorca: Wzorzec funkcji: inline const int& Min( const int&, const int& );
Wzorce klas 1 Zajmijmy się szablonami klas. Wzorzec klasy: template <class parametr> class figura {parametr wysokosc; parametr krawedz1; parametr krawedz2;}; Do szablonu podajemy klasę jakąś, potem możemy ją wykorzystać w nowej klasie tworzonej w szablonie. Tak możemy stworzyć nową klasę figura której parametry są typu int. nowy obiekt: figura<int> trapez;
Wzorce klas 2 Rozbuduwując naszą klasę we wzrocu: Wzorzec klasy: template <class parametr> class figura { private: parametr wysokosc;... //inne parametry public: parametr wez_wysokosc ();... // reszta funkcji zwracających void wpisz_wysokosc (parametr);... //reszta funkcji wypisujących}; Mamy tu kilka funkcji, trzeba je teraz oprogramować.
Wzorce klas 3 Zaimplementujmy teraz funkcje w szablonie klasy: Funkcje szablonu klasy: template<class parametr> parametr figura<parametr>::wez_wysokosc () { return wysokosc; }... // reszta funkcji typu parametr template<class parametr> void figura<parametr>::wpisz_wysokosc (parametr wys) { wysokosc = wys; }... //reszta funkcji
Wzorce klas 4 Definicja funkcji składowej szablonu klasy trochę przypomina definicję szablonu funkcji. Zaczynamy od słówka template i nawiasów trójkątnych zawierających parametry szablonu. Następnie podajemy rezultat zwracany przez definiowaną funkcję. Dalej jest nazwa klasy wraz z nawiasami trójkątnymi. W nich ponownie umieszczamy wszystkie parametry szablonu. Kolejny jest operator zakresu i wreszcie nazwa funkcji. Zauważmy jednak, że po nazwie funkcji podajemy jedynie nazwę parametru! Niema tam już słówka class.
Klasy specjalizowane Specjalizowana wersja szablonu klas, jest to specjalna definicja klasy która zostanie dopasowana przez szablon gdy nastąpi odpowiednie wywołanie. Czyli np. dla typu int* możemy napisać osobną klasę która zostanie utworzona poprzez szablon: Funkcje szablonu klasy: class figura<int *> { //składniki szablonu klas }; //funkcja składowa void figura<int*>::wpisz_wysokosc (int * wys) { wysokosc = *wys; }
Nowy obiekt Jak wcześniej wspomniano, mając już cały szablon klasy, możemy na jego podstawie utworzyć nowy obiekt np. klasy utworzonej wcześniej z liczb zespolonych: Funkcje szablonu klasy: figura<lzespolona> kwadrat; kwadrat.wpisz_wysokosc(lzespolona(2, 4)); //oczywiście figura i liczby zespolone //nie za bardzo do siebie pasują
STL W standardzie języka C++ wprowadzono biblioteki standardowych szablonów, są to szablony na podstawie których możemy tworzyć najczęstsze struktury danych, na podstawie dowolnych obiektów. Niektóre elementy STL a: Vector: vector<lzesoplona> zesptab;//tablica dynamiczna List: list<lzespolona> listazesp;//lista dwukierunkowa Stack: stack<zespolona> zespstos;//stos