Funkcje i klasy zaprzyjaźnione
Przeciążanie operatorów
Szablony funkcji
Szablony klas
Funkcje wirtualne
Programowanie obiektowe
1
Są to takie funkcje, które mimo, że nie są częścią klasy mają dostęp do jej składników czyli innych funkcji, zmiennych i obiektów
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{
int x,y;
…….
friend void goniec(figura f)
};
Programowanie obiektowe
2
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ą
Może być napisana w zupełnie innym języku niż C++ i dlatego może
nie być funkcją składową klasy.
Programowanie obiektowe
3
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).
Jest deklarowana w klasie ze słowem kluczowym friend i nie podlega
etykietom hermetyzacji ( public, private, protected)
Może być cała zdefiniowana w klasie i wtedy jest typu inline, ale nadal jest funkcją zaprzyjaźnioną.
Nie musi być funkcją składową żadnej klasy, ale może nią być
Programowanie obiektowe
4
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
przyjaciela nie jest moim przyjacielem” );
Z zasady umieszcza się funkcje zaprzyjaźnione na początku wszystkich deklaracji w klasie;
Przykład programowy
Program 3.6
Programowanie obiektowe
5
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 prywatnych składników drugiej klasy.
Przykład programowy
Program 3.7
Programowanie obiektowe
6
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:
1)
jako parametr funkcji,
2)
jako zwracaną wartość,
3)
jako zmienną referencyjną.
Programowanie obiektowe
7
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;
Przykład:
Program 3.8
Programowanie obiektowe
8
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:
jawne przekazanie wskaźnika do parametru;
posłużenie się się tzw. parametrem referencyjnym;
Programowanie obiektowe
9
Przykład jawnego przekazania wskaźnika do parametru
Program 3.9
Przykład użycia parametru referencyjnego
Program 3.10
Programowanie obiektowe
10
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 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
c:
Program 3.11
Programowanie obiektowe
11
Funkcja może zwracać referencję;
W konsekwencji funkcja może występować z lewej strony instrukcji przypisania!
Przykład
Program 3.12
Programowanie obiektowe
12
Referencję można zastosować jako samodzielną zmienną;
Zmienna referencyjna jest tworzona jako nowa nazwa dla istniejącej zmiennej (przezwisko);
Podczas tworzenia zmiennej referencyjnej obowiązkowa jest jej inicjacja;
Przykład
Program 3.13
Programowanie obiektowe
13
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 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
Operator można przeciążyć tworząc funkcję operatora
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
Funkcje operatora nie będące składowymi klasy są najczęściej jej funkcjami zaprzyjaźnionymi
Programowanie obiektowe
14
Ogólna składnia funkcji operatora , będącej składową klasy: zwracany_typ nazwa_klasy::operator#(lista_argumentów)
{
symbol operatora
//operacje
}
Lista argumentów może być pusta (w przypadku operatorów
jednoargumentowych)
Przykład
Program 4.1
Programowanie obiektowe
15
// 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;
}
int main() {
loc ob1(10, 20), ob2( 20, 30);
ob1.show(); // wyświetla 10 20
ob2.show(); // wyświetla 20 30
ob1 = ob1 + ob2;
operand przekazywany przez parametr op2
...
operand przekazywany przez wskaźnik this
Programowanie obiektowe
16
W kolejnym programie przykładowym przeciążone zostaną trzy operatory dwuargumentowe (+, −, =) oraz jeden operator jednoargumentowy (++)
Przykład
Program 4.2
Programowanie obiektowe
17
Operatory przyrostowe - przypomnienie
Przykład
#include <stdio.h>
int main(void) {
int i, j;
i = 10;
j = i++;
// Najpierw przypisanie, potem inkrementacja
/* wyświetlenie wartości zmiennych i oraz j */
printf("i = %d",i);
printf(", j= %d", j);
getchar();
return 0;
}
Program 4.3
Programowanie obiektowe
18
Operatory przyrostowe - przypomnienie
Przykład
#include <stdio.h>
int main(void) {
int i, j;
i = 10;
j = ++i;
// Najpierw inkrementacja, potem przypisanie
/* Wyświetlenie wartości zmiennych i oraz j */
printf("i = %d",i);
printf(", j= %d", j);
getchar();
return 0;
}
Program 4.4
Programowanie obiektowe
19
Formy przedrostkowe i przyrostkowe przeciążonych operatorów inkrementacji i dekrementacji
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
}
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
}
Programowanie obiektowe
20
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
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.5
Programowanie obiektowe
21
Szablony są jedną z najbardziej wyrafinowanych i najbardziej efektywnych cech języka C++
Zostały dodane do specyfikacji języka zaledwie kilka lat temu
Dzięki szablonom możliwe jest tworzenie ogólnych definicji funkcji i klas
W ogólnych funkcjach lub klasach typ danych, na których operuje funkcja lub klasa jest określany jako parametr
W konsekwencji można stworzyć funkcję lub klasę, przystosowaną do pracy z kilkoma różnymi typami danych, bez konieczności jawnego
redefiniowania specyfikacji dla poszczególnych typów
Funkcja może być zatem w pewnym stopniu inteligentna: może
samodzielnie dobrać zarówno typ argumentów, jak typ wyniku samej
funkcji!
Programowanie obiektowe
22
Funkcje ogólne tworzymy posługując się słowem kluczowym template
template <class Ttype > typ_zwracany nazwa_funkcji(lista parametrów)
{
// ciało funkcji
}
gdzie:
Ttype – symboliczna nazwa typu danych, wykorzystywanych przez funkcję (podczas tworzenia konkretnej wersji funkcji kompilator
zamienia ten typ na rzeczywisty typ danych)
class - słowo kluczowe konieczne, aby powstał szablon;
Uwaga:
Jeśli parametrów szablonu jest więcej niż jeden, to po przecinku powtarzamy dla słowo class i podajemy kolejną nazwę parametru; Programowanie obiektowe
23
Funkcje ogólne sa podobne do funkcji przeciążonych, ale podczas pracy z nimi obowiązuje więcej ograniczeń
Nazwa szablonu musi być zdefiniowana w zakresie globalnym, czyli na zewnątrz wszystkich funkcji i klas.
Parametr szablonu musi wystąpić jako typ argumentu funkcji definiowanej tym szablonem.
WNIOSEK: nie można wprost zadać szablonem funkcji o typie innym niż
typ jej argumentów; jest to możliwe poprzez specjalne obejście ograniczeń języka.
Parametrem szablonu funkcji może być typ obiektu czyli nazwa klasy.
Definicja szablonu nie powoduje jeszcze utworzenia funkcji szablonowej.
Zostanie ona utworzona dopiero po wystąpieniu w kodzie nazwy tej funkcji. Utworzenie odbędzie się zgodnie z „recepturą” zapisaną w szablonie.
Programowanie obiektowe
24
Argumenty są do funkcji wstawiane takie jak przy jej wywołaniu w kodzie, czyli to wtedy jest użyty ich właściwy typ i utworzony zgodnie z funkcją jej typ.
Nazwa funkcji szablonowej nie powinna pokrywać się z innymi nazwami zmiennych i funkcji globalnych.
Operacja zapisana w funkcji poddana jest takim samym regułom na poprawność operacji jak we wszystkich innych funkcjach, czyli nie wykona się na przykład operacja arytmetyczna na zmiennych znakowych.
Parametr szablonu – nazwany podczas deklarowania szablonu – nie musi mieć tej samej nazwy podczas definiowania szablonu.
Dwa szablony o takiej samej nazwy mogą wystąpić w tym samym kodzie; należy tylko uważać, aby jeden nie był szczególnym przypadkiem drugiego (wówczas linker wiedział byz którego ma skorzystać)
Programowanie obiektowe
25
Szablon wstawiając do wzorca funkcji różne typy argumentów realizuje tak naprawdę przeciążenie nazwy funkcji.
Sam szablon też może być przeciążony czyli w tym samym zakresie ważności może wystąpić dwa lub kilka razy z tą sama nazwą, ale różnymi parametrami, np.:
template <class typ> void funkcja (typ, int);
template <class typ> void funkcja (typ, int, float, char);
Programowanie obiektowe
26
Szablon jest deklarowany globalnie, ale stosowany lokalnie; dlatego nie powinien używać nazw zmiennych globalnych
UWAGA: szablony ciągle są nowością: w różnych środowiskach języka C++, a szczególnie w zakresie pracy linkerów mogą występować problemy
W szablonie możemy stosować przydomki inline, extern, static.
Pamiętajmy jednak, że słowo to odnosi się ostatecznie nie do szablonu ale do funkcji, która wg tego szablonu zostanie zbudowana:
template <class bb> inline bb wieksza (bb z1, bb z2)
{
return (z1>z2)? z1:z2;
}
Programowanie obiektowe
27
Funkcje ogólne (szablony funkcji) są bardzo użyteczne
Można je stosować w tych sytuacjach, kiedy różne funkcje wykorzystują ten sam ogólny algorytm
Przykład: funkcja ogólna, realizująca sortowanie bąbelkowe
Program 4.6
Programowanie obiektowe
28
Oprócz funkcji ogólnych (szablonów funkcji) w języku C++ można także definiować szablony klas (klasy ogólne)
W takim przypadku tworzona klasa zawiera definicje wszystkich
wykorzystywanych przez nią algorytmów, jednak typ danych, na których klasa operuje, zostanie przekazany do niej jako parametr dopiero w chwili tworzenia obiektów
Programowanie obiektowe
29
Ogólna składnia szablonu klasy:
template <class Ttype > class nazwa_klasy
{
// ciało klasy
}
gdzie Ttype oznacza nazwę typu, który zostanie określony podczas tworzenia obiektu (egzemplarza klasy)
Można zdefiniować więcej niż jeden typ ogólny: wówczas kolejne typy są oddzielane od siebie przecinkami;
Po utworzeniu klasy ogólnej można stworzyć obiekt (egzemplarz klasy), posługując się składnią:
nazwa_klasy <typ_rzeczywisty> nazwa_obiektu;
Programowanie obiektowe
30
Przykład: stos ogólny
Program 4.7
Przykład: szablon klasy z dwoma typami ogólnymi
Program 4.8
Przykład: szablon klasy: ogólna, bezpieczna tablica
Program 4.9
Programowanie obiektowe
31
Polimorfizm jest obsługiwany przez język C++ zarówno na etapie
kompilacji (przeciążanie funkcji i operatorów), jak i na etapie wykonania (dziedziczenie i funkcje wirtualne)
Funkcja wirtualna to metoda składowa zadeklarowana w klasie bazowej a zdefiniowana w klasie pochodnej
Aby utworzyć funkcję wirtualną należy jej deklarację w klasie bazowej poprzedzić słowem kluczowym virtual
Zasadę, pozwalającą funkcjom wirtualnym realizować polimorfizm
określamy w skrócie jako „jeden interfejs, wiele metod”
W klasie bazowej ma miejsce definicja intejfejsu, każda definicja funkcji wirtualnej w klasie pochodnej to implementacja funkcji specyficznych dla danej klasy pochodnej
Programowanie obiektowe
32
Funkcję wirtualną można wywoływać w „normalny sposób”, tzn.
wykorzystując nazwę obiektu i operator kropki
Do wskazywania obiektów klas pochodnych można wykorzystać wskaźnik ich klasy bazowej
Wówczas kompilator C++ decyduje o wyborze wersji funkcji na podstawie typu obiektu wskazywanego przez ten wskaźnik; wybór odbywa się na
etapie wykonania programu
Oznacza to, że wskazanie różnych obiektów spowoduje wskazanie różnych wersji funkcji wirtualnej
Przykład
Program 4.10
Programowanie obiektowe
33
Atrybut virtual jest dziedziczony
Oznacza to, że bez względu na to, ile razy jest dziedziczona funkcja wirtualna pozostaje ona funkcją wirtualną
Przykład
Program 4.11
Programowanie obiektowe
34
Funkcje wirtualne są hierarchiczne
Funkcja zadeklarowana w klasie bazowej jako virtual może być przesłonięta w klasie pochodnej lub nie;
Jeśli w klasie pochodnej funkcja wirtualna nie zostanie przesłonięta, wtedy obiekty tej klasy odwołujące się do takiej funkcji wirtualnej będą
wykorzystywały funkcję zdefiniowana w klasie bazowej
Przykład
Program 4.12
Programowanie obiektowe
35
Przykład:
konwersja litrów na galony i temperatury w stopniach Fahrenheita na
stopnie Celsjusza z wykorzystaniem funkcji wirtualnych
Program 4.13
Programowanie obiektowe
36
37