Wskaźniki Cztery atrybuty zmiennej. Wskaźnik jest typem wartości. Operator * oznacza: Operator & oznacza: nazwa typ. wartość lokacja (adres)



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

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

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

Programowanie obiektowe i C++ dla matematyków

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

PARADYGMATY PROGRAMOWANIA Wykład 3

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

Programowanie obiektowe - Przykładowe zadania egzaminacyjne (2005/2006)

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

Wykład 5: Klasy cz. 3

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

Deklaracje i definicje klas w C++ Składowe, pola, metody Konstruktory Wskaźnik this Destruktor Przeciążanie funkcji i operatorów Funkcje otwarte

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

TEMAT : KLASY DZIEDZICZENIE

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

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

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

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

Konstruktor kopiujacy

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

Programowanie Obiektowe i C++

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

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

Wstęp do Programowania 2

Wykład 8: klasy cz. 4

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

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

Przeciążenie operatorów

Języki i techniki programowania Ćwiczenia 2

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

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

Szablony klas, zastosowanie szablonów w programach

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

PARADYGMATY PROGRAMOWANIA Wykład 4

Funkcje przeciążone, konstruktory kopiujące, argumenty domyślne

Dzisiejszy wykład. Klasa string. wersja prosta wersja ze zliczaniem odwołań. Wyjątki Specyfikator volatile Semafory

Programowanie Obiektowe i C++

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

Wyliczanie wyrażenia obiekty tymczasowe

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

Język C++ Różnice między C a C++

Wstęp do Programowania 2

Operatory na rzecz typu TString

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

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

Operacje wejścia/wyjścia (odsłona druga) - pliki

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

Listy powiązane zorientowane obiektowo

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

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

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

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

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

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

Wykład 4: Klasy i Metody

Programowanie w języku C++

Dziedziczenie jednobazowe, poliformizm

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

Wykład 3 Składnia języka C# (cz. 2)

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.

Język C++ zajęcia nr 2

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

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

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy KONSTRUKTORY

Programowanie i struktury danych. Wykład 4 Dr Piotr Cybula

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Kurs WWW. Paweł Rajba.

Paradygmaty programowania. Paradygmaty programowania

EGZAMIN PROGRAMOWANIE II (10 czerwca 2010) pytania i odpowiedzi

Wstęp do programowania obiektowego, wykład 7

Wykład V. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Podstawy Programowania Obiektowego

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

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

Programowanie, część I

Materiały do zajęć VII

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

Przeciążanie operatorów

ALGORYTMY I STRUKTURY DANYCH

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

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

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

Programowanie obiektowe i C++ dla matematyków

Dzisiejszy wykład. Wzorce funkcji Wzorce klas Tablica asocjacyjna Składowe statyczne

Lab 9 Podstawy Programowania

KLASY cz.1. Dorota Pylak

Programowanie obiektowe

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

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

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

Programowanie obiektowe w C++ Wykład 12

Programowanie w języku C++

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

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy PRAWA PRZYJACIÓŁ KLASY. Dostęp z zewnątrz: Dostęp z wewnątrz:

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

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

Do czego służą klasy?

Do czego służą klasy?

Transkrypt:

Dzisiejszy wykład Wskaźniki Klasa listy jednokierunkowej Przekazywanie parametrów do funkcji Płytkie i głębokie kopiowanie Konstruktor kopiujący Operator przypisania Przeciążenie operatorów 1

Wskaźniki Cztery atrybuty zmiennej nazwa typ int x = 5; wartość lokacja (adres) Wskaźnik jest typem wartości przechowywany w zmiennej jest to pewna liczba Operator * oznacza: pobierz wartość przechowywaną w zmiennej i użyj jej jako adresu innej zmiennej Operator & oznacza: pobierz adres zmiennej (NIE wartość zmiennej) 2

Wskaźniki Zmienna nazwa, typ, wartość, lokacja (adres) lokacja int x = 5; 001000 5 typ wartość nazwa W programie W pamięci Która zmienna pod jakim adresem? Ile pamięci zajmuje? Kto o tym decyduje? 3

Wskaźniki Jaka jest wartość poniższych wyrażeń? Czy są one poprawne? x &x *x lokacja int x = 5; 001000 5 typ wartość nazwa W programie W pamięci 4

