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