content48









Przesyłanie argumentów przez referencję





Powyżej opisany sposób znany był programistom C. Język C++ przynosi jeszcze inny sposób przesyłania argumentów. Przez referencję. Czyli przez przezwisko.


W dalszej części książki powinniśmy mówić: "przez referencję", jednak dopóki się z tym nie oswoisz - używać będziemy też równolegle terminu: "przez przezwisko"


Oto przykład:

#include <iostream.h>
void zer( int wart, int &ref); // 1
/*********************************************/
main( )
{
int a = 44,
b = 77 ;

cout << "Przed wywołaniem funkcji: zer \n" ;
cout << "a = " << a << ", b = " << b << endl ;

zer(a, b) ; // 2

cout << "Po powrocie z funkcji: zer \n" ;
cout << "a = " << a << ", b = " << b << endl ; // 7
}
/***********************************************/
void zer( int wart, int &ref)
(
cout << "\tW funkcji zer przed zerowaniem \n" ;
cout << "\twart = " << wart << ", ref = "
<< ref << endl ; // 3

wart = 0 ;
ref = 0 ; // 4

cout << "\tW funkcji zer po zerowaniu \n" ;
cout << "\twart = " << wart << ", ref = "
<< ref << endl ; // 5
} // 6

foliogram nr 21


W rezultacie działania tego programu na ekranie pojawi się

Przed wywołaniem funkcji: zer
a = 44, b = 77
       W funkcji zer przed zerowaniem
       wart = 44, ref = 77
       W funkcji zer po zerowaniu
       wart = 0, ref = 0
Po powrocie z funkcji: zer
a = 44, b = 0


Komentarz

Patrząc na ekran zauważamy, że funkcja, która chciała wyzerować dwa obiekty a i b wysłane do niej jako argumenty - zaskoczyła nas. Oczywiście obiekt a jest nietknięty. To znamy. Jednak obiekt b ma wartość 0. Dlaczego?


Jeśli chodzi o zmienną a - to nic nas tu nie dziwi. Funkcja odebrała ją przez wartość.


Rzućmy więc okiem na deklarację funkcji zer. Widzimy, że to funkcja, która przyjmuje dwa argumenty. Pierwszy z nich jest przesyłany - tak jak poprzednio - przez wartość. Drugi natomiast jest przesyłany przez referencję. Zauważ znak: &
W main mamy dwie zmienne, które wysyłamy do funkcji zer. Inaczej mówiąc: wywołujemy funkcję zer z parametrami aktualnymi a, b
Wewnątrz funkcji zer, na moment przed "egzekucją" wypisujemy jeszcze wartość dwóch parametrów formalnych wart, ref
Tu następuje jakaś operacja zmieniająca wartość zmiennych wart i ref. W naszym wypadku to wpisanie tam zer.
Na dowód, że tak się stało w istocie - wypisujemy ich wartość na ekran.
Kończymy pracę funkcji. Ponieważ funkcja zwraca typ void (po prostu nic nie zwraca) dlatego możemy sobie tu oszczędzić instrukcji return. Gdybyśmy chcieli by ona koniecznie była, to linijkę wcześniej należałoby napisać

return ;

Po powrocie z funkcji, będąc już w ma i n wypisujemy na ekranie wartości zmiennych a i b. I tu jest cała niespodzianka. Z treści, która pojawia się na ekranie widać, że ten argument, który funkcja przyjmowała starym sposobem (przez wartość) nie został zmodyfikowany. Natomiast ta zmienna, którą funkcja odebrała przez referencję (przezwisko) została zmodyfikowana.


Dlaczego ?

Otóż w tym wypadku do funkcji, zamiast liczby 77 (wartość zmiennej b), został wysłany adres zmiennej b w pamięci komputera.
Ten adres funkcja sobie odebrała i (na stosie) stworzyła sobie referencję. Czyli powiedziała sobie coś takiego: "Dobrze, zatem komórce pamięci o przysłanym mi adresie nadaję pseudonim (przezwisko) ref."


Podkreślmy jasno: ta sama komórka, na którą w main mówiło się b, stała się teraz w funkcji zer znana pod przezwiskiem ref. Są to dwie różne nazwy, ale określają ten sam obiekt.

W 4 do obiektu o tym przezwisku ref wpisano zero. Skoro ref było przezwiskiem obiektu b, to znaczy, że odbyło się to na obiekcie b.

Ponieważ, jak pamiętamy, po zakończeniu działania funkcji likwiduje się śmieci, zobaczmy co zostało zlikwidowane.

Będąca na stosie kopia zmiennej a. (Która to kopia początkowo miała wartość 44, a potem 0). Pamiętamy, że ten argument odebrany był przez wartość.
Drugi argument przesyłany był przez referencję, więc na stosie mieliśmy zanotowany adres tego obiektu, który to obiekt wewnątrz programu przezywaliśmy ref. Ten adres został zlikwidowany. (Jeśli podrzemy kartkę z zapisanym adresem jakiegoś budynku, to mimo wszystko budynek ten nadal stoi. My co prawda tracimy adres tego budynku, ale inni - np. funkcja main - mają ten adres u siebie zanotowany.


Wniosek: Przesłanie argumentów funkcji przez referencję pozwala tej funkcji na modyfikowanie zmiennych (nawet lokalnych! ) znajdujących się poza tą j funkcją.


Programistów klasycznego C opanowała na pewno teraz euforia:

"-Wreszcie jest łatwy sposób modyfikowania argumentów! To, na co nie pozwalało przesyłanie argumentów przez wartość, staje się wreszcie możliwe dzięki przesyłaniu przez referencję!"

Hola, hola! Chciałbym Cię tutaj przestrzec. Na pewno w innych, nawet prymitywnych językach programowania, spotkałeś już taki właśnie sposób przesłania argumentów do funkcji, mimo że tam nie nazwał się on przesłaniem przez referencję.

Dlaczego zatem tak wspaniały język jak C (klasyczne) nie pozwalał na to? Widocznie były powody. Nie, nie chodzi o to, że może dla piszących kompilatory byłoby to trudne w realizacji. Powody są inne. Otóż przesyłanie przez referencję jest prostą drogą do pisania programów bardzo trudnych do późniejszej analizy.

Nasze zmienne w jakimÅ› fragmencie programu zmieniajÄ… siÄ™ bowiem w sposób niezauważony na skutek dziaÅ‚ania jakiegoÅ› innego fragmentu programu (innej funkcji). Niezauważony, bowiem z wywoÅ‚ania funkcji zer w Å›rodku main © nie widać, który argument przesyÅ‚any przez wartość, a który przez referencjÄ™. Nie ma wiÄ™c ostrzeżenia: acha, ten argument może być tam modyfikowany!

Ten sposób przesyłania argumentów do funkcji powinien być więc zasadniczo unikany. Są jednak sytuacje, kiedy się bardzo przydaje. To właśnie z powodu takich sytuacji ten sposób przesyłania argumentów został do języka C++ wprowadzony. O tych sytuacjach będziemy jednak mówić dokładniej w dalszych rozdziałach. Tutaj tylko wspomnę, że sposób ten stosuje się do tak dużych obiektów, że przesłanie ich przez wartość (wymagające zrobienia kopii np. dziesiątek, setek bajtów) powodowałoby znaczące spowolnienie wywoływania takiej funkcji. W wypadku, gdy taka funkcja jest wywoływana bardzo wiele razy, może to być czynnikiem ważnym.



Jeszcze innym sposobem przesłania argumentu może być posłużenie się tzw. wskaźnikiem. Ten sposób omówimy bliżej w rozdziale o wskaźnikach.










Wyszukiwarka

Podobne podstrony:
content
content
content
content
content
content
content
content
content
function domnode get content
content
content
content
content
content
content

więcej podobnych podstron