Wykład 5
Funkcje i klasy zaprzyja
ź
nione
Wska
ź
nik this
Programowanie obiektowe
1
Wska
ź
nik this
Przeci
ąż
anie operatorów
Są to takie funkcje, które mimo, że nie są metodami klasy mają dostęp do jej
składowych, czyli innych funkcji i zmiennych danej klasy
Mają dostęp także do tych składników klasy, które są hermetyzowane
etykietą private
Funkcja zaprzyjaźniona jest wprowadzana słowem kluczowym friend
Sposób stosowania:
class figura{
Funkcje zaprzyjaźnione
Programowanie obiektowe
2
class figura{
int x,y;
…….
friend void goniec(figura f)
};
Funkcja goniec(figura f) jest zdefiniowana gdzieś w programie w całkowicie
innym miejscu i nie jest funkcją składową klasy figura
W klasie figura {} chcemy z niej skorzystać nawet, jeśli przynależy ona do innej
klasy (wtedy poprawnie jest taką funkcję umieścić w sekcji public w jej klasie).
Cechy funkcji zaprzyjaźnionych:
Funkcja może być zaprzyjaźniona z kilkoma klasami
Na argumentach jej wywołania może wykonywać operacje zgodnie ze
swoją definicją
Nie musi być funkcją składową żadnej klasy, ale może nią być
Funkcje zaprzyjaźnione
Programowanie obiektowe
3
Nie musi być funkcją składową żadnej klasy, ale może nią być
Może być napisana w zupełnie innym języku niż C++
Jest deklarowana w klasie ze słowem kluczowym friend i nie podlega
etykietom hermetyzacji (public, private, protected)
Może być zdefiniowana w innej klasie i wtedy jest typu inline, ale nadal
jest funkcją zaprzyjaźnioną.
Klasa może się przyjaźnić z wieloma funkcjami, które są lub nie są
składnikami innych klas;
Funkcje zaprzyjaźnione nie są przechodnie, czyli zaprzyjaźnienie nie
przenosi
się
z
klasy
do
klasy,
tzn.
zaprzyjaźnienie
nie
podlega
mechanizmowi dziedziczenia (w tym przypadku „przyjaciel mojego
Funkcje zaprzyjaźnione
Programowanie obiektowe
4
mechanizmowi dziedziczenia (w tym przypadku „przyjaciel mojego
przyjaciela nie jest moim przyjacielem” );
Z zasady umieszcza się funkcje zaprzyjaźnione na początku wszystkich
deklaracji w klasie;
Dopuszcza się stosowanie słowa kluczowego friend do definiowania klas
zaprzyjaźnionych;
W takim przypadku wszystkie funkcje składowe klasy zaprzyjaźnionej mają
dostęp do wszystkich (także prywatnych) składników drugiej klasy.
Klasy zaprzyjaźnione
Programowanie obiektowe
5
Przykład programowy
Program 4.1
Funkcje i klasy zaprzyjaźnione
Programowanie obiektowe
6
Program 4.2
Wskaźnik this
Podczas wywoływania funkcji składowej jest do niej automatycznie
przekazywany niejawny wskaźnik do obiektu, na rzecz którego realizowane
jest wywołanie funkcji;
Wskaźnik ten nazywa się this
Funkcja musi odszukać obiekt, aby wykonać operację na jego rzecz. Robi to
Programowanie obiektowe
7
Program 4.3
tak, że korzysta z niejawnego wskaźnika this, który jest inicjowany w
momencie pojawienia się operatora kropki po nazwie obiektu.
Wskaźnik this wskazuje, na którym egzemplarzu (konkrecie) obiektów klasy
funkcja ma wykonać swoje czynności.
Przykład
Wskaźnik this
pwr::pwr(double base, int exp)
{
b = base;
e = exp;
val = 1;
if(exp==0) return;
for( ; exp>0; exp--) val = val * b;
Programowanie obiektowe
8
for( ; exp>0; exp--) val = val * b;
}
pwr::pwr(double base, int exp)
{
this->b = base;
this->e = exp;
this->val = 1;
if(exp==0) return;
for( ; exp>0; exp--)
this->val = this->val * this->b;
}
Ponieważ funkcja typu friend nie jest składnikiem klasy, to nie ma
wskaźnika this czyli musi się posłużyć operatorem jawnego wskaźnika lub
przypisania, aby wykonać działania (także te na składniku klasy, z którą jest
zaprzyjaźniona).
Funkcje zaprzyjaźnione a wskaźnik this
Programowanie obiektowe
9
Referencje
Język C++ posiada dodatkową cechę związana ze wskaźnikami - referencję;
Zasadniczo referencja to niejawny wskaźnik;
Można ja wykorzystywać na trzy sposoby:
Programowanie obiektowe
10
1)
jako parametr funkcji,
2)
jako zwracaną wartość,
3)
jako zmienną referencyjną.
Referencje jako parametry
W momencie wywołania funkcji jej parametry mogą być przekazywane na
dwa sposoby: przez wartość i przez referencję;
Podczas przekazywania parametru przez wartość do funkcji przekazywana
jest kopia argumentu;
Programowanie obiektowe
11
jest kopia argumentu;
Przykład:
Program 4.4
Referencje jako parametry
Ważnym zastosowaniem referencji jest możliwość definiowania funkcji,
wykorzystujących mechanizm przekazywania parametrów przez referencję;
Przekazywanie przez referencję polega na przekazaniu do funkcji wskaźnika
do argumentu; można to realizować na dwa sposoby:
Programowanie obiektowe
12
do argumentu; można to realizować na dwa sposoby:
jawne przekazanie wskaźnika do parametru;
posłużenie się się tzw. parametrem referencyjnym;
Referencje jako parametry
Przykład jawnego przekazania wskaźnika do parametru
Program 4.5
Programowanie obiektowe
13
Program 4.6
Przykład użycia parametru referencyjnego
Przekazywanie obiektów przez referencję
Przekazywanie obiektu jako argumentu funkcji polega na przekazaniu jego
kopii;
Po zakończeniu funkcji kopia jest niszczona i wywoływany jest destruktor
tej kopii;
Jeśli nie chcemy, aby destruktor był uaktywniany, możemy przekazać obiekt
Programowanie obiektowe
14
Jeśli nie chcemy, aby destruktor był uaktywniany, możemy przekazać obiekt
do funkcji przez referencję;
Podczas przekazywania obiektu przez referencję kopia obiektu nie jest
tworzona (w konsekwencji nie ma jej niszczenia i nie jest wywoływany
destruktor obiektu);
Przykład
Program 4.7b
c:
Program 4.7a
c:
Zwracanie referencji
Funkcja może zwracać referencję;
W konsekwencji funkcja może występować z lewej strony instrukcji
przypisania!
Przykład
Programowanie obiektowe
15
Program 4.8
Zmienne referencyjne
Referencję można zastosować jako samodzielna zmienną;
Zmienna referencyjna jest tworzona jako nowa nazwa dla istniejącej
zmiennej (tzw. przezwisko);
Podczas tworzenia zmiennej referencyjnej obowiązkowa jest jej inicjacja;
Przykład
Programowanie obiektowe
16
Przykład
Program 4.9
Przeciążanie operatorów
Z przeciążaniem funkcji ściśle związane jest zagadnienie przeciążania
operatorów
W języku C++ można przeciążyć większość operatorów tak, aby
Programowanie obiektowe
17
W języku C++ można przeciążyć większość operatorów tak, aby
wykonywały zadania charakterystyczne dla danej klasy, np. dla klasy stos
operator + może być wykorzystany do dodawania elementu na stos, a
operator – do zdejmowania elementu ze stosu
Po przeciążeniu odpowiednich operatorów można posługiwać się obiektami
w wyrażeniach tak samo jak zmiennymi typów wbudowanych
Przeciążanie operatorów
Operator można przeciążyć tworząc tzw. funkcję operatora :
będącą składową klasy,
nie będącą składową klasy, ale będącą funkcją zaprzyjaźnioną (z klasą)
Programowanie obiektowe
18
Funkcja operatora definiuje, jakie operacje na obiektach wskazanej klasy ma
wykonywać operator
Do tworzenia funkcji operatora służy słowo kluczowe operator
Funkcja operatora może, ale nie musi, być składową klasy
Przeciążanie operatorów
Ogólna składnia funkcji operatora, będącej składową klasy:
zwracany_typ nazwa_klasy::operator # (lista_argumentów)
{
//operacje
symbol operatora
Programowanie obiektowe
19
}
Lista argumentów może być pusta (w przypadku operatorów
jednoargumentowych)
Przykład
Program 4.10
Przeciążanie operatorów
// Przeciążenie operatora + dla klasy loc
loc loc::operator+(loc op2) {
loc temp;
temp.longitude = op2.longitude + longitude;
temp.latitude = op2.latitude + latitude;
return temp;
Programowanie obiektowe
20
return temp;
}
int main() {
loc ob1(10, 20), ob2( 20, 30);
ob1.show();
// wyświetla 10 20
ob2.show();
// wyświetla 20 30
ob3 = ob1 + ob2;
...
operand przekazywany przez wskaźnik this
operand przekazywany przez parametr op2
Przeciążanie operatorów
W kolejnym programie przykładowym przeciążone zostaną trzy operatory
dwuargumentowe (+,
−
, =) oraz jeden operator jednoargumentowy (++)
w formie przedrostkowej
Przykład
Programowanie obiektowe
21
Program 4.11
Operatory przyrostowe - przypomnienie
Przykład
#include <iostream>
using namespace std;
int main(void) {
int i, j;
i = 10;
Programowanie obiektowe
22
i = 10;
j = i++;
/* wy
ś
wietlenie warto
ś
ci zmiennych i oraz j */
cout << "i=" << i;
cout << ", j=" << j;
getchar();
// Zatrzymanie okna konsoli
return 0;
}
Program 4.12
// Najpierw przypisanie, potem inkrementacja
Przykład
#include <iostream>
using namespace std;
int main(void) {
int i, j;
i = 10;
Operatory przyrostowe - przypomnienie
Programowanie obiektowe
23
i = 10;
j = ++i;
/* Wy
ś
wietlenie warto
ś
ci zmiennych i oraz j */
cout << "i=" << i;
cout << ", j=" << j;
getchar();
// Zatrzymanie okna konsoli
return 0;
}
Program 4.13
// Najpierw inkrementacja, potem przypisanie
Inkrementacja w formie przedrostkowej
typ operator++() {
// ciało operatora w formie przedrostkowej
}
Inkrementacja w formie przyrostkowej
typ operator++(int x) {
// ciało operatora w formie przyrostkowej
Formy przedrostkowe i przyrostkowe przeciążonych operatorów
inkrementacji i dekrementacji
Programowanie obiektowe
24
// ciało operatora w formie przyrostkowej
}
Dekrementacja w formie przedrostkowej
typ operator
−−−− −−−−
() {
// ciało operatora w formie przedrostkowej
}
Dekrementacja w formie przyrostkowej
typ operator
−−−− −−−−
(int x) {
// ciało operatora w formie przyrostkowej
}
Program 4.14
Przeciążanie operatorów za pomocą funkcji zaprzyjaźnionych
Operator można przeciążyć także posługując się funkcjami nie będącymi
składowymi klasy
W tym celu najczęściej stosuje się funkcje zaprzyjaźnione
Funkcje zaprzyjaźnione nie są składowymi klasy, nie jest więc do nich
przekazywany wskaźnik this
Dlatego do zaprzyjaźnionej funkcji operatora operandy przekazywane są w
sposób jawny
Programowanie obiektowe
25
sposób jawny
Oznacza to, że funkcja zaprzyjaźniona służąca do przeciążania operatora
dwuargumentowego
ma
dwa
parametry,
a
funkcja
zaprzyjaźniona
przeciążająca operator jednoargumentowego ma jeden parametr
Podczas przeciążania operatora dwuargumentowego za pomocą funkcji
zaprzyjaźnionej lewy operand przekazywany jest jako pierwszy, a prawy –
jako drugi
Przykład
Program 4.15
Programowanie obiektowe
26