• Nie Znaleziono Wyników

Funkcja składowa zaprzyjaźniona z inną klasą

W dokumencie Język C++ – podstawy programowania (Stron 144-151)

Funkcja zaprzyjaźniona danej klasy może być też funkcją składową zu-pełnie innej klasy. Taka funkcja ma dostęp do prywatnych danych swojej klasy i do danych klasy, z którą się przyjaźni. Kolejny przykład ilustruje to zagadnienie. Mamy dwie klasy - klasę prostokat i klasę punkt. Klasa prostokat definiuje prostokąt przy pomocy współrzędnych dwóch punktów – lewego dolnego rogu prostokąta i prawego górnego rogu prostokąta. Kla-sa punkt opisuje punkt na podstawie jego współrzędnych kartezjańskich.

Mając dany punkt i dany prostokąt należy określić czy punkt znajduje się wewnątrz prostokąta czy też leży poza nim.

W programie deklarujemy dwie klasy – punkt i prostokat z konstruk-torami. Funkcja miejsce() jest funkcją składową klasy prostakat i jest za-przyjaźniona z klasą punkt. W programie testującym wywołujemy funkcję miejsce(), aby ustalić położenie punktu względem prostokąta.

Listing 6.9. Dostęp do składowych prywatnych; funkcje zaprzyjaźnione

1#include <i o s t r e a m >

#include <c o n i o >

3 using namespace s t d ;

c l a s s punkt ; // d e k l a r a c j a z a p o w i a d a j a c a

5

c l a s s p r o s t o k a t

7 { i n t xp , yp , xk , yk ; public:

9 p r o s t o k a t ( i n t xpo , i n t ypo , i n t xko , i n t yko ) ; void m i e j s c e ( punkt &p ) ;

11 } ;

c l a s s punkt

13 { i n t x1 , y1 ; public:

15 punkt (i n t ax , i n t ay ) ;

friend void p r o s t o k a t : : m i e j s c e ( punkt &p ) ;

17 } ;

6.3. Funkcja składowa zaprzyjaźniona z inną klasą 135

Po uruchomieniu tego programu mamy następujący wydruk:

punkt l e z y w p o l u

Jak widzimy deklaracja klasy prostokat ma postać:

c l a s s p r o s t o k a t

Funkcja miejsce() jest zwykłą funkcją składową klasy prostokat. W dekla-racji funkcji składowej miejsce() argumentem jest obiekt klasy punkt, dla-tego konieczna jest deklaracja zapowiadająca klasę punkt. Deklaracja klasy punkt ma postać:

c l a s s punkt { i n t x1 , y1 ;

public:

punkt (i n t ax , i n t ay ) ;

136 6. Funkcje zaprzyjaźnione

friend void p r o s t o k a t : : m i e j s c e ( punkt &p ) ; } ;

Deklaracja funkcji zaprzyjaźnionej ma postać:

friend void p r o s t o k a t : : m i e j s c e ( punkt &p ) ;

W deklaracji zaprzyjaźnionej funkcji miejsce() musimy podać nazwę klasy, w której ta funkcja jest funkcją składową, w naszym przypadku jest to klasa prostokat.

Bardzo często argumentuje się, że nie należy nadużywać funkcji zaprzy-jaźnionych, ponieważ istotą programowania obiektowego jest ukrywanie in-formacji. Funkcja zaprzyjaźniona nie jest składową klasy a mimo tego ma dostęp do danych prywatnych. W wielu jednak przypadkach zastosowanie funkcji zaprzyjaźnionych znacznie usprawnia algorytm. Klasycznym przy-kładem jest program do wykonania mnożenie wektora przez macierz. Załóż-my, że są zdefiniowane dwie klasy: wektor i macierz. Każda z nich ukrywa swoje dane i dostarcza odpowiedni zbiór operacji do działania na obiektach swojego typu. Należy zdefiniować funkcję mnożącą macierz przez wektor.

Ustalmy konkretne warunki. Niech wektor ma cztery elementy indeksowane 0,1,2,3. Wektor zapamiętywany będzie w postaci tablicy jednowymiarowej.

Macierz ma rozmiar 4x4 i będzie zapamiętywana w postaci tablicy dwuwy-miarowej. Funkcja obliczająca iloczyn musi korzystać z danych pochodzą-cych z dwóch klas, jest więc oczywiste, że musi być z nimi zaprzyjaźniona. W tym przypadku konieczne jest także użycie deklaracji referencyjnej(zwaną także deklaracją zapowiadającą, albo referencją zapowiadającą), w przypad-ku deklaracji klasy wekt musi wystąpić deklaracja klasy macierz i podobnie w deklaracji klasy macierz musi wystąpić deklaracja klasy wekt.

Jest to konieczne, gdyż w klasie wekt w deklaracji iloczyn() istnieje odwo-łanie do niezadeklarowanej jeszcze klasy macierz. Deklaracje poszczególnych klas i ich definicje zapisujemy w oddzielnych plikach. Musimy na początku opracować klasę wekt do obsługi wektorów. Deklaracja klasy wekt może mieć postać:

Listing 6.10. Iloczyn macierzy przez wektor; funkcje zaprzyjaźnione

1 // p l i k w e k t o r 1 . h , d e k l a r a c j a k l a s y wekt

3#i f n d e f _WEKTOR1_H

#define _WEKTOR1_H

5

c l a s s m a c i e r z ; // d e k l a r a c j a z a p o w i a d a j a c a

7

c l a s s wekt

9 {

6.3. Funkcja składowa zaprzyjaźniona z inną klasą 137

Podobnie w deklaracji klasy macierz musimy zastosować deklaracje zapo-wiadającą klasy wekt. Deklaracja klasy macierz do obsługi macierzy może mieć postać:

Listing 6.11. Iloczyn macierzy przez wektor; funkcje zaprzyjaźnione

// p l i k m a c i e r z . h , d e k l a r a c j a k l a s y m a c i e r z

W klasie wekt mamy deklarację funkcji składowej pokaz(), która służy do wyświetlania składowych wektora. Definicja funkcji pokaz() może mieć po-stać:

Listing 6.12. Iloczyn macierzy przez wektor; funkcje zaprzyjaźnione

1 // p l i k p o k a z . cpp , d e f i n i c j a s k l a d o w e j p o k a z ( )

#include <i o s t r e a m . h>

3#include " wektor1 . h"

5 void wekt : : pokaz ( ) { i n t i ;

7 f o r ( i =0; i < 4 ; i ++) c o u t << v [ i ] << " ␣ "; c o u t << " \n";

9 }

138 6. Funkcje zaprzyjaźnione W klasie macierz jest zadeklarowany konstruktor macierz(). Jego definicja może mieć postać:

Listing 6.13. Iloczyn macierzy przez wektor; funkcje zaprzyjaźnione

1 // p l i k konmac . cpp , d e f i n i c j a k o n s t r u k t o r a k l a s y m a c i e r z

Należy opracować funkcje zaprzyjaźnioną iloczyn(). Definicja tej funkcji (wykonuje ona mnożenie macierzy przez wektor) może mieć postać:

Listing 6.14. Iloczyn macierzy przez wektor; funkcje zaprzyjaźnione

1 // p l i k i l o c z y n . cpp

// d e f i n i c j a f u n k c j i i l o c z y n

3#include " wektor1 . h"

#include " m a c i e r z . h"

Możemy zacząć testować mnożenie macierzy przez wektor. Funkcje ilo-czyn() możemy wykorzystać do realizacji przekształceń w przestrzeni 3D. W grafice komputerowej wykorzystuje się tzw. współrzędne jednorodne. W tych współrzędnych punkt (x,y,z) reprezentowany jest jako punkt w przestrzeni 4-wymiarowej (x,y,z,1). Poszczególne przekształcenia takie jak przesuniecie, skalowanie czy obroty reprezentowane są macierzami 4x4. Aby otrzymać położenie nowego punktu P’ należy punkt początkowy P pomnożyć przez macierz przekształcenia M:

P= M • P (6.1)

6.3. Funkcja składowa zaprzyjaźniona z inną klasą 139 Przesunięcie w przestrzeni 3D ma postać:

T (dx, dy, dz) =

Operacja skalowania przedstawiana jest następująco:

S(sx, sy, sz) =

Mamy trzy macierze reprezentujące obroty wokół osi x,y i z. Obrót wokół osi z przedstawia się następująco:

Rz(θ) =

Obrót wokół osi x ma postać:

Rx(θ) =

Obrót wokół osi y ma postać:

Ry(θ) =

Te transformacje można łatwo zweryfikować: wynikiem obrotu o 90o jed-nostkowego wektora osi xh 1 0 0 1 iT powinien być jednostkowy wektor h 0 1 0 1 iT osi y.

Ogólnie mamy do czynienia z mnożeniem macierzy przez wektor:

W programie testującym wyliczymy współrzędne punktu po przekształ-ceniach.

140 6. Funkcje zaprzyjaźnione Listing 6.15. iloczyn macierzy przez wektor; funkcje zaprzyjaźnione

#include <i o s t r e a m . h>

2#include <c o n i o . h>

#include " wektor1 . h"

4#include " m a c i e r z . h"

Po uruchomieniu programu testującego otrzymamy następujący wynik:

punkt poczatkowy : 1 0 0 1 po t r a n s l a c j i : 3 3 1 1 po s k a l o w a n i u : 2 0 0 1 po o b r o c i e o PI /2 : 0 1 0 1

W dokumencie Język C++ – podstawy programowania (Stron 144-151)