Podstawy Programowania Obiektowego

Podobne dokumenty
C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Podstawy Programowania Obiektowego

Przeciążanie operatorów

Podstawy Programowania Obiektowego

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

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

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

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

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

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Podstawy Programowania Obiektowego

Wykład 5: Klasy cz. 3

PARADYGMATY PROGRAMOWANIA Wykład 3

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

PROGRAMOWANIE OBIEKTOWE W C++ cz. 2. Dziedziczenie, operacje wej cia-wyj cia, przeładowanie operatorów.

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 4. Karol Tarnowski A-1 p.

PARADYGMATY PROGRAMOWANIA Wykład 4

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

Programowanie Obiektowe i C++

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

Wykład 8: klasy cz. 4

Operatory. Operatory bitowe i uzupełnienie informacji o pozostałych operatorach. Programowanie Proceduralne 1

Programowanie w C++ Wykład 14. Katarzyna Grzelak. 3 czerwca K.Grzelak (Wykład 14) Programowanie w C++ 1 / 27

Przeciążenie operatorów

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

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

TEMAT : KLASY DZIEDZICZENIE

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

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

Wstęp do Programowania 2

Język C++ umożliwia przeciążanie operatora, tzn. zmianę jego znaczenia na potrzeby danej klasy. W tym celu definiujemy funkcję o nazwie:

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

EGZAMIN PROGRAMOWANIE II (10 czerwca 2010) pytania i odpowiedzi

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Opus Magnum C++11 : programowanie w języku C++. T. 2 / Jerzy Grębosz. Gliwice, cop Spis treści

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

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

Szablony klas, zastosowanie szablonów w programach

Programowanie w języku C++

Programowanie obiektowe w języku C++ Zarządzanie procesami. dr inż. Jarosław Forenc. Przeładowanie (przeciążanie) operatorów

Konstruktor kopiujacy

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

Programowanie w C++ Wykład 13. Katarzyna Grzelak. 4 czerwca K.Grzelak (Wykład 13) Programowanie w C++ 1 / 26

Programowanie Obiektowo Zorientowane w języku c++ Konstruktory

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

Ok. Rozbijmy to na czynniki pierwsze, pomijając fragmenty, które już znamy:

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

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

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

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

W dowolnym momencie można zmienić typ wskaźnika.

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

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

Programowanie, część I

Język C++ zajęcia nr 2

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Dziedziczenie. Ogólna postać dziedziczenia klas:

Programowanie obiektowe. Dr hab. Inż. Marta Gładysiewicz-Kudrawiec Pokój 229 A1 Operatory new delete pliki-odczyt

Lab 9 Podstawy Programowania

Szablony funkcji i szablony klas

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

PARADYGMATY PROGRAMOWANIA Wykład 2

Wyliczanie wyrażenia obiekty tymczasowe

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

Spis treści PROGRAMOWANIE OBIEKTOWE W JĘZYKU C++: FUNKCJE ZAPRZYJAŹNIONE Z KLASĄ, PRZEŁADOWANIE OPERATORÓW. Informatyka 2

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

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

C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm POLIMORFIZM

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Wstęp do programowania obiektowego. Przekazywanie parametrów do funkcji w C++ Metody i funkcje operatorowe Strumienie: standardowe, plikowe, napisowe

Programowanie Obiektowe i C++

Rozdział 4 KLASY, OBIEKTY, METODY

ROZDZIAŁ 2. Operatory

Podstawy programowania w języku C i C++

Programowanie obiektowe i C++ dla matematyków

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

Funkcje. Spotkanie 5. Tworzenie i używanie funkcji. Przekazywanie argumentów do funkcji. Domyślne wartości argumentów

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

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

Wstęp do Programowania 2

Programowanie obiektowe i C++ dla matematyków

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

KLASY cz.1. Dorota Pylak

