w05-twisei-this
Transkrypt
w05-twisei-this
Programowanie obiektowe w C++ Wykład 05 Temat wiodący: Funkcje zaprzyjaźnione i operatory this n n n n n dostępny w każdej niestatycznej metodzie jest to wskaźnik do obiektu na rzecz którego wywołano metodę można go wykorzystać jeżeli mamy zwrócić wskaźnik bądź referencję do obiektu na rzecz którego wywoływana jest metoda nie zajmuje pamięci nie wymaga deklarowania 1 this person & person::timeTravel(int years) { age += years; return *this; } person Boss(40, „John”, „Kovalsky”); ( Boss.timeTravel(+20) ).timeTravel(-30) friend n po polsku zaprzyjaźnianie n jest to deklaracja znosząca ograniczenia dostępu do składowych klasy przez ściśle określony kod 2 friend class A { friend C::theMethodNoArg(); // dotyczy konkretnej metody, tu bezargumentowej friend class B; // dotyczy całej deklaracji klasy B // również deklaracje wewnątrz B mają dostęp // do pól prywatnych A friend fun(); // dotyczy konkretnej funkcji, tu bezargumentowej } friend n zaprzyjaźnianie nie podlega dziedziczeniu n zaprzyjaźnianie nie jest przechodnie n deklarowane w dowolnej sekcji (nawet prywatnej) klasy 3 friend class M { friend void f() {… } }; jest równoważne class M { friend void f(); }; void f() {… } Operatory n Co to jest operator? a = b.val >> 4 + c * d[ e++ ] 4 Operatory n operator definiuje operacje wykonywane na swoich argumentach i typ wyniku takiej operacji – analogicznie do funkcji. n w C++ można definiować znaczenie operatorów w przypadku wykorzystania ich do obiektów (przynajmniej jeden argument obiektowy). Operatory n Można przeciążać większość operatorów n Przeciążanie może poprawić wydajność n Nie ma obowiązku a zwykle nie ma i potrzeby definiowania wielu operatorów, jeżeli operator byłby sporadycznie używany to zaleca się deklarowanie go jako metody bądź zwykłej funkcji. 5 Operatory które można przeciążać () [] -> + - ++ -- ! ~ * & new delete (typ) ->* */% +<< >> < <= > >= == != & ^ | && || = += -= *= /= %= &= ^= |= <<= >>= // jednoargumentowe // op. przypisania Operatory n nie można przeciążać operatorów: . n :: ?: sizeof operatory, które mogą być jedynie metodami klasy: () n .* [] -> new delete (typ) = operatory domniemane, jeżeli nie zdefiniowane: = & 6 Łączność n operatory przypisania i jednoargumentowe są prawostronnie łączne a=b=c n oznacza a=(b=c) pozostałe lewostronnie a+b+c oznacza (a+b)+c Kolejność wartościowania n kolejność wartościowania argumentów niezdefiniowana, za wyjątkiem: , || && n „ , ”, „ || ”, „ && ” najpierw wartościowany lewy argument, potem prawy n dla operacji logicznych stosowane jest skrócone wartościowanie. 7 Operatory a metody n inna składnia deklaracji, definicji i wywołania n operatory nie mogą mieć argumentów domyślnych (wyjątek: operator wywołania funkcji) n określona jest liczba argumentów (wyjątek: operator wywołania funkcji) n przyjęte jest zwyczajowe znaczenie operatorów Operatory n Chcesz += to zdefiniuj go, nie wystarczy zdefiniować + i = n Chcesz ++ to zdefiniuj go, kompilatorowi nie wtstarczy + i = n Zachowaj wierność zwyczajowemu znaczeniu operatorów n n niech jednoarg. & zwraca adres Zachowaj konsekwencję n ++i; i++; i+=1; i=i+1; // powinny dawać taki sam efekt. 8 Operatory jako metody n pierwszy argument będzie obiektem klasy metoda ma mniej argumentów niż operator n deklarowanie n int point::operator<=(point); n wywoływanie point point1, point2; int i= point1 <= point2; int j= point1.operator<=(point2); Operatory jako funkcje n przynajmniej jeden z argumentów musi być obiektem wszystkie argumenty są parametrami funkcji operatorowej nie ma this n deklarowanie n n int operator<=(point, point); n wywoływanie point point1, point2; int i = point1 <= point2; int j= operator<=(point1, point2); 9 Konwencja dla operatorów ++ i -n operator++() i operator--() są przedrostkowe ++i --i n operatory przyrostkowe deklarujemy i definiujemy z dokładnie jednym nienazwanym parametrem int: operator++(int); operator--(int); i++ i-- Konwencja dla operatorów ++ i -class X { public: X& operator++(); X operator++(int); }; // prefix ++a // postfix a++ class Y { public: }; Y& operator++(Y&); Y operator++(Y&, int); // prefix ++b // postfix b++ 10 Konwencja dla operatorów ++ i -void f(X a, Y b) { ++a; // a.operator++(); a++; // a.operator++(0); ++b; // operator++(b); b++; // operator++(b, 0); a.operator++(); // odpowiada ++a; a.operator++(0); // odpowiada a++; operator++(b); // odpowiada ++b; operator++(b, 0); // odpowiada b++; } Operatory class X // składowe z niejawnym this { X* operator&(); // przedrostkowy, jednoargumentowy & X operator&(X); // dwuargumentowy X operator++(int); // przyrostkowy // X operator&(X, X); // blad trzyargumentoowy // X operator/(); // blad jednoarg. }; // funkcje globalne, bez this, często zaprzyjazniane X operator-(X); // przedrostkowy X operator-(X, X); // dwuargumentowy X operator--(X&, int); // przyrostkowy dekrementacja // X operator-(X, X, X); // blad trzyargumentoowy // X operator/(); // blad jednoarg. 11 Operator przypisania n Musi być zdefiniowany jako metoda (niestatyczna) a nie jako funkcja T& T::operator=(const T &) n Nie ma listy inicjalizacyjnej bo to nie jest konstruktor !!! Operator przypisania person& person::operator=(const person &o) { if (this == &o) // Tom = Tom; return *this; delete name; name=new char[strlen(o.name) + 1]; strcpy(name, o.name); delete lastName; lastName=new char[strlen(o.lastName) + 1]; strcpy(lastName, o.lastName); age=o.age; return *this; // zwraca obiekt, będzie przekazany przez referencje } 12 Operator przypisania n dlaczego operator przypisania zwraca referencję? n chcemy napisać o1=o2=o3; person& operator=(const person&); n przypisania do: o2 z: o3 n operator przypisania do: o1 z: (o2=o3) person operator=(const person&); n przypisania do: o2 z: o3 n konstruktor kopiujący (powstaje tymczasowy obiekt t1) n operator przypisania do: o1 z: t1 n konstruktor kopiujący (powstaje tymczasowy obiekt t2) n destruktor t2 n destruktor t1 Operator przypisania n dlaczego operator przypisania zwraca referencję? n n chcemy napisac o1=o2=o3; void operator=(const person&); n n można o2=o3; nie da się o1=o2=o3; 13 Operator przypisania generowany automatycznie n jeżeli się nie zdefiniuje operatora przypisania to kompilator dostarczy własny operator przypisania, który skopiuje obiekt pole po polu. n n n nie będzie działał dla pól stałych nie będzie działał dla dla referencji dla wskaźników skopiuje wartość Operatory strumieniowe n Wejście friend istream & operator >>(istream &, T &); n Wyjście friend ostream &operator <<(ostream &, const T &); 14 Operatory strumieniowe class point { int x, y; public: … friend istream &operator >>(istream & s, point & p); friend ostream &operator <<(ostream & s, const point &); } point p, p2; cin>>p; cin>>p>>p2; cout<<p2<<p; Operatory strumieniowe istream & operator >>(istream & s, point & p) { s >> p.x >> p.y; return s; } ostream &operator <<(ostream & s, const point & p) { s << p.x << p.y; return s; } 15 Operator wyłuskania T* operator->(); n n n n T t; t->m jest interpretowane jako: ( t.operator-> )->m można używać zmiennej jak wskaźnika jeżeli operator-> nie zwraca wskaźnika to n n można użyć go: a=t.operator->(); nie można: a=t->; bo to błąd składniowy Operator wywołania funkcji wynik T::operator()(args); n n n Dowolna liczba argumentów, można przeciążać Wyjątek: tylko ten operator może mieć domyślne argumenty potem można obiekt używać tak, jak funkcję T ob; ob(); n stosuje się gdy n n nie istnieje odpowiedni operator, a by się przydał jest jakś metoda dominująca dla klasy (będzie bez nazwy ale łatwiejsza do wywołania – np. klasa counter i metoda inkrementacja) 16 operator indeksowania wynik T::operator[](index); n typy wyniku i indeksu dowolne ale zdrowy rozsądek nakazuje indeks całkowity n można tworzyć inteligentne agregaty, Operator konwersji n operator konwersji tworzy obiekt określonego typu lub klasy z obiektu na rzecz którego jest wywoływany T::operator X(); n operator konwersji klasy T do typu X: n n n nie określa się jawnie typu wartości zwracanej, jest bezargumentowy musi być metodą (nie funkcją) 17 Operator konwersji n np.: person::operator int(); n konwersja może następować niejawnie: person o; int i=o+5; n W wyrażeniach n n n dla danego obiektu konwersja jest stosowana tylko jeżeli jest konieczna, może być tylko jedna konwersja danego składnika definiowana przez użytkownika nie może być niejednoznaczności Operator konwersji n np. dla klas: A, B i C, zdefiniowano operatory konwersji B::operator A() i C::operator B(); int f(A a); A a; B b; C c; f(a); f(b); // f(c); // ok.. // f(A(b)) // nie ma operatora C::operator A() // nie zrobi się niejawnie f(A(B(c))) // (2 konwersje skladnika) 18 Operator konwersji n aby przekształcić obiekt jednego typu w obiekt innego typu można jawnie użyć konstruktora, ale: n n nie da się przeprowadzić przekształcenia do typu podstawowego (np. int, bo to nie klasa), nie da się przekształcić do starej klasy bez modyfikowania jej definicji. Przykład class counter { int cnt; public: counter(int = 0):cnt(0){}; int operator()(int =1); operator int(); operator double(); counter &operator+=(int); friend counter operator+(int, counter); counter operator+(int); int operator++(); int operator++(int); }; // increment by arg. or by 1 // convert to int // convert to double // int+counter, friend // counter+int // ++counter // counter++ 19 Przykład inline int counter::operator ()(int i) { return cnt+=i; }; inline counter::operator int() { return cnt; }; inline counter::operator double() { return double(cnt); }; Przykład counter & counter::operator +=(int i) { cnt+=i; // operator()(i); (*this)(i) return *this; }; counter operator+(int i, counter l) { l+=i; // zaprzyjaźnianie nie było konieczne return l; }; 20 Przykład counter counter::operator +(int i) { counter l=*this; l+=i; return l; }; int counter::operator ++() { cnt+=1; return cnt; }; int counter::operator ++(int) { int ret=cnt; cnt+=1; return ret; }; Przykład class complex { double re, im; public: complex(double re, double im):re(re), im(im) {}; complex & operator = (const complex &c); complex & operator += (const complex &c); complex operator + (const complex &); friend complex & operator -= (complex &, const complex &); friend complex operator - (const complex &, const complex &); friend istream & operator >>(istream &, complex &); friend ostream & operator <<(ostream &, const complex &); }; 21 Przykład complex & complex::operator = (const complex &c) { re=c.re; im=c.im; return *this; }; Przykład complex & complex::operator += (const complex &c) { re+=c.re; im+=c.im; return *this; }; inline complex complex::operator + (const complex & c) { return complex(re+c.re, im+c.im); // complex(double, double) } 22 Przykład inline complex & operator-=(complex & c1, const complex & c2) { c1.re-=c2.re; c1.im-=c2.im; //friend return c1; } complex operator-(const complex & c1, const complex & c2) { complex c=c1; c-=c2; //friend return c; } Przykład istream & operator >>(istream & s, complex & c) { s >> c.re >> c.im; return s; } ostream &operator <<(ostream & s, const complex & c) { s << c.re << c.im; return s; } 23