• Nie Znaleziono Wyników

Funkcja operatorowa w postaci niezależnej funkcji

W dokumencie Język C++ – podstawy programowania (Stron 164-171)

Ta definicja jest zrozumiała, ale możemy ją uprościć.

W bardziej wydajnej implementacji nie tworzymy lokalnego obiektu tym-czasowego (ulamek ulamek12 ):

ulamek ulamek : : operator ∗ ( ulamek x ) { return ulamek ( l i ∗ x . l i , mia ∗ x . mia ) ; }

7.5. Funkcja operatorowa w postaci niezależnej funkcji

Jak już mówiliśmy funkcja operatorowa może być implementowana jako funkcja składowa klasy albo jako zwykła funkcja globalna. Należy pamiętać, że zwykła funkcja nie ma dostępu bezpośredniego do danych prywatnych.

Jeżeli chcemy, aby funkcja globalna miała dostęp do danych prywatnych kla-sy musimy funkcję operatorową zadeklarować jako funkcję zaprzyjaźnioną z klasą. Zanim pokażemy przykład z funkcją operatorową w postaci funkcji zaprzyjaźnionej, omówimy program, dzięki któremu wykonamy mnożenie ułamków korzystając z funkcji zaprzyjaźnionych (nie użyjemy przeciążenia operatora.). Klasa ulamek ma następującą postać:

7.5. Funkcja operatorowa w postaci niezależnej funkcji 155 friend i n t iloczyn_Mi ( ulamek &, ulamek &) ; void pokaz ( ) ;

} ;

Listing 7.3. mnożenie ułamkow – funkcje zaprzyjaźnione

#include <i o s t r e a m > friend i n t iloczyn_Mi ( ulamek &, ulamek &) ;

12 void pokaz ( ) ;

i n t iloczyn_Mi ( ulamek &x , ulamek &y )

30 { return ( x . mia ∗ y . mia ) ;

156 7. Przeciążanie operatorów

40 mianownik = iloczyn_Mi ( u1 , u2 ) ; ulamek u3 ( l i c z n i k , mianownik ) ;

42 c o u t << " i l o c z y n ␣ulamkow␣=␣ "; u3 . pokaz ( ) ;

44 g e t c h e ( ) ; return 0 ;

46 }

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

ulamek 1 = 3/4 ulamek 2 = 5/6

i l o c z y n ulamkow = 15/24

W definicji klasy ulamek mamy dwie dane prywatne li i mia (są to wartości odpowiednio licznika i mianownika), dwie funkcje składowe (konstruktor i funkcja pokaz() ) oraz dwie funkcje zaprzyjaźnione. Iloczyn dwóch ułam-ków jest nowym ułamkiem – jego licznik jest równy iloczynowi liczniułam-ków a mianownik jest równy iloczynowi mianowników. Do obliczenia iloczynów użyjemy funkcji zaprzyjaźnionych:

friend i n t i l o c z y n _ L i ( ulamek &, ulamek &) ; friend i n t iloczyn_Mi ( ulamek &, ulamek &) ;

Implementacja tych funkcji ma postać:

i n t i l o c z y n _ L i ( ulamek &x , ulamek &y ) { return ( x . l i ∗ y . l i ) ;

}

i n t iloczyn_Mi ( ulamek &x , ulamek &y ) { return ( x . mia ∗ y . mia ) ;

}

Należy zwrócić uwagę, że argumentami funkcji są referencje obiektów. W funkcji testującej tworzymy dwie zmienne pomocnicze licznik i mianownik oraz dwa ułamki u1 i u2:

ulamek u1 ( 3 , 4 ) , u2 ( 5 , 6 ) ; i n t l i c z n i k , mianownik ;

a następnie wywołujemy funkcje zaprzyjaźnione:

l i c z n i k = i l o c z y n _ L i ( u1 , u2 ) ; mianownik = iloczyn_Mi ( u1 , u2 ) ;

dzięki którym obliczamy iloczyn liczników ułamków u1 i u2 oraz iloczyn

7.5. Funkcja operatorowa w postaci niezależnej funkcji 157 mianowników tych ułamków. Mając obliczony nowy licznik i nowy mianow-nik tworzymy ułamek u3 będący iloczynem ułamków u1 i u2:

ulamek u3 ( l i c z n i k , mianownik ) ;

Analizując powyższy przykład widzimy, że obliczanie iloczynu ułamków przy pomocy funkcji zaprzyjaźnionych wymaga skomplikowanych wywołań tych funkcji. Pokażemy, że w znaczący sposób możemy uprościć program wpro-wadzając przeciążony operator mnożenia jako funkcję operatorową zaprzy-jaźnioną z klasą.

Listing 7.4. mnożenie ułamków – funkcja operatorowa zaprzyjaźniona

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

9 friend ulamek operator∗ ( ulamek , ulamek ) ; void pokaz ( ) ;

158 7. Przeciążanie operatorów

W powyższym przykładzie deklaracja klasy jest następująca:

c l a s s ulamek { i n t l i , mia ;

public:

ulamek ( ) ; // k o n s t r u k t o r

ulamek (int, i n t) ; // k o n s t r u k t o r friend ulamek operator∗ ( ulamek , ulamek ) ; void pokaz ( ) ;

} ;

Deklaracja klasy ulamek zawiera deklaracje dwóch danych prywatnych li i mia, dwóch konstruktorów, jednej funkcji składowej pokaz() oraz operatoro-wej funkcji zaprzyjaźnionej. Należy pamiętać o konstruktorze bezparametro-wym, bez jego jawnej definicji kompilacja naszego programu nie powiedzie się. Deklaracja funkcji zaprzyjaźnionej ma postać:

friend ulamek operator∗ ( ulamek , ulamek ) ;

W języku C++ operatory mogą być przeciążane także przy pomocy funkcji, które nie są składowymi klasy. Można stosować zwykłe funkcje globalne a także funkcje zaprzyjaźnione. Aby dana funkcję zadeklarować jako funkcję zaprzyjaźnioną z konkretną klasą, należy wstawić prototyp takiej funkcji do wnętrza definicji klasy (tak, jakby to była metoda danej klasy) i poprze-dzić ten prototyp słowem kluczowym friend. W zastosowaniach taka funkcja będzie traktowana jakby była metodą należącą do danej klasy. Jak pamię-tamy, funkcje zaprzyjaźnione mają dostęp do danych prywatnych klasy. W pokazanym przykładzie funkcja operatorowa jest funkcją zaprzyjaźnioną.

Ponieważ do funkcji zaprzyjaźnionych nie jest przekazywany wskaźnik this, zaprzyjaźniona funkcja operatorowa wymaga przekazania operandów w sposób jawny. Wobec tego funkcja operatorowa jednoargumentowa wy-maga przekazania jednego parametru, funkcja operatorowa dwuargumen-towa wymaga przekazania dwóch argumentów. Należy pamiętać o kolej-ności przekazywanych argumentów. W funkcji operatorowej zaprzyjaźnio-nej lewy operand jest przekazywany jako pierwszy, a prawy jako drugi. Do

7.5. Funkcja operatorowa w postaci niezależnej funkcji 159 przeciążania operatorów najczęściej wykorzystuje się funkcje zaprzyjaźnio-ne, ponieważ funkcje zaprzyjaźnione są bardziej elastyczne w porównaniu z funkcjami składowymi. Składowe funkcje operatorowe wymagają zgodności typów operandów, natomiast w przypadku funkcji zaprzyjaźnionych nie jest wymagane, aby lewy operand koniecznie był obiektem klasy. Stąd wynika użyteczność zaprzyjaźnionych funkcji operatorowych - możemy mieszać typy operandów, co jest przydatne, jeżeli chcemy stosować w wyrażeniach obiek-ty i dane numeryczne. Implementacja przeciążenia operatora mnożenia przy pomocy funkcji zaprzyjaźnionej może mieć postać:

ulamek operator∗ ( ulamek x , ulamek y ) { ulamek u ;

u . l i = x . l i ∗ y . l i ; u . mia = x . mia ∗ y . mia ; return u ;

}

Przypominamy, że w definicji klasy zaprzyjaźnionej nie podajemy nazwy klasy bazowej (bo funkcja zaprzyjaźniona nie jest funkcją składową klasy) łącznie z operatorem zakresu. Funkcja operatorowa pobiera dwa argumenty typu ulamek i zwraca obiekt typu ulamek. W ciele funkcji tworzymy tym-czasowy obiekt ulamek u. W funkcji testującej tworzymy trzy obiekty typu ulamek:

ulamek u1 ( 3 , 4 ) , u2 ( 5 , 6 ) , u3 ;

Przy pomocy prostej instrukcji:

u3 = u1 ∗ u2 ;

mnożymy dwa ułamki, wykorzystując przeciążony operator mnożenia (*).

Tą prostą instrukcję należy porównać z pokazana poprzednio metoda mno-żenia ułamków:

l i c z n i k = i l o c z y n _ L i ( u1 , u2 ) ; mianownik = iloczyn_Mi ( u1 , u2 ) ; ulamek u3 ( l i c z n i k , mianownik ) ;

aby docenić zalety używania przeciążonych operatorów. Bardziej wydajna wersja programu ilustrującego wykorzystanie przeciążonego operator mno-żenia pokazana jest na kolejnym wydruku. Argumenty zaprzyjaźnionej funk-cji operatorowej są przekazywane przez referencję. Deklaracja funkfunk-cji ope-ratorowej ma postać:

friend ulamek operator∗ ( ulamek &x , ulamek &y ) ;

160 7. Przeciążanie operatorów Implementacja może mieć postać:

ulamek operator ∗ ( ulamek &x , ulamek &y ) {

return ulamek ( x . l i ∗ y . l i , x . mia ∗ y . mia ) ; }

Jeżeli tą implementację porównamy z implementacją, gdzie argumenty są przekazywane przez wartość:

to widzimy widoczne zalety gdy argumenty przekazywane są przez referen-cję. Przede wszystkim nie tworzymy kopii argumentów, nie tworzymy także tymczasowego obiektu, który musi być następnie zniszczony. Zagadnienie wydajności w naszym przykładzie nie odgrywa większej roli, gdy mnożymy dwa ułamki, staję się jednak istotne, gdy zechcemy dokonać mnożeń tysięcy ułamków.

Listing 7.5. mnożenie ułamków – funkcja operatorowa zaprzyjaźniona

// f u n k c j a operatorowa , z a p r z y j a z n i o n a , r e f e r e n c j e

12 friend ulamek operator∗ ( ulamek &x , ulamek &y ) ; void pokaz ( ) ;

W dokumencie Język C++ – podstawy programowania (Stron 164-171)