Wskaźniki Jaka jest wartość poniższych wyrażeń? Czy są one poprawne? x, &x, *x p, &p, *p q, &q, *q ip, &ip, *ip lokacja wartość nazwa int x=5; char *p="hello"; char *q; int *ip; ip=&x; 001000 5 int x 001004 3000 char*p 001008? char*q 001012? int* ip...... 003000 hello\0 W programie W pamięci 5

Wskaźniki do funkcji int* f1(int*, const int*); int* (*fp1)(int*, const int*); int* (*f2(int))(int*, const int*); int* (*(*fp2)(int))(int*, const int*); fp1=f1; fp1=&f1; fp1=&fp1; /* zle */ fp2=f2; fp2=&f2; int a,b,*c; c=f1(&a,&b); c=fp1(&a,&b); c=(*fp1)(&a,&b); c=*fp1(&a,&b); /* zle */ c=(f2(3))(&a,&b); c=(*f2(3))(&a,&b); c=(fp2(3))(&a,&b); c=(*fp2(3))(&a,&b); c=(*(*fp2)(3))(&a,&b); 6

Klasa List Lista jednokierunkowa z dowiązaniami l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 class list private: struct node node *next; int val; ; node * head; ; 7

Klasa List - konstruktor i destruktor l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 class list private: struct node node *next; int val; ; node * head; public: list (); ~list (); void insert (int a); list::list() head = NULL; list::~list() while(head) node* t=head->next; delete head; head=t; ; 8

Klasa List - destruktor l head next=0x1334 val=2 next=null val=3 class list private: struct node list::list() node *next; int val; head = NULL; ; node * head; public: list (); ~list (); void insert (int a); list::~list() while(head) node* t=head->next; delete head; head=t; ; 9

Klasa List - destruktor l head next=null val=3 class list private: struct node list::list() node *next; int val; head = NULL; ; node * head; public: list (); ~list (); void insert (int a); list::~list() while(head) node* t=head->next; delete head; head=t; ; 10

Klasa List - destruktor l head=null class list private: struct node list::list() node *next; int val; head = NULL; ; node * head; public: list (); ~list (); void insert (int a); list::~list() while(head) node* t=head->next; delete head; head=t; ; 11

Klasa List - insert l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 class list private: struct node ; node *next; int val; node * head; public: list (); ~list (); void insert (int a); next=0x1134 val=1 void list::insert(int a) node* nowy=new node; nowy->next=head; head = nowy; head->val = a; 12

Klasa List - insert l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 class list private: struct node ; node *next; int val; node * head; public: list (); ~list (); void insert (int a); next=0x1134 val=1 void list::insert(int a) node* nowy=new node; nowy->next=head; head = nowy; head->val = a; 13

Klasa List - insert l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 class list private: struct node ; node *next; int val; node * head; public: list (); ~list (); void insert (int a); next=0x1134 val=1 void list::insert(int a) node* nowy=new node; nowy->next=head; head = nowy; head->val = a; 14

Klasa List - iterator l head current next=0x1234 val=1 next=0x1334 val=2 next=null val=3 class list private:... node * head; node *current; public:... void gotohead (); int getcurrentdata (); ; void advance (); bool moredata (); #include <iostream> using namespace std; #include "list.h" int main() list l; l.insert(3); l.insert(2); l.insert(1); l.gotohead(); while(l.moredata()) int val; val=l.getcurrentdata(); cout << val << " "; l.advance(); cout << endl; ; 15

Przekazywanie argumentów do funkcji Przekazywanie przez wartość parametry formalne są kopią parametrów aktualnych Przekazywanie przez referencję parametry formalne są referencją do parametrów aktualnych, tj. wszystkie operacje na parametrach formalnych odnoszą się do parametrów aktualnych C i C++ domyślnie przekazują parametry przez wartość void d1(int x) x = 10; void d2(int *p) (*p) = 10; void d3(int *p) p = new int(4); int main() int y = 2; d1(y); cout << y; d2(&y); cout << y; d3(&y); cout << y; 16

