Zesp
Transkrypt
Zesp
Tematyka wykładów 1. Wprowadzenie. Klasy – cz. 1 2. Klasy – cz. 2 3. 4. 5. 6. 7. 8. Funkcje operatorowe. Wskaźnik this Dziedziczenie Polimorfizm i funkcje wirtualne Szablony Strumienie Tworzenie aplikacji w systemie Windows - Język C++. Programowanie obiektowe - Klasy i obiekty - Budowa i deklaracja klasy. Prawa dostępu - Pola i funkcje składowe - Konstruktor i destruktor - Tworzenie obiektów - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne Przeciążanie operatorów Przeciążanie (przeładowywanie) operatora pozwala na zdefiniowanie jego działania dla argumentów typu obiektowego (tzn. nadanie operatorowi nowego znaczenia). Cechy przeciążania operatorów: • standardowe działanie operatora dla danych typów prostych pozostaje bez zmian • nie jest możliwa zmiana liczby argumentów operatora • nie jest możliwa zmiana priorytetu operatora • nie jest możliwa zmiana wiązania operatora • nie jest możliwe tworzenie nowych operatorów – przeciąża się istniejące. Działanie operatora można zdefiniować w dowolny sposób (formalnie dopuszczalne jest przeciążenie operatora + tak, aby np. odejmował obiekty, ale nie należy tak postępować). Funkcje operatorowe Przeciążenie operatora polega na zdefiniowaniu funkcji operatorowej. Funkcja operatorowa to funkcja o nazwie operator <symbol_operatora> Przykłady: operator+ operator<< operator() operator new Funkcje operatorowe mogą być definiowane jako: • funkcje składowe klasy • funkcje zaprzyjaźnione z klasą Przeciążanie operatorów Nie jest możliwe przeciążanie operatorów: . .* ?: :: sizeof Następujące operatory należy przeciążać jako funkcje składowe: = () [] -> domyślny operator = („pole po polu”) W przypadku użycia strumieni WE / WY, następujące operatory należy przeciążać jako funkcje zaprzyjaźnione: << >> Funkcje operatorowe class Zesp { public: Zesp(); Zesp(float a, float b); private: float re, im; }; Zesp::Zesp() { re = im = 0; } Zesp::Zesp(float a, float b) { re = a; im = b; } Problem: klasę Zesp należy uzupełnić o operacje: • dodawania liczb zespolonych operatory 2-argumentowe • odejmowania liczb zespolonych • zmiany znaku liczby zespolonej - operator 1-argumentowy Funkcja operatorowa składowa (operator 2-argumentowy) ROZWIĄZANIE 1: class Zesp { public: Zesp(); Zesp(float a, float b); Zesp dodaj(Zesp z); Zesp operator+ (Zesp z); private: float re, im; }; Zesp Zesp::dodaj(Zesp z) { return Zesp(re+z.re, im+z.im); } Zesp Zesp::operator+ (Zesp z) { return Zesp(re+z.re, im+z.im); } funkcje składowe klasy void main() { Zesp z1(2, 5), z2(1, -3), z3; z3 = z1.dodaj(z2); z3 = z1.operator+ (z2); z3 = z1+z2; } Funkcja operatorowa zaprzyjaźniona (operator 2-argumentowy) ROZWIĄZANIE 2: class Zesp { public: Zesp(); Zesp(float a, float b); friend Zesp odejmij(Zesp z1, Zesp z2); friend Zesp operator- (Zesp z1, Zesp z2); private: float re, im; }; funkcje zaprzyjaźnione z klasą Funkcja operatorowa zaprzyjaźniona (operator 2-argumentowy) ROZWIĄZANIE 2: Zesp odejmij(Zesp z1, Zesp z2) { return Zesp(z1.re-z2.re, z1.im-z2.im); } Zesp operator– (Zesp z1, Zesp z2) { return Zesp(z1.re-z2.re, z1.im-z2.im); } void main() { Zesp z1(2, 5), z2(1, -3), z3; z3 = odejmij(z1, z2); z3 = operator– (z1, z2); z3 = z1-z2; } Funkcja operatorowa składowa (operator 1-argumentowy) ROZWIĄZANIE 1: class Zesp { public: Zesp(); Zesp(float a, float b); Zesp minus(); Zesp operator- (); private: float re, im; }; Zesp Zesp::minus() { return Zesp(-re, -im); } Zesp Zesp::operator- () { return Zesp(-re, -im); } funkcje składowe klasy void main() { Zesp z1(2, 5), z2; z2 = z1.minus(); z2 = z1.operator- (); z2 = -z1; } Funkcja operatorowa zaprzyjaźniona (operator 1-argumentowy) ROZWIĄZANIE 2: class Zesp { public: Zesp(); Zesp(float a, float b); friend Zesp minus(Zesp z); friend Zesp operator- (Zesp z); private: float re, im; }; funkcje zaprzyjaźnione z klasą Funkcja operatorowa zaprzyjaźniona (operator 1-argumentowy) ROZWIĄZANIE 2: Zesp minus(Zesp z) { return Zesp(-z.re, -z.im); } Zesp operator– (Zesp z) { return Zesp(-z.re, -z.im); } void main() { Zesp z1(2, 5), z2; z2 = minus(z1); z2 = operator– (z1); z2 = -z1; } Dlaczego konieczne są funkcje operatorowe zaprzyjaźnione? MNOŻENIE OBIEKTU PRZEZ WARTOŚĆ void main() { float x = 5; Zesp z1(2, 5), z2; z2 = x * z1; } Funkcja składowa z2 = x.operator* (z1); float ŹLE! Funkcja zaprzyjaźniona z2 = operator* (x, z1); Funkcja operatorowa zaprzyjaźniona Zesp operator* (float x, Zesp z) { Zesp wynik; wynik.re = x*z.re; wynik.im = x*z.im; return wynik; } Dlaczego konieczne są funkcje operatorowe zaprzyjaźnione? STRUMIENIE WE / WY void main() { Zesp z1(2, 5), z2; cout << z1; cin >> z2; } ostream Funkcje składowe Funkcje zaprzyjaźnione cout.operator<< (z1); operator<< (cout, z1); cin.operator>> (z2); operator>> (cin, z2); istream ŹLE! Funkcja operatorowa zaprzyjaźniona ostream& operator<< (ostream &os, Zesp z) { os << ‘(‘ << z.re << ‘,‘ << z.im << ‘)‘; return os; } istream& operator>> (istream &is, Zesp &z) { is >> z.re; is >> z.im; return is; } Wskaźnik this Wskaźnik this zawiera adres obiektu bieżącego (tzn. obiektu, na rzecz którego wywołano funkcję składową). Wskaźnik ten jest dostępny wyłącznie wewnątrz niestatycznych funkcji składowych. this *this - adres obiektu obiekt (wartość obiektu) Słowo kluczowe this jest stosowane wszędzie tam, gdzie w funkcji składowej istnieje potrzeba zwrócenia wskaźnika na obiekt lub wartości obiektu. return this return *this - adres obiektu obiekt Zastosowanie wskaźnika this Zesp Zesp::operator+= (Zesp z) { re += z.re; // this->re += z.re; im += z.im; // (*this).im += z.im; return *this; } zwrócenie obiektu po modyfikacji void main() { Zesp z1(2, 3), z2(3, 5); z1.operator+= (z2); z1 += z2; } Podsumowanie Operator dwuargumentowy Funkcja składowa: Zesp operator+ (Zesp z); Funkcja zaprzyjaźniona: friend Zesp operator- (Zesp z1, Zesp z2); Operator jednoargumentowy Funkcja składowa: Zesp operator- (); Funkcja zaprzyjaźniona: friend Zesp operator- (Zesp z);