Funkcje zaprzyjaźnione
argumentu i ta kopia przekazywana jest do wywoływanej funkcji. Możemy
dokonywać dowolnych zmian na kopii, oryginalna wartość jest bezpieczna –
nie ulega zmianom. Przekazywanie argumentów przez wartość jest bezpiecz-
ne, ale bardzo wolne. Przekazywanie parametrów funkcji przez referencje
jest korzystne ze względu na wydajność – jest po prostu procesem bardzo
szybkim. Za każdym razem, gdy przekazywany jest obiekt do funkcji przez
wartość, tworzona jest kopia tego obiektu. Za każdym razem, gdy zwracany
jest obiekt z funkcji tworzona jest kolejna kopia. Obiekty kopiowane są na
stos. Wymaga to sporej ilości czasu i pamięci. W przypadku zdefiniowa-
nych przez programistę dużych obiektów, ten koszt staje się bardzo duży.
Rozmiar obiektu umieszczonego na stosie jest sumą rozmiarów wszystkich
jego zmiennych składowych. Stosowanie referencji jest korzystne, ponieważ
eliminuje koszty związane z kopiowaniem dużych ilości danych.
Parametr referencji jest aliasem (synonimem) do odpowiadającego mu
argumentu. Referencja jest specjalnym, niejawnym wskaźnikiem, który dzia-
ła jak alternatywna nazwa dla zmiennej. Zmienną o podanej po nazwie typu
z przyrostkiem &, np.
T& nazwa
gdzie T jest nazwą typu, nazywamy referencją do typu T. Na przykład,
deklaracja
i n t &l i c z b a
umieszczona w nagłówku funkcji oznacza „liczba jest referencją do int”. W
krótkim przykładzie przypomnimy przekazywanie argumentów przez refe-
rencję.
Listing 6.4. Przekazywanie parametrów przez referencję
1 #i n cl u d e <i o s t r e a m >
#include <c on io >
3
using namespace s t d ;
void Kwadrat ( i n t & ) ;
5
i n t main ( )
{
i n t x = 2 ;
7
c o u t << " zmienna␣x␣ zmodyfikowana : " <<e n d l ;
c o u t << "x␣=␣ " << x << e n d l ;
9
Kwadrat ( x ) ;
c o u t << "x␣=␣ " << x << e n d l ;
11
i n t a = x ;
c o u t << " zmienna␣x␣ n i e ␣ zmodyfikowana : " <<e n d l ;
13
c o u t << "x␣=␣ " << x << e n d l ;
Kwadrat ( a ) ;
15
c o u t << "x␣=␣ " << x << e n d l ;
g e t c h e ( ) ;
6.2. Funkcja niezależna zaprzyjaźniona z klasą
129
17
return 0 ;
}
19
void Kwadrat ( i n t &x r e f )
{ x r e f ∗= x r e f ; }
// argument zmodyfikowany
Po uruchomieniu programu otrzymujemy wydruk:
zmienna x zmodyfikowana :
x = 2
x = 4
zmienna x n i e zmodyfikowana :
x = 4
x = 4
W programie parametr funkcji Kwadrat() jest przekazywany przez referen-
cję, co widać wyraźnie w prototypie:
void Kwadrat ( i n t & ) ;
Programista analizując wywołanie funkcji:
Kwadrat ( x ) ; l u b
Kwadrat ( a ) ;
nie jest wstanie zorientować się, czy parametry przekazywane są przez war-
tość czy przez referencję (taką pewność ma tylko wtedy, gdy zanalizuje pro-
totyp funkcji). Może to prowadzić do nieoczekiwanych skutków ubocznych,
co demonstruje nasz program. Po pierwszym wywołaniu funkcji Kwadrat()
zmienna x zmieniała wartość. Aby uniknąć takich skutków wielu programi-
stów przesyła niemodyfikowalne argumenty stosując referencję do stałych.
Ponieważ zagadnienia wydajności są istotne, w kolejnym programie de-
monstrującym wykorzystanie funkcji zaprzyjaźnionych, zastosujemy prze-
kazywanie argumentów przez referencję.
Mamy następującą klasę:
c l a s s Demo_1
{
friend void ustawA ( Demo_1 &, i n t
) ;
// f u n k c j a z a p r z y j a z n i o n a
public :
Demo_1( ) { a = 0 ; }
void pokaz ( ) const { c ou t << a << e n d l ; }
private :
i n t a ;
} ;
W tej klasie jest zadeklarowana dana składowa a, która jest prywatna, czyli
130
6. Funkcje zaprzyjaźnione
jest niedostępna dla funkcji nie będących składowymi klasy. Aby funkcja ze-
wnętrzna ustawA() miała dostęp do tej danej, zadeklarowano ją jako friend:
friend void ustawA ( Demo_1 &, i n t ) ;
// f u n k c j a z a p r z y j a z n i o n a
Definicja funkcji ustawA() ma postać:
void ustawA ( Demo_1 &x , i n t i l e )
{
x . a = i l e ;
}