Przekazywanie argumentów do funkcji Przez wartość parametry formalne są kopią parametrów aktualnych Przez referencję parametry formalne są referencją do parametrów aktualnych, tj. wszystkie operacje na parametrach formalnych odnoszą się do parametrów aktualnych Przez stałą referencję do funkcji przekazywana jest referencja do parametru w celu uniknięcia kosztów kopiowania, ale wartość nie może być w funkcji modyfikowana (co jest sprawdzane przez kompilator) void f1(int x) x = x + 1; void f2(int& x) x = x + 1; void f3(const int& x) x = x + 1; void f4(int *x) *x = *x + 1; int main() int y = 5; f1(y); f2(y); f3(y); f4(&y); Która metoda w którym przykładzie? Ile wynosi y po każdym wywołaniu? Co jest przekazywane do f4? Czy to wartość, czy referencja? Czy można przekazać wskaźnik przez referencję? 17

Przekazywanie argumentów do funkcji Przekazywanie obiektów do funkcji nie różni się koncepcyjnie od przekazywania typów prostych klasy umożliwiają modyfikację zachowania w tym przypadku Trzy metody: przez wartość, przez referencję, przez stałą referencję Przez wartość parametr formalny jest kopią parametru aktualnego. Użyty konstruktor kopiujący. Przez referencję parametry formalne są referencją do parametrów aktualnych, tj. wszystkie operacje na parametrach formalnych odnoszą się do parametrów aktualnych Przez stałą referencję do funkcji przekazywana jest stała referencja do argumentu. Tylko metody ze specyfikatorem const mogą być wywoływane wewnątrz metody na rzecz argumentu. 18

Przekazywanie obiektów jako parametrów Kiedy obiekt jest użyty jako parametr wywołania funkcji, rozróżnienie między kopiowaniem płytkim a głębokim może spowodować tajemnicze problemy void PrintList (list & toprint, ostream & Out) int nextvalue; Out << "Printing list contents: " << endl; toprint.gotohead (); if (!toprint.moredata ()) Out << "List is empty" << endl; return; while (toprint.moredata ()) nextvalue = toprint.getcurrentdata (); Out << nextvalue << " "; toprint.advance (); Out << endl; Obiekt toprint jest przekazywany przez referencję, gdyż może być duży i kopiowanie byłoby nieefektywne Czy można użyć stałej referencji? Co by się stało, gdybyśmy przekazali toprint przez wartość? 19

Przekazywanie obiektów jako parametrów W poprzednim przykładzie obiekt nie może być przekazany przez stałą referencję, gdyż wywołana funkcja zmienia obiekt (wskaźnik current) Z tego powodu możemy chcieć wyeliminować możliwość przypadkowej modyfikacji listy i przekazywać ją przez wartość Będzie to rozwiązanie nieefektywne Może spowodować problemy, jeżeli brak konstruktora kopiującego 20

Przekazywanie obiektów przez wartość void PrintList (list toprint, ostream & Out) // identyczna implementacja int main() list BigLst; // initialize BigList with some data nodes PrintList(BigList, cout); BigList l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 toprint l head BigList i toprint są tym samym obiektem Funkcja PrintList dalej może modyfikować BigList Konsekwencje są jeszcze poważniejsze 21

Przekazywanie obiektów przez wartość Po zakończeniu PrintList czas życia zmiennej toprint kończy się i wywoływany jest jej destruktor BigList l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 toprint l head Dealokacja toprint powoduje dealokację listy węzłów, na którą wskazuje toprint.head Jest to ta sama lista, która jest zawarta w BigList. Po powrocie do funkcji main(), BigList została zniszczona, ale BigList.head wskazuje na zwolnioną pamięć 22

Przypisanie obiektów Obiekty posiadają domyślny operator przypisania (identyczny jak struktury) class DateType public: // constructor DateType(); DateType(int newmonth, int newday, int newyear);... ;... DateType A(1, 22, 2002); DateType B; B = A; // copies the data members of A into B Domyślny operator przypisania kopiuje pole po polu wartości z obiektu źródłowego do obiektu docelowego W wielu przypadkach jest to zadowalające. Niestety, jeżeli obiekt zawiera wskaźnik do dynamicznie zaalokowanej pamięci, rezultat zwykle nas nie zadowoli. 23

Problem z przypisaniem wskaźników class Wrong private: int *table; // some data here public: // constructor Wrong() table = new int[1000]; ~Wrong() delete [] table; ;... Wrong A; Wrong B; B = A; // copies the data members of A into B Jaki typ danych przechowuje Wrong? Czy int * table to to samo co int table[]? Co się stanie przy kopiowaniu? Jakie napotkamy problemy? Jak temu zapobiec? A B table? 24