Operatory w C++ Operatory arytmetyczne. Operatory relacyjne (porównania) Operatory logiczne. + dodawanie - odejmowanie * mnożenie / dzielenie % modulo

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Dziedziczenie jednobazowe, poliformizm

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

ZASADY PROGRAMOWANIA KOMPUTERÓW

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

Programowanie Komputerów

Do czego służą klasy?

Zadanie 2: Arytmetyka symboli

Programowanie komputerowe. Zajęcia 7

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

Programowanie obiektowe. Materiały przygotował: mgr inż. Wojciech Frohmberg

Zaawansowane programowanie w języku C++ Klasy w C++

Transkrypt:

Podstawy Programowania Obiektowego. Spotkanie 07 Dr inż. Dariusz JĘDRZEJCZYK

Tematyka wykładu Pojęcie funkcji operatorowej Definicja funkcji operatorowej w klasie Definicja funkcji operatorowej poza klasą Argumentowość operatorów Operator przypisania = Operator wypisywania << Przykłady do samodzielnego wykonania 2

Definicja i trochę teorii Przeciążanie operatorów (ang. operator overloading, tłumaczone też jako przeładowanie operatorów) to rodzaj polimorfizmu występującego w niektórych językach programowania, polegający na tym, że operator może mieć różne implementacje w zależności od typów użytych argumentów (operandów). Mówiąc najkrócej w tej części zajmiemy się tym, jak sprawić by znaczki takie jak: +, - itd. pracowały dla nas i robiły to, co my im każemy. Sam znaczek + nie dodaje dwóch liczb. Kiedy on występuje w tekście programu, kompilator wywołuje specjalną funkcję, która zajmuje się dodawaniem liczb. Nazwijmy tę funkcję operatorem dodawania. Możesz sam napisać swoją funkcję operator dodawania, która będzie wywoływana wtedy, gdy koło znaczka + pojawią się argumenty wybranego przez Ciebie typu. Po prostu po raz kolejny zostanie przeładowany operator. 3

Definicja i trochę teorii Projektując klasę możemy zadbać o to, by pewne operacje na obiektach tej klasy wykonywane były na zasadzie przeładowania operatorów. Przeładowanie operatora dokonuje się definiując swoją własną funkcję która: Nazywa się operator@ - gdzie @ oznacza symbol operatora, o którego przeładowanie nam chodzi (np.: +, -, *, &, itd.) Jako co najmniej jeden z argumentów, przyjmuje obiekt danej klasy. (Musi być obiekt, nie może być wskaźnik do obiektu.) 4

Definicja i trochę teorii Definicja takiego operator wygląda tak: typ_zwracany operator@(argumenty) { // ciało funkcji } Do przeładowania mamy do dyspozycji bardzo wiele operatorów - wybieramy z nich te, które rzeczywiście będą nam potrzebne. Nie ma sensu przeładowywać wszystkich. 5

Definicja i trochę teorii Oto lista operatorów, które mogą być przeładowane: + - ~ * / % ^ &! = %= < > += -= *= /= ^= &=!= = << >> >>= <<= == <= > >= && ++ --, ->* - new delete () [] Natomiast nie mogą być przeładowane operatory:..* ::?: 6

Definicja i trochę teorii Kilka uwag dotyczących przeładowania operatorów: Przeładowywać można te operatory i tylko te podane na wcześniejszym slajdzie. Przeładowanie może nadać operatorowi dowolne znacznie, nie ma też ograniczeń co do wartości zwracanej przez operator (wyjątkiem są operatory new i delete) Nie można jednak zmieniać priorytetu wykonywania tych operatorów. Nie można też zmieniać argumentowości operatorów, czyli tego, czy operator jest jedno-, czy dwuargumentowy. Nie można zmienić łączności operatorów czyli tego, czy operator łączy się z argumentem z lewej, czy z prawej strony. 7

Definicja i trochę teorii Jeśli funkcja operatorowa jest zdefiniowana jako zwykła (globalna) funkcja, to przyjmuje tyle argumentów na ilu pracuje operator. Przynajmniej jeden z tych argumentów musi być typu zdefiniowanego przez użytkownika. Nie ma znaczenia który. Argumenty nie mogą być domniemane. jest niczym więcej jak tylko efektownym sposobem ułatwiającym notację wyrażeń, w których występują obiekty danych klas. Przeładowanie operatorów nie daje niczego takiego, co nie było możliwe do tej pory. To tylko notacja się upraszcza. 8

Funkcja operatorowa jako funkcja składowa Funkcja operatorowa może być zwykłą, globalną funkcją, a może być także funkcją składową klasy. Dana funkcja operatorowa może być albo funkcją globalną albo niestatyczną funkcją składową klasy, dla której pracuje. Jeśli operator definiujemy jako funkcję składową, to ma ona zawsze o jeden argument mniej niż ta sama funkcja napisana w postaci funkcji globalnej. Ten brakujący argument w wypadku funkcji składowej spowodowany jest oczywiście istnieniem wskaźnika this. 9

Funkcja operatorowa przyjacielem klasy? Jeśli funkcja operatorowa ma pracować tylko na publicznych składnikach klasy, to może być ona zwykłą funkcją składową, bez żadnych specjalnych uprawnień. Jeśli chcemy, by operator mógł pracować także na niepublicznych składnikach klasy wówczas klasa musi zadeklarować tę funkcję operatorową jako zaprzyjaźnioną. Przyjaźń nie jest obowiązkowa. To przecież oczywiste jeśli operator ma np.: zagwizdać na cześć obiektu danej klasy, to do tego nie jest mu potrzebny dostęp do jej prywatnych składników. 10

Operatory predefiniowane Jest kilka operatorów, których znaczenie jest tak oczywiste, że zostają one automatycznie generowane dla każdej klasy. Te operatory to: = - przypisanie [podstawienie] do obiektu danej klasy, & - (jednoargumentowy) pobranie adresu obiektu danej klasy,, (przecinek) - znaczenie takie jak dla typów wbudowanych, new, delete - kreacja i likwidacja obiektów w zapasie pamięci. 11

Argumentowość operatorów Operatory działają albo na jednym argumencie (jak np.: negacja), albo na dwóch argumentach (jak np.: dzielenie). Poza jednym wyjątkiem nie ma operatorów, które pracują na więcej niż dwóch argumentach. Jednym jedynym wyjątkiem, kiedy do operatora można przesłać większą liczbę argumentów jest operator( ). 12

Operatory jednoargumentowe Operatory te występują zwykle jako przedrostek, czyli stoją przed obiektem danej klasy. Oto przykłady: int i; - i! i ++i ~i &i Mogą być też jednoargumentowe operatory przyrostkowe. Stoją one za obiektem. i -- i ++ 13

Operatory jednoargumentowe Jeśli mamy daną klasę, to operatory jednoargumentowe typu przedrostkowego pracują na obiektach klasy K można zdefiniować jako: nieskładową (zwykłą) funkcję wywołaną z jednym argumentem (obiektem tej klasy K): typ_zwracany operator@(k){ } // ciało funkcji funkcję składową klasy K wywoływaną beż żadnych argumentów: 14

Operatory dwuargumentowe Operatory dwuargumentowe możemy także przeładowywać na dwa sposoby: jako funkcję składową niestatyczną wywoływaną z jednym argumentem: x.operator@(y) albo jako funkcję globalną (czyli zwykłą, nieskładową), wywoływaną z dwoma argumentami: operator@(x,y) Taka funkcja operatorowa zostaje automatycznie wywołana, gdy obok znaczka danego operatora znajdą się dwa argumenty określonego przez nas typu: x @ y 15

Przeładowanie operatora dwuargumentowego Załóżmy, że chcemy przeładować operator mnożenia *. Dla odmiany weźmy klasę reprezentującą trójwymiarowy wektor. Trzeba się zastanowić co chcemy, by ten operator* z obiektem klasy wektorek dla nas robił. Niech, służy do mnożenia wektora przez liczbę rzeczywistą. Jeśli jakiś wektor mnożymy przez 2, to wszystkie jego współrzędne mają zostać podwojone.

Przeładowanie operatora dwuargumentowego #include <iostream.h> //*************************************************** *** class wektorek { public : float x, y, z ; // --- konstruktor wektorek(float xp = 0, float yp = 0, float zp = 0 ) : x(xp), y(yp), z(zp) { /* cialo puste */ } ; } ; //... inne funkcje skladowe

Przeładowanie operatora dwuargumentowego wektorek operator*(wektorek kopia, float liczba ){ wektorek rezultat ; } rezultat.x = kopia.x * liczba ; rezultat.y = kopia.y * liczba ; rezultat.z = kopia.z * liczba ; return rezultat ; //*************************************************** *** void pokaz(wektorek www) ; // deklaracja //*************************************************** *** 18

Przeładowanie operatora dwuargumentowego void main(){ wektorek a(1,1,1), b(-15, -100, +1), c ; } c = a * 6.66 ; pokaz(c) ; c = b * -1.0 ; pokaz(c) ; //*************************************************** *** void pokaz(wektorek www){ } cout << " \n\n\nx = " << www.x << " y = " << www.y << " z = " << www.z << endl ; 19

Przemienność Dzięki przeładowaniu, możemy tworzyć wyrażenia: wektora = wektorb * 11.1; wektora = 11.1 * wektorb; Natomiast odwrócenie kolejności czynników iloczynu nie wchodzi w grę, czyli zapis zostanie przez kompilator odrzucony jako błędny. Funkcja operatorowa, która jest funkcją składową klasy wymaga, aby obiekt stojący po lewej stronie (znaczka) operatora był obiektem jej klasy. Operator, który jest zwykłą funkcją globalną -nie ma tego ograniczenia. 20

Operator przypisania = Dwuargumentowy operator przypisania klasa & klasa::operator=(klasa &) służy do przypisania jednemu obiektowi klasy klasa treści drugiego obiektu tej klasy. Jeśli nie zdefiniujemy sobie tego operatora kompilator automatycznie wygeneruje swoją wersję tego operatora polegającą na tym, że przypisanie odbędzie się metodą składnik po składniku. W rezultacie takiego przypisania będziemy mieli dwa obiekty o bliźniaczo identycznej treści. 21

Operator przypisania = W operatorze przypisania można wyraźnie rozróżnić dwie części, w których: obiekt likwidujemy. kreujemy obiekt jeszcze raz. Jedyna sytuacją, gdy na widok znaczka = rusza do pracy konstruktor kopiujący jest wystąpienie tego znaku w linijce definicji obiektu. Znaczek ten wtedy oznacza inicjalizacji, a nie przypisanie. Inicjalizacja zajmuje się konstruktor kopiujący, a przypisaniem operator przypisania. 22

Przeładowanie operatora przypisania Załóżmy, że mamy do czynienia z klasą wizytowka. Jeśli zamierzamy zbierać dane o tysiącach ludzi należy to zrobić w ten sposób, żeby rezerwować w pamięci tyle miejsca ile jest nam potrzebne do pomieszczenia konkretnego nazwiska oraz imienia. 23

Przeładowanie operatora przypisania #include <iostream.h> #include <string.h> //*************************************************** *** class wizytowka { public : char *nazw ; char *imie ; wizytowka(char *n, char *im) ; wizytowka(const wizytowka &wzor) ; // konstruktor kopiujacy ~wizytowka(); void pisz(char *) ; przypisania // operator // konstruktor // destruktor wizytowka & operator=(const wizytowka &wzor) ; 24

Przeładowanie operatora przypisania wizytowka::wizytowka(char*n, char *im){ nazw = new char[strlen(n) + 1] ; imie = new char[strlen(im) + 1] ; strcpy(nazw, n); strcpy(imie, im); cout << "Pracuje konstruktor zwykly" << endl ; } //*************************************************** *** wizytowka::wizytowka(const wizytowka &wzor){ nazw = new char[strlen(wzor.nazw) + 1] ; imie = new char[strlen(wzor.imie) + 1] ; strcpy(nazw, wzor.nazw); strcpy(imie, wzor.imie); cout << "Pracuje konstruktor kopiujacy " << endl ; } 25

Przeładowanie operatora przypisania wizytowka::~wizytowka(){ delete nazw ; delete imie ; } //*************************************************** *** void wizytowka::pisz(char *txt){ cout << " " << txt << ": Mamy goscia, jest to " << imie << " " << nazw << endl ; } 26

Przeładowanie operatora przypisania wizytowka & wizytowka::operator=(const wizytowka &wzor){ // -- czesc "destruktorowa" ------ } delete nazw ; delete imie ; // -- czesc "konstruktorowa (konst. kopiujacy)"- nazw = new char[strlen(wzor.nazw) + 1] ; imie = new char[strlen(wzor.imie) + 1] ; strcpy(imie, wzor.imie); strcpy(nazw, wzor.nazw); cout << "Pracuje operator= (przypisania)\n" ; return *this ; 27

Przeładowanie operatora przypisania void main(){ } cout << "Definicje 'veneziano', i 'salzburger' \n" ; wizytowka veneziano("vivaldi", "Antonio"), salzburger("mozart", "Wolfgang A."); cout << "Definicja 'nowy' : \n" ; wizytowka nowy = veneziano ; cout << "Oto tresc w obiektach\n" ; veneziano.pisz("ven1"); salzburger.pisz("sal1"); nowy.pisz("now1"); cout << "Zabawy z przypisywaniem ---\n" ; nowy = salzburger ; nowy.pisz("now2"); nowy = veneziano ; nowy.pisz("now3"); nowy = salzburger = veneziano ; nowy.pisz("now4"); salzburger.pisz("sal4"); veneziano.pisz("ven4"); // konstruktor kopiujacy 28

Operator = nie generowany automatycznie Jeśli klasa ma składnik const, to operator nie będzie wygenerowany. Jest to oczywiste, bo skoro w klasie jest jakiś składnik ustawiony jako const to znaczy, że dopuszczamy jego inicjalizację, ale potem nie wolno już go zmieniać, czyli nic do niego przypisywać. Podobnie w wypadku obecności składnika będącego referencją. Jak wiemy, referencję tylko się inicjalizuje, a potem już przepadło nie można rozmyślić się i przerzucić przezwisko na inny obiekt. Jeśli klasa ma składnik będący obiektem innej klasy i w tej innej klasie operator przypisania określony jest jako private to wówczas nie będzie generowany operator przypisania dla klasy go zawierającej. Analogicznie jest w przypadku, gdy klasa ma klasę podstawową, w której operator= jest typu private. 29

Operatory postinkrementacji i postdekrementacji Wyróżniamy dwa różne operatory++: Gdy symbol ++ stoi przed nazwą obiektu to kompilator wybiera wersję jednoargumentową tego operatora (pre inkrementacja). Gdy symbol ++ stoi za nazwą obiektu kompilator wywołuje wersję dwuargumentową (post inkrementacja). Ważne jest tu sformułowanie: dwa różne. Bowiem ciało jednej i drugiej wersji może być całkowicie odmienne. To co dotyczy operatora postinkrementacji, dotyczy analogicznie operatora postdekrementacji. 30

Praktyczne rady dotyczące przeładowania Jako, że mechanizm przeładowania jest możliwością, z której można skorzystać lub nie. Chodzi o to, żeby jeśli już zdecydujemy się na przeładowanie jednego lub kilku operatorów żeby robić to mądrze, posługując się jakąś logiką. Oto kilka rad: Nie ma sensu przeładowywać wszystkich operatorów dla danej klasy. Może się bowiem okazać, że wykonałeś kawał dobrej, solidnej, nikomu nie potrzebnej roboty. Nie staraj się przeładowywać na siłę. Jeśli nazwa funkcji składowej lepiej opisuje działanie tej funkcji, niż robi to wygląd operatora, to lepiej pozostać przy funkcji. Przeładowanie powinno służyć uproszczeniu czytania, a nie produkcji łamigłówek. Cała wspaniałość przeładowania polega na zbliżeniu zapisu operacji na klasach, do prostoty zapisu operacji na typach wbudowanych. 31

Praktyczne rady dotyczące przeładowania Jeśli przeładowałeś operator+ oraz operator= to nie sądź, że tym samym masz automatycznie operator+= albo operator++. Są to zupełnie inne funkcje operatorowe i jeśli chcesz się nimi posługiwać wobec obiektów danej klasy, to musisz je także przeładować. Przed przystąpieniem do pracy trzeba się zastanowić, jak taka klasa wygląda z zewnątrz - to znaczy jakie wykonuje się operację na obiektach danej klasy. Kiedy już to jest jasne, można się zastanowi, które z tych funkcji wygodniej byłoby przeprowadzać za pomocą operatorów. Mimo całej dowolności treści przeładowanych funkcji operatorowych - staraj się zachować logikę pewnych zależności między operatorami. 32

Praktyczne rady dotyczące przeładowania Jeśli operator jest nieszkodliwy dla typu wbudowanego - to znaczy nie zmienia wartości zmiennej, na której pracuje, to staraj się, by jego odpowiednik dla klasy również niczego nie zmieniał wewnątrz obiektu. Wartość zwracana przez operator jest bardzo ważna. Dzięki temu, że operator nie tylko wykonuje działanie, ale też zwraca rezultat możliwe są wyrażenia kaskadowe a + b + c + d gdzie najpierw odbywa się jedno dodawanie, potem jego rezultat staje się składnikiem drugiego dodawania i kolejny rezultat staje się składnikiem trzeciego. 33

Operator jako funkcja składowa - czy globalna Skoro ten sam operator można zdefiniować jako funkcję składową albo funkcję globalną, to nasuwa się pytanie: jak lepiej zrobić? Jednoznacznej odpowiedzi nie ma. Zależy to od tego, czego oczekujemy od operatora. Ogólnie można powiedzieć, że: Jeśli operator zmienia w jakiś sposób obiekt, na którym pracuje, to powinien być zdefiniowany jako funkcja składowa jego klasy. Operatory te wtedy zwracają l wartości (np.: operatory =, ++, --). Jeśli natomiast operator sięga po obiekt po to, by pracować z nim bez modyfikowania go to wówczas raczej stosuje się operator w postaci funkcji globalnej (np.: operatory -, +, /). 34

Operator jako funkcja składowa - czy globalna Jeśli operator ma dopuszczać, by po jego lewej stronie stał typ wbudowany, to nie może być funkcją składową. Musi być globalną. Jeśli używamy operatora, który zdefiniowany jest jako funkcja składowa, wówczas w stosunku do jego pierwszego argumentu nie może zajść żadna niejawna konwersja (Mowa o tym argumencie, który zostaje przesłany za pomocą wskaźnika this). 35

Tajemnica operator << Właściwie już od początku posługujemy się zapisem, w którym występuje operator<<: cout<< Pierwszy program ; Dwuargumentowy operator<< jest, w stosunku do typu wbudowanego int, operatorem powodującym przesunięcie bitów o żądaną liczbę pozycji. Pytanie: Jak to więc jest możliwe, że taki zapis: int m = 2; cout<< m; powoduje wyprowadzenie na ekran liczby zapisanej w zmiennej typu int? 36

Tajemnica operator << Odpowiedź: Mamy tu do czynienia z najzwyklejszym przeładowaniem operatora<<. Zapis ten jest inaczej rozumiany tak: cout.operator<< (m); cout jest egzemplarzem obiektu klasy, która nazywa się ostream. To dla tej klasy dokonano przeładowania operatora. Przeładowanie jest możliwe, gdy jednym z argumentów jest obiekt typu zdefiniowanego przez użytkownika. Takim typem zdefiniowanym - choć bibliotecznym - jest właśnie klasa ostream. 37

Tajemnica operator << Jest kilka wersji przeładowania operatora<< na okoliczność pracy z różnymi typami argumentów np.: float, char*. Jeśli mamy zdefiniowaną własną klasę np.: wektor, to użycie operatora<< w ten sposób: wektor w; cout<< w; Jest niepoprawne. Aby można było wykorzystać taki zapis należy po raz kolejny przeładować operator<<. Jest on dwuargumentowy, więc argumenty będą typu ostream i wektor. 38

Tajemnica operator << 01. #include <iostream.h> 02. class wektor 03. { 04. public: 05. float x, y, z ; 06. wektor(float a=0, float b=0, float c=0):x(a) 07. { 08. y = b; 09. z = c; 10. } 11. }; 39

Tajemnica operator << 01. ostream & operator<<(ostream & ekran, wektor & w) 02. { 03. ekran << "wspolrzedne wektora : "; 04. ekran << "( ; 05. ekran << w.x; 06. ekran << ", " << w.y << ", " << w.z << ")"; 07. return ekran; 08. } 40

Tajemnica operator << 01. void main(void) 02. { 03. wektor w(1,2,3),v,k(-10, -20, 100); 04. cout << w; 05. cout << "\nwektor v ->" << v<< endl; 06. cout << "Wywolanie jawne \n ; 07. operator<<( cout, w); 08. } 41

Koniec Dziękuję za uwagę i zapraszam na 15 minut przerwy. W dalszej części ćwiczenia do samodzielnego wykonania. 42

Ćwiczenia do wykonania Zadanie 01 Zdefiniuj klasę LiczbaZespolona oraz stosowne funkcje, konstruktory oraz funkcje operatorowe, ponadto uzupełnij klasę LiczbaZespolona tak, aby możliwe stało się wykonywanie następującego kodu: LiczbaZespolona z1, z2(1,2), z3(z2); cout<< z1; cout<< z2; cout<< z3; int a; z1 = z2 + z3; cout<< z1; cout<< z2; cout<< z3; z1 = z1 - z3; cout<< z1; cout<< z2; cout<< z3; z3 = z1 * z2; cout<< z1; cout<< z2; cout<< z3; z1 = z2.liczbaprzeciwna(); cout<< z1; cout<< z2; z3 = liczbasprzezona(z1); cout<< z1; cout<< z3; a =!z2; cout<< a; cout<< z1; cout<< z2; cout<< z3; //modul z liczby zespolonej z2 43

Ćwiczenia do wykonania UWAGA: Dla liczby zespolonej z1(a,b) -> z1 = a + bi oraz z2(c,d) -> z2 = c + di gdzie: a,c -> to części rzeczywiste, b,d -> części urojone, operacje arytmetyczne przedstawiają się następująco: - dodawanie: z1 + z2 = (a+c) + (b+d)i, - odejmowanie: z1- z2 = (a-c) + (b-d)i, - mnożenie: z1*z2 = (ac-bd)+ (ad+ bc)i, - moduł z z1: z1 = pierwiastek(a^2 + b^2), - liczba przeciwna do liczby zespolonej z1: -z1 = -a - bi, - liczbą sprzężoną z liczbą z1: z1* = a - bi. 44