Zawsze kiedy masz właśnie cos zrobić, okazuje się ze najpierw musisz zrobić cos innego.
Kurs C++ - Część VII. Referencje

Teraz o czymś, co jest poniekąd odwrotnością wskaźnika: o referencji. Referencja to zmienna, która "podszywa się" pod inną zmienną. Jest ona szczególnie przydatna przy funkcjach, o czym nieco dalej. Oto przykładowy kod:

   int liczba = 10;
   int & adr_liczby = liczba;

Pierwszy wiersz jest oczywisty: zwyczajne zadeklarowanie zmiennej z inicjacją. Drugi wiersz to coś nowego. Mamy tu operator adresu (zwany operatorem referencji). To jest właśnie deklaracja specjalnej zmiennej zwanej referencją. Taka deklaracja oznacza, że zmienna adr_liczby jest typu referencyjnego i może "podszywać się" pod zmienną typu int. Zmienną referencyjną musimy od razu zainicjować, inaczej kompilator zgłosi błąd, bo musi wiedzieć, jaką zmienną "udaje" referencja. Przypisujemy referencji adr_liczby zmienną liczba. Co to daje? Sprawdź poniższy kod:

   cout << "Oryginalna liczba: " << liczba << endl;
   cout << "Jej referencja: " << adr_liczby << endl;

Zmienna referencyjna to jakby druga nazwa dla naszej zmiennej. Jak działa taka referencja? Każda zmienna dla kompilatora to nazwa jakiegoś miejsca w pamięci. Dla kompilatora nazwa zmiennej to odpowiednik jakiegoś adresu w pamięci. Referencja działa tak, jak działa, bo przejmuje adres wewnętrzny każdej zmiennej, którą się jej przypisze. Dla kompilatora wygląda to po prostu jak inna nazwa pewnego miejsca w pamięci, czyli inna nazwa jakiejś zmiennej.

Sprawdźmy, czy rzeczywiście chodzi o tą samą zmienną, tylko pod inną nazwą. Zmienimy wartość jednej z nich i zobaczymy, czy druga też się zmieni:

   liczba = 10;  cout << "liczba: "  << adr_liczby << endl;
   adr_liczby = 20;  cout << "liczba: " << liczba << endl;

Jak widać zmiana referencji powoduje również zmianę drugiej zmiennej. Gdy program chce coś zrobić z referencją, to wskazuje mu ona zawsze miejsce w pamięci, gdzie przechowywana jest ta druga zmienna. Poniższy kawałek kodu powinien wyjaśnić resztę wątpliwości:

   cout << "Adres liczby: "  << (&liczba) << endl;
   cout << "Adres referencji: " << (&adr_liczby)  << endl;

Jak widać, w oby wypadkach chodzi o ten sam adres pamięci.

Spójrzmy na to jeszcze raz od strony kompilatora. Dla niego każda nazwa zmiennej to coś na kształt wskaźnika, bo jest odpowiednikiem jakiegoś adresu w pamięci. Zwykła zmienna ma swoje stałe miejsce. Referencja to jakby "bezdomna" zmienna, która korzysta z cudzych adresów. Natomiast wskaźnik - jak zwykła zmienna - ma stały adres pamięci, ale jest czymś w rodzaju informatora, który może wskazać nam adres innej zmiennej, gdy go o to zapytamy. Referencja i wskaźnik, mimo że wydają się być tym samym, są całkowitymi przeciwieństwami. Dlatego operator & (referencji) i operator * (dereferencji) znoszą się nawzajem. Sprawdź poniższy przykład:

   int liczba = 123;
   int *wskaznik = &liczba;

   cout << (*&liczba)  << "\t\t" << liczba  << endl;
   cout << (&*wskaznik) << "\t\t" << wskaznik << endl;

Deklarujemy zmienną i wskaźnik do niej. W 3 wierszu zawartość nawiasu działa tak: najpierw operator referencji zwraca adres zmiennej, po czym operator dereferencji zwraca to, co jest pod tym adresem, czyli zawartość zmiennej. Oba operatory działają całkowicie odwrotnie, co powoduje, że kasują się nawzajem. Dowodem na to jest 3 wiersz:. najpierw operator dereferencji wyciąga to, co jest pod adresem wskazywanym przez wskaźnik, po czym operator referencji zwraca adres tego czegoś (czyli to, co zawiera zmienna wskaźnikowa). Powinno to już wyjaśnić, czym się różni pojęcie REFERENCJI od pojęcia DEREFERENCJI. W kolejnej części poznasz funkcje i zobaczysz wreszcie, do czego może się przydać referencja.

« wstecz   dalej »