Kopiowanie płytkie i głębokie Wyróżniamy dwa typy kopiowania obiektów zawierających pola będące wskaźnikami Kopiowanie płytkie Kopiowanie wszystkich składowych (w tym wskaźników) Kopiowane są wskaźniki, a nie to, na co wskazują Kopiowanie głębokie A B table Alokacja nowej pamięci dla wskaźników Kopiowanie zawartości wskazywanej przez wskaźniki w nowe miejsce Kopiowanie pozostałych pól, nie będących wskaźnikami A table B table 25

Problemy z płytkim kopiowaniem list mylist; mylist.insert (3); mylist.insert (2); mylist.insert (1); mylist l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 26

Problemy z płytkim kopiowaniem list mylist; mylist.insert (3); mylist.insert (2); mylist.insert (1); list anotherlist; anotherlist=mylist; mylist l head next=0x1234 val=1 next=0x1334 val=2 next=null val=3 anotherlist l head 27

Głębokie kopiowanie Kiedy obiekt zawiera wskaźnik do dynamicznie zaalokowanego obszaru, należy zdefiniować operator przypisania wykonujący głębokie kopiowanie W rozważanej klasie należy zdefiniować operator przypisania: AType& AType::operator=(const AType& otherobj) Operator przypisania powinien uwzględnić przypadki szczególne: Sprawdzić przypisanie obiektu do samego siebie, np. A=A: if (this == &otherobj) // if true, do nothing Skasować zawartośc obiektu docelowego delete this->... Zaalokować pamięć dla kopiowanych wartości Przepisać kopiowane wartości Zwrócic *this 28

Konstruktor kopiujący i operator przypisania Konstruktor kopiujący jest używany do stworzenia nowego obiektu Jest prostszy od operatora przypisania - nie musi sprawdzać przypisania do samego siebie i zwalniać poprzedniej zawartości Jest użyty do skopiowania parametru aktualnego do parametru formalnego przy przekazywaniu parametru przez wartość Przy tworzeniu nowego obiektu, można go zainicjalizować istniejącym obiektem danego typu. Wywołany jest wówczas konstruktor kopiujący. int main() list a; //... list b(a); //copy constructor called list c=a; //copy constructor called ; 29

Obiekty anonimowe Obiekt anonimowy to obiekt bez nazwy Tworzony jest obiekt, ale nie ma nazwanej zmiennej, która go przechowuje Użyteczny do użytku tymczasowego (parametr przy wywołaniu funkcji, zwracaniu wartości, fragment wyrażenia) jako domyślna wartość parametru będącego obiektem Rozważmy metodę pobierającą obiekt typu Address: Argument może zostać przekazany w sposób następujący: Person joe; joe.setaddress(address("disk Drive"...)); Zamiast: void Person::setAddress(Address addr); Person joe; Address joeaddress("disk Drive"...); joe.setaddress(joeaddress); 30

Przykład: obiekty anonimowe jako parametry Bez obiektów anonimowych mamy nieporządek Name JBHName("Joe", "Bob", "Hokie"); Address JBHAddr("Oak Bridge Apts", "#13", "Blacksburg","Virginia", "24060"); Person JBH(JBHName, JBHAddr, MALE);... Użycie obiektów anonimowych zmniejsza zanieczyszczenie lokalnej przestrzeni nazw Person JBH(Name("Joe", "Bob", "Hokie"), Address("Oak Bridge Apts", "#13", "Blacksburg", "Virginia", "24060"),MALE);... 31

Przykład: obiekty anonimowe jako wartości domyślne Użycie obiektów anonimowych jest relatywnie prostą metodą na kontrolowanie inicjalizacji i zmniejszenie liczby metod klasy Person::Person(Name N = Name("I", "M", "Nobody"), Address A = Address("No Street", "No Number", "No City", "No State", "00000"), Gender G = GENDERUNKNOWN) Nom = N; Addr = A; Spouse = NULL; Gen = G; 32

Wybrane metody tworzenia obiektów Zmienne automatyczne Atype a; //konstruktor domyślny Zmienne automatyczne z argumentami Atype a(3); //konstruktor z parametrem int Przekazywanie parametrów funkcji przez wartość void f(atype b)... Atype a; //konstruktor domyślny... f(a); //konstruktor kopiujący Przypisanie wartości zmiennym Atype a,b;... a=b; //operator przypisania Inicjalizacja nowych obiektów Atype b; //konstruktor domyslny... Atype a=b; //konstruktor kopiujący (NIE operator przypisania) Zwracanie wartości z funkcji Atype f() Atype a; //konstruktor domyślny... return a; //konstruktor kopiujący 33

Cechy dobrze napisanej klasy Jawny konstruktor domyślny Gwarantuje, że każdy zadeklarowany egzemplarz obiektu zostanie w kontrolowany sposób zainicjalizowany Jeżeli obiekt zawiera wskaźniki do dynamicznie zaalokowanej pamięci: Jawny destruktor Zapobiega wyciekom pamięci. Zwalnia zasoby podczas usuwania obiektu. Jawny operator przypisania Używany przy przypisywaniu nowej wartości do istniejącego obiektu. Zapewnia, że obiekt jest istotnie kopią innego obiektu, a nie jego aliasem (inną nazwą). Jawny konstruktor kopiujący Używany podczas kopiowania obiektu przy przekazywaniu parametrów, zwracaniu wartości i inicjalizacji. Zapewnia, że obiekt jest istotnie kopią innego obiektu, a nie jego aliasem. 34

Przeciążenie Przeciążenie - istnienie wielu definicji tej samej nazwy Wiele funkcji noszących tę samą nazwę W C++, przeciążone nazwy są rozróżniane na podstawie liczby i typu argumentów z uwzględnieniem dziedziczenia Powyższe parametry zwane są sygnaturą funkcji typy wartości zwracanej nie są rozróżniane, poniższy przykład jest niepoprawny double fromint(int x) float fromint(int x) Częste zastosowanie przeciążenia to przeciążenie operatorów 35

Przeciążenie i polimorfizm Przeciążenie to forma polimorfizmu Pozwala na zdefiniowanie nowego znaczenia (funkcjonowania) operatorów dla wybranych typów. Kompilator rozpoznaje, której implementacji użyć, po sygnaturze funkcji (typach operandów użytych w wyrażeniu) Przeciążenie jest wspierane dla wielu wbudowanych operatorów 17 * 42 4.3 * 2.9 cout << 79 << 'a' << "overloading is profitable" << endl; Użyta implementacja zależy od typów operandów 36

Przyczyny przeciążania operatorów Wsparcie dla naturalnego, sugestywnego użycia: Complex A(4.3, -2.7), B(1.0, 5.8); Complex C; C = A + B; // '+' oznacza dodawanie dla tego typu, tak samo jak dla np. typu int Integralność semantyczna: przypisanie dla typów obiektowych musi zapewnić wykonanie głębokiej kopii Możliwość użycia obiektów w sytuacjach, w których oczekiwany jest typ prosty 37

Operatory, które mogą być przeciążane Jedynie poniższe operatory mogą być przeciążane + - * / % ^ & ~! = < > += -= *= /= %= ^= &=!= << >> >>= <<= ==!= <= >= && ++ -- ->*, -> [] () new new[] delete delete [] Operatory =, ->, [], () muszą być metodami niestatycznymi 38

Wskazówki dotyczące przeciążania operatorów Operator powinien zachowywać się zgodnie z oczekiwaniami użytkownika Complex Complex::operator~() const return ( Complex(Imag, Real) ); Należy dostarczyć pełen zestaw pokrewnych operatorów: a = a + b i a+=b powinny mieć taki sam efekt i należy dostarczyć użytkownikowi klasy oba operatory Należy zdefiniować operator jako składową jeśli inne rozwiązanie nie jest konieczne Jeżeli przeciążony operator nie może być składową, powinien być funkcją zaprzyjaźnioną, a nie używać akcesorów, dodanych do klasy wyłącznie w tym celu 39

Składnia przeciążania operatorów Deklarowane i definiowane tak samo jak inne metody i funkcje, różnią się użyciem słowa kluczowego operator Jako metoda klasy: bool Name::operator== (const Name& RHS) return ((First == RHS.First) && (Middle == RHS.Middle) && (Last == RHS.Last) ); Jako funkcja zaprzyjaźniona: bool operator==(const Name& LHS, const Name& RHS) return ((LHS.First == RHS.First) && (LHS.Middle == RHS.Middle) && (LHS.Last == RHS.Last) ); Bardziej naturalne jest użycie metody 40

Użycie operatorów przeciążonych Jeżeli Name::operator= jest zdefiniowany jako metoda klasy Name, wówczas nme1 == nme2 jest równoważne nme1.operator==(nme2) Jeżeli operator== nie jest zdefiniowany jako składowa, wówczas nme1 == nme2 jest równoważne operator==(nme1, nme2) 41

Przeciążenie operatorów Składowe przeciążające operatory są definiowane z użyciem słowa kluczowego operator // add to DateType.h: bool operator==(datetype otherdate) const ; Interfejs // add to DateType.cpp: bool datetype::operator==(datetype otherdate) const return( (Day == otherdate.day ) && (Month == otherdate.month ) && (Year == otherdate.year )); DateType adate(10, 15, 2000); DateType bdate(10, 15, 2001); Klient Odpowiednio użyte przeciążenie operatorów pozwala na traktowanie obiektów typu zdefiniowanego przez użytkownika w sposób tak samo naturalny, jak typów wbudowanych. if (adate == bdate)... Implementacja 42

Operator binarny jako składowa Operator odejmowania dla klasy Complex jako składowa: Complex Complex::operator-(const Complex& RHS) const return ( Complex(Real - RHS.Real, Imag - RHS.Imag) ); Lewy operand operatora musi być obiektem Complex X(4.1, 2.3), Y(-1.2, 5.0); int Z; OK: X + Y; Not OK: Z + X; Zazwyczaj przekazuje się operand przez stałą referencję, żeby uniknąć narzutu kopiowania 43

Operator binarny jako funkcja nie będąca składową Operator odejmowania dla klasy Complex jako funkcja nie będąca składową: Operator odejmowania musi używać interfejsu publicznego w celu dostępu do danych prywatnych klasy... Complex operator-(const Complex& LHS, const Complex& RHS) return ( Complex(LHS.getReal() - RHS.getReal(), LHS.getImag() - RHS.getImag()) );...chyba że klasa Complex zadeklaruje funkcję jako funkcję zaprzyjaźnioną Funkcja zaprzyjaźniona ma dostęp do składowych prywatnych klasy tak samo, jak składowa. class Complex ;... friend Complex operator+ (const Complex&, const Complex&);... 44

Operatory jednoargumentowe (unarne) Operator negacji dla klasy Complex Complex Complex::operator-() const return ( Complex(-Real, -Imag) ); Complex A(4.1, 3.2); // A = 4.1 + 3.2i Complex B = -A; // B = -4.1-3.2i Składowa implementująca unarny operator nie ma parametrów. 45

Operatory pre- i postinkrementacji class Value private: int x; public: Value(int i = 0) : x(i) int get() const return x; void set(int x) ( this->x = x; Value& operator++(); Value operator++(int Dummy); Operator preinkrementacji Value& Value::operator++() x = x + 1; return *this; Operator postinkrementacji Value Value::operator++(int Dummy) x = x + 1; return Value(x-1); // return previous value 46

Wielokrotne przeciążenie W klasie może być kilka operatorów dodawania Complex Complex::operator+(double RHS) const return (Complex(Real + RHS, Imag)); Complex Complex::operator+(Complex RHS) const return (Complex(Real + RHS.Real, Imag + RHS.Imag)); Napiszmy kilka wyrażeń mieszanych: Complex X(4.1, 2.3); double R = 1.9; Complex Y = X + R; // Y.Real is 6.0 Sygnatura funkcji zostanie użyta do wyboru właściwej funkcji Complex Z = Y + R; // complex plus double Complex W = Y + X; // complex plus complex 47

Wielokrotne przeciążenie Konstruktor może służyć jako operator konwersji Complex Complex::operator+(Complex RHS) const return (Complex(Real + RHS.Real, Imag + RHS.Imag)); Complex:: Complex (double co) Real = co; Imag = 0; ; Complex X(4.1, 2.3); double R = 1.9; Complex Y = X + R; // Y = X.operator+(Complex(R)); Nie zadziała, gdy lewy argument jest typu double Complex X(4.1, 2.3); double R = 1.9; Complex Y = R + X; // syntax error Lepiej zdefiniować operator dwuargumentowy jako funkcję zaprzyjaźnioną 48

Wielokrotne przeciążenie Funkcja zaprzyjaźniona działa również, gdy argument typu double jest po lewej stronie friend Complex operator+(complex LHS, Complex RHS) return (Complex(LHS.Real + RHS.Real, LHS.Imag + RHS.Imag)); Complex X(4.1, 2.3); double R = 1.9; Complex Y = X + R; // Y = operator+(x,complex(r)); Complex Z = R + X; // Y = operator+(complex(r),x); Kiedy implementować operatory jako funkcje zaprzyjaźnione: Przy operacjach na podstawowych typach danych, np. Complex operator+(int LHS, const Complex& RHS); Kiedy nie można zmodyfikować klasy oryginalnej, np. ostream 49

Implementowany zbiór operatorów W wielu przypadkach dla danego typu cała kategoria operatorów ma sens Na przykład, dla klasy Complex ma sens przeciążenie wszystkich operatorów arytmetycznych. Dla klasy Name ma sens przeciążenie wszystkich operatorów relacji Często implementacje jednych operatorów mogą korzystać z implementacji innych operatorów, np: Complex operator + (Complex s1, Complex s2) Complex n (s1); return n += s2; 50

Operatory wejścia/wyjścia Nie mamy dostępu do kodu klas istream i ostream, a więc nie możemy przeciążyć operatorów << i >> jako składowych tych klas. Nie możemy ich także uczynić składowymi klasy danych, ponieważ obiekt klasy danych musiałaby wówczas znajdować się po lewej stronie operatora, a nie o to nam chodzi. Dlatego musimy zdefiniować operator<< jako osobną funkcję Funkcja ta musi mieć dostęp do składowych klasy danych, a więc zazwyczaj będzie to funkcja zaprzyjaźniona. Rozwiązaniem alternatywnym, często nieakceptowalnym, jest zdefiniowanie akcesorów dla wszystkich pól prywatnych. Sygnatura funkcji będzie miała następującą postać: ostream& operator<<(ostream& Out, const Data& towrite) 51

operator<< dla obiektów Complex Przeciążony operator<< drukuje sformatowany obiekt klasy Complex do strumienia wyjściowego ostream& operator<<(ostream& Out, const Complex& towrite) const int Precision = 2; const int FieldWidth = 8; Out << setprecision(precision); Out << setw(fieldwidth) << towrite.real; if (towrite.imag >= 0) Out << " + "; else Out << " - "; Out << setw(fieldwidth) << fabs(towrite.imag); Out << "i"; Out << endl; return Out; 52

operator>> dla obiektów Complex Przeciążony operator>> wczytuje ze strumienia wejściowego obiekt klasy Complex sformatowany w sposób używany przez operator<< istream& operator>>(istream& In, Complex& toread) char signofimag; In >> toread.real; In >> signofimag; In >> toread.imag; if (signofimag == '-') toread.imag = -toread.imag; In.ignore(1, 'i'); return In; Funkcja zależy od sposobu formatowania obiektów Complex w strumieniu wejściowym. Może być znacznie bardziej skomplikowana, jeżeli chcemy wczytywać różne formaty. 53

Przeciążenie operatora indeksowania class vector int *dane; unsigned int size; public: vector(int n); //creates n-element vector ~vector(); int& operator[] (unsigned int pos); int operator[] (unsigned int pos) const //copy constructor, assignment operator,... ; int& vector::operator[] (unsigned int pos) if (pos >= size) abort (); return dane[pos]; int vector::operator[] (unsigned int pos) const if (pos >= size) abort (); return dane[pos]; Dostarcza spodziewaną funkcjonalność, pozwalając na napisanie vector a(10); a[5]=10; cout << a[4]<<endl; 54

Przeciążenie operatorów relacji Jeżeli zamierzamy przechowywać obiekty danej klasy jako elementy kolekcji, przynajmniej część operatorów relacji powinna zostać przeciążona W celu efektywnego wyszukiwania elementów i sortowania kolekcja musi mieć możliwość porównywania przechowywanych obiektów. Może ona: Użyć funkcji akcesorów i porównywać bezpośrednio pola Użyć specjalnej metody porównującej zdefiniowanej w danej klasi Użyć przeciążonych operatorów relacji (==,!=, <, <=, >, >= w zależności od potrzeb) Pierwsze podejście wymaga, aby kolekcja posiadała szczegółowe informacje o swoich elementach Drugie podejście wymaga dostarczenia specjalnych składowych Trzecie podejście jest najbardziej naturalne i pozwala na niezależne projektowanie klas danych i kolekcji. 55