Przeładowanie operatorów
" cel doprowadzenie do sytuacji, w której obiekty typów
zdefiniowanych przez u\ytkownika mo\na traktować tak
samo, jak obiekty typów wbudowanych:
typ wbudowany typ zdefiniowany
int a=5; int a[]={1, 2, 3};
int b=7; int b[]={2,1,2};
int c; int c[3];
c=a+b; //legalne c=a+b; // nielegalne
" poka\emy, \e dla klas mo\na sprawić, by operatory +, -, *,
/, % itp. pracowały zgodnie z oczekiwaniami
Przykład: klasa dla liczb zespolonych:
class zespol {
float rzecz;
float uroj;
public:
zespol(float r=0.0, float i=0.0) : rzecz(r), uroj(i) { }
float re() {return rzecz;}
float im() {return uroj;}
void ustaw_re(float r) {rzecz=r;}
void ustaw_im(float i) {uroj=i;}
};
" dodawanie liczb zespolonych dodawanie oddzielnie części
rzeczywistych i urojonych mo\e być zrealizowane jako funkcja:
zespol dodaj_zespol(zespol a, zespol b)
{
zespol c;
c.ustaw_re(a.re()+b.re());
c.ustaw_im(a.im()+b.im());
return c;
}
" mając definicję klasy i funkcję dodaj_zespol mo\emy
napisać:
zespol a(1, 1.5), b(0, -1), c;
c=dodaj_zespol(a,b); // c = (1, 0.5)
" co trzeba zrobić, by ostatnią instrukcję zapisać: c=a+b; ?
" analiza dla typów wbudowanych
o znaczek + nie wykonuje dodawania sprawia tylko
wywołanie operatora dodawania funkcji dwóch argumentów
o muszą istnieć ró\ne rodzaje operatorów dodawania dla
ró\nych typów argumentów (ró\ne reprezentacje liczb
ró\nych typów)
o mamy klasyczną sytuację przeładowania funkcji (tu:
operatora dodawania)
" wniosek: mo\emy napisać własną wersję funkcji operator
dodawania , która zostanie uruchomiona wtedy, gdy
argumenty będą np. typu zespolonego
" jak to zrobić?
o funkcja musi się nazywać: operator+(x, y) , gdzie x, y
argumenty odpowiednich typów
o poza tym funkcja mo\e być dowolna (mo\e robić
cokolwiek, to do nas nale\y taka jej realizacja, by robiła to, co
od niej oczekujemy)
o kompilator gwarantuje, \e sekwencja x + y zostanie
zastąpiona wywołaniem: operator+(z, y)
Realizacja dla klasy zespol:
zespol operator+(zespol a, zespol b)
{
zespol c;
c.ustaw_re(a.re()+b.re());
c.ustaw_im(a.im()+b.im());
return c;
}
teraz legalna sekwencja:
zespol a(1, 1.5), b(0, -1), c;
c=a+b; // c = (1, 0.5), równowa\ne wywołaniu c=operator+(a,b);
Przeładowanie operatorów zasady ogólne
" projektując klasę mo\na zadbać o to, by pewne operacje
na obiektach tej klasy były wykonywane na zasadzie
przeładowania operatorów
" przeładowanie operatora dokonuje się poprzez definicję
własnej funkcji, która
o nazywa się operator@ , gdzie @ - symbol operatora, który
chcemy przeładować (np. +, - , * , / , itp.)
o co najmniej jeden argument tej funkcji jest obiektem danej
klasy; istotne jest by to był obiekt, a nie wskaznik do obiektu
Składnia tej funkcji:
typ_zwracany operator@(argumenty)
{
// ciało funkcji
}
" mo\emy przeładować operatory:
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> <<= >>= == !=
<= >= && || ++ -- , ->* ->
new delete ( ) [ ]
" nie mo\na przeładować operatorów:
. operator odniesienia do składnika
. * wybór składnika wskaznikiem
:: operator zakresu
?: operator trójargumentowy
" przeładować mo\na tylko wypisane wy\ej operatory
" przez przeładowanie nie mo\na zmieniać:
o priorytetów wykonania operatorów
zawsze a+b*c = a + (b * c)
o liczby argumentów
+, *, / zawsze dwuargumentowe, ! zawsze jednoargumentowy
o typu łączności
a=b=c=d zawsze znaczy: a=(b=(c=d)))
" gdy funkcja operatorowa jest realizowana jako zwykła
(globalna) funkcja, to spełnione być muszą warunki:
o musi ona mieć tyle samo argumentów co operator
o co najmniej jeden argument musi być typem zdefiniowanym
przez u\ytkownika (nie mo\na przeładować operatora od
argumentów wbudowanych)
Funkcja operatorowa jako składnik klasy
" funkcja operatorowa definiująca przeładowanie mo\e być
zrealizowana jako funkcja składowa klasy jednego z
argumentów
" w porównaniu do funkcji globalnej ma o jeden argument
mniej (jest to pierwszy argument na rzecz którego ta
funkcja jest wywoływana)
c = a + b; // c=operator+(a,b); funkcja globalna
c = a + b; // c=a.operator+(b); funkcja składowa
Przykład: operator dodawania dla klasy zespol:
zespol operator+(zespol d); //deklaracja w ciele klasy
zespol zespol::operator+(zespol d) //definicja
{
zespol c;
c.ustaw_re(rzecz+d.re());
c.ustaw_im(uroj+d.im());
return c;
}
Podsumowanie
" przeładowanie ka\dego operatora mo\na zrealizować na
dwa sposoby:
o albo przez funkcję globalną
o albo przez niestatyczną funkcję składową klasy
o (nie jednocześnie bo wtedy przeładowanie
niejednoznaczne
" są cztery wyjątki, których przecią\enie musi być
zrealizowane poprzez niestatyczną funkcję składową
klasy, są to:
o = operator przypisania
o [ ] operator odwołania do elementu tablicy
o ( ) operator wywołania funkcji
o -> operator odwołania do składnika klasy
Dostęp do składników klasy
" funkcja operatorowa musi mieć zapewniony dostęp do
potrzebnych składników klasy, na rzecz której działa
" gdy operator jest składową klasy ma pełny dostęp
" gdy operator jest funkcją globalną musimy o taki dostęp
zadbać
" najczęstszy sposób dostępu deklaracja przyjazni do
funkcji operatorowej w ciele klasy
" przyjazń niekonieczna gdy dostęp zapewniony przez
publiczne metody klasy (patrz klasa zespol)
Operatory predefiniowane
" gdy w stosunku do obiektu klasy u\yjemy operatora, który
nie jest dla niej zdefiniowany, to wystąpi błąd:
float a;
zespol z1(3.5, 1), z2;
z2=a*z1; //błąd, niezdefiniowany operator* dla zespol
" jest kilka operatorów, które są automatycznie generowane
przez kompilator dla ka\dej klasy. Są to:
o = operator przypisania (składnik po składniku)
o & operator adresu obiektu klasy
o , (przecinek) działa jak dla typów wbudowanych
o new, delete kreacja i likwidacja obiektów klasy w
zapasie pamięci
" gdy stworzymy własne definicje (przeładowania) tych
operatorów, to maskują one wersje predefiniowane
" nie ma innej metody uniewa\nienia predefiniowanych
wersji operatorów
Przeładowanie operatorów o ró\nej liczbie argumentów
" przeładowanie zasadniczo operatory jedno- i
dwuargumentowe
" jedyny operator mo\liwy z większą ilością argumentów
który potencjalnie mo\e być przeładowywany operator ( )
" klasa u\yta w przykładach pomiar (gromadząca wyniki
pomiarów ró\nych parametrów w pokojach jednego
budynku)
const int ile_pokoi = 100;
class pomiar {
public:
float odczyt[ile_pokoi];
pomiar(float w=0.0); // deklaracja konstruktora
\\
\\ inne metody klasy
};
pomiar::pomiar(float w)
{
for(int k=0, k
odczyt[k]=w;
}
Operatory jednoargumentowe
" przykładowe operatory tego rodzaju: - , ! , ++ , -- , ~ , &
" zapis ich działania na typ wbudowany i zdefiniowany
int k; pomiar temp;
-k; -temp;
++k; ++temp;
&k; &temp;
na ogół są to operatory prefiksowe, ale mo\liwe tez wersje
postfiksowe:
k++; temp++;
k--; temp--;
" jednoargumentowy operator @ działający na obiekt klasy
K mo\e być zrealizowany jako:
o albo globalna funkcja z jednym argumentem
obiektem klasy K
operator@(K);
o albo funkcja składowa klasy K wywoływana bez
\adnych argumentów
" typ wyniku funkcji operatorowej zale\y od rodzaju
operatora
" dobrze jest, gdy działanie przedefiniowanego operatora na
obiekty klasy przypomina działanie oryginalnego operatora
na obiekty typu wbudowanego
Przykład 1: operator dla klasy pomiar
Dla typu wbudowanego:
float a=2.0;
cout << (-a) << endl;
cout << a << endl;
-2.0
2.0
Widać, \e operator działając na argument typu
wbudowanego:
o zwraca wartość przeciwnego znaku
o nie zmienia oryginalnej wartości
Definicja dla obiektu klasy powinna te\ mieć te cechy
1. Realizacja jako funkcja globalna:
pomiar operator (pomiar dane)
{
pomiar wynik;
for(int k=0; k wynik.odczyt[k]= dane.odczyt[k];
return wynik;
}
pomiar stary(21), nowy;
nowy = ( stary);
cout << stary.odczyt[0] << << nowy.odczyt[0];
21 -21
_____________________________________________________
Działanie:
temp operator (temp)
2. Realizacja jako funkcja funkcja składowa klasy:
pomiar pomiar::operator ( )
{
pomiar wynik;
for(int k=0; k wynik.odczyt[k]= odczyt[k];
return wynik;
}
Działanie:
temp temp.operator ();
Ró\nice: liczba argumentów (dla realizacji przez funkcję
składową argument przekazany jako wskaznik obiektu
this)
Przykład 2: operator ++ (prefiksowy) dla klasy pomiar
Dla typu wbudowanego:
float a=2.0, b;
b=++a;
cout << a << << b << endl;
3.0 3.0
_________________________________________________
Widać, \e operator działając na argument typu
wbudowanego:
o zwraca wartość powiększoną o jeden
o zmienia oryginalnej wartość w ten sam sposób
Realizacja funkcja globalna:
pomiar & operator++(pomiar &dane)
{
for(int k=0; k dane.odczyt[k]= dane.odczyt[k]+1;
return dane;
}
Realizacja funkcja składowa klasy:
pomiar & pomiar::operator++( )
{
for(int k=0; k odczyt[k]= odczyt[k]+1;
return *this;
}
Jak zrealizować wersje postfiksowe operatorów ++, ?
Trick fałszywa zmienna int:
pomiar pomiar::operator++(int )
{
for(int k=0; k odczyt[k]= odczyt[k]+1;
return *this;
}
Operatory dwuargumentowe
" przeładowania realizujemy jako:
o albo globalna funkcja z dwoma argumentami
o albo niestatyczna metoda klasy z jednym
argumentem
Przykład: dodanie do wszystkich pomiarów stałej:
Realizacja przez funkcję globalną:
pomiar operator+(pomiar d, float x)
{
pomiar wynik;
for(int k=0; k wynik.odczyt[k]= d.odczyt[k]+x;
return wynik;
}
pomiar cisnienie(990), cis_korekta;
cis_korekta=cisnienie+15.0;
cout << cisnienie.odczyt[0] << << cis_korekta.odczyt[0] << endl;
990 1005
Realizacja przez metodę klasy:
pomiar pomiar::operator+(float x)
{
pomiar wynik;
for(int k=0; k wynik.odczyt[k]= odczyt[k]+x;
return wynik;
}
Przemienność
" powy\sze definicje operatora+ nie mają wszystkich
własności operatora + dla typów wbudowanych na razie
brak im przemienności
float x,y; | pomiar c1,c2
int a; | float x;
y=a+x; | c2=c1+x; // O.K.
y=x+a; //równowa\ne | c2=x+c1; // nie zdefiniowana
Czy mo\na uczynić ten operator symetrycznym?
" zale\y to od formy przeładowania
" dla realizacji przez funkcję globalną mo\liwe; potrzebna
definicja:
pomiar operator+( float x, pomiar d)
{
pomiar wynik;
for(int k=0; k wynik.odczyt[k]= d.odczyt[k]+x;
return wynik;
}
pomiar p1(10), p2;
p2=p1+10.0; // operator+(pomiar, float)
p2=10.0+p1; // operator+(float, pomiar)
" dla metody klasy nie da się zrealizować operacji
float+pomiar; aby to było mo\liwe konieczny byłoby:
float x;
pomiar p;
x+p x.operator+(x)
ale float to nie jest klasa z mo\liwością definicji operatora
Czy przeładowanie operatorów jest konieczne?
" odpowiedz nie !!!
" jest efektownym sposobem ułatwiającym notację wyra\eń
" nie stwarza \adnych dodatkowych mo\liwości, tylko
upraszcza zapis
" wa\ne zwłaszcza, gdy piszemy klasę biblioteczną, która
ma łatwo obsługiwane przez wielu u\ytkowników
Wyszukiwarka
Podobne podstrony:
w18 przeladowanie operatory wyjatkowe
trans operation
m01 operatorchecker sowi
F 15 Układ do pomiaru czasów przełączania diody
Naprawa przełącznika kierunkowskazów
Dodatek C Kolejność operatorów
Cisco Broadband Operating System Appendix A
Operation Peiper
9 Operatory
instrukcja bhp na stanowisku operator koparko ladowarki
Or Operator koparko spycharki
language operators comparison
language operators increment
272?1105 operator koparko ladowarki
1b 2 2 4 11 Lab Konfiguracja aspektów bezpieczeństwa na przełączniku
więcej podobnych podstron