Odniesienie (referencja) może nie tylko być parametrem, ale i wartością zwracaną przez funkcję. Następująca deklaracja
int& fun(int);oznacza, że funkcja zwraca pewną zmienną typu int przez referencję, czyli na przykład wyrażenie fun(3) jest inną nazwą pewnej istniejącej zmiennej, która została podana w funkcji w instrukcji return.
To, że wyrażenie z wywołaniem funkcji jest traktowane jako nazwa
istniejącej zmiennej, a więc jako l-wartość, oznacza, że
wywołanie funkcji zwracającej referencję może pojawić się po
lewej stronie przypisania, co wygląda trochę szokująco i nie
stosuje się zbyt często; tym niemniej nie ma tu błędu.
Przykładem może być funkcja
funmax
z poniższego programu:
1. #include <iostream>
2. #include <cmath>
3. using namespace std;
4.
5. double potegi(double&,double*);
6. int* kwadrat(int*);
7. int& funmax(int[],int);
8.
9. int main() {
10.
11. // argumenty wskaźnikowe i referencyjne
12. double u = 4, v;
13. double szesc = potegi(u, &v); ➊
14. cout << "Szescian: " << szesc << "; kwadrat: "
15. << u << "; pierwiastek: " << v << endl;
16.
17. // to też ma sens
18. int i = 4;
19. cout << "20? : " << ++*kwadrat(&i)+3 << endl; ➋
20.
21. // funkcja zwracająca referencję
22. int tab[] = {1,4,6,2};
23. cout << "Tablica przed: ";
24. for ( i = 0; i < 4; i++ ) cout << tab[i] << " ";
25. cout << endl;
26.
27. funmax(tab,4) = 0; ➌
28.
29. cout << "Tablica po : ";
30. for ( i = 0; i < 4; i++ ) cout << tab[i] << " ";
31. cout << endl;
32. }
33.
34. double potegi(double& u, double* v) { ➍
35. double x = u;
36. u *= u;
37. *v = sqrt(x);
38. return u*x;
39. }
40.
41. int* kwadrat(int* p) { ➎
42. *p *= *p;
43. return p;
44. }
45.
46. int& funmax(int* tab, int ile) { ➏
47. int i, ind = 0;
48. for ( i = 1; i < ile; i++ )
49. if ( tab[i] > tab[ind] ) ind = i;
50. return tab[ind];
51. }
Funkcja potegi ma jeden argument referencyjny i jeden wskaźnikowy. Przez referencję, jako pierwszy argument, przesyłamy liczbę dodatnią: funkcja oblicza jej kwadrat, sześcian i pierwiatek kwadratowy (➍). Kwadrat argumentu jest w funkcji przypisywany do tej samej zmiennej u, która była pierwszym argumentem tej funkcji. Ponieważ argument jest referencyjny, w funkcji main po wywołaniu funkcji potegi wartość ta będzie nową wartością zmiennej u — stara wartość, wynosząca 4, zostanie zamazana.
Jako drugi argument wysyłamy do funkcji kopię adresu zmiennej v (➊). W funkcji do zmiennej o tym adresie wpisywana jest wartość pierwiastka kwadratowego pierwszego argumentu. Tak więc po powrocie z funkcji zmienna v w programie głównym będzie miała wartość 2. Funkcja potegi zwraca, przez wartość, sześcian argumentu.
Po wywołaniu funkcji potegi sześcian pierwotnej wartości zmiennej u mamy w zmiennej szesc, kwadrat jest nową wartością zmiennej u, a pierwiastek jest wartością zmiennej v: potwierdza to wydruk z programu
Szescian: 64; kwadrat: 16; pierwiastek: 2
20? : 20
Tablica przed: 1 4 6 2
Tablica po : 1 4 0 2
Dość karkołomna konstrukcja użyta jest w linii ➋. Funkcja
kwadrat
(➎) oblicza kwadrat argumentu przekazanego przez
wskaźnik (do funkcji przekazujemy kopię adresu zmiennej
i). Obliczona wartość jest wpisywana do zmiennej wskazywanej
przez wskaźnik, a więc do zmiennej
i
z programu głównego.
Jednocześnie wskaźnik do tej samej zmiennej jest zwracany
w instrukcji
return
— zauważmy, że nie jest to wskaźnik
do zmiennej lokalnej, ale do istniejącej w programie głównym
zmiennej
i. Po wywołaniu funkcji wartością wyrażenia
kwadrat(&i)
jest zatem wskaźnik do zmiennej
i,
której wartość uległa zmianie i wynosi teraz 16 (= 4×4). Za pomocą
operatora gwiazdki dokonujemy dereferencji, a więc wyrażenie
*kwadrat(&i)
jest równoważne zmiennej
i,
o wartości 16. Tę wartość zwiększamy o jeden za pomocą operatora
zwiększenia i do wyniku dodajemy 3; zatem wartością całego wyrażenia
++*kwadrat(&i)+3 jest 20. Wbrew pozorom
takie zawiłe konstrukcje dość często zdarzają się w realnych
programach (choć może nie powinny).
Przyjrzyjmy się teraz funkcji funmax (➏). Do funkcji w tradycyjny sposób wysyłamy tablicę i jej wymiar (➌). Funkcja szuka największego elementu i zwraca tenże element przez referencję, czego z linii zawierającej instrukcję return nie widać, ale widać z nagłówka w deklaracji/definicji. Zatem wyrażenie funmax(tab,4) jest nazwą tej zmiennej, która jest największym elementem tablicy, czyli w naszym przykładzie tab[2]. Wyrażenie to stoi po lewej stronie przypisania, zatem zmienna ta jest zerowana, o czym przekonuje nas wydruk.
T.R. Werner, 21 lutego 2016; 20:17