Czym jest referencja?

edytuj

Referencja w swym działaniu przypomina wskaźniki. Różnica polega jednak na tym, że do referencji można przypisać adres tylko raz, a jej dalsze używanie niczym się nie różni od używania zwykłej zmiennej. Operacje jakie wykona się na zmiennej referencyjnej, zostaną odzwierciedlone na zmiennej zwykłej, z której pobrano adres.

Można by pokusić się o stwierdzenie, że:

Deklaracja referencji

edytuj

Referencje deklaruje się jak zmienne z podaniem znaku &:

TypDanych & referencja

Taki zapis byłby możliwy w liście argumentów funkcji, jednak w ciele funkcji referencja musi być od razu zainicjalizowana. Zapisujemy do niej adres innej zmiennej (robi się to trochę inaczej niż w wypadku wskaźników):

TypDanych & referencja = innaZmienna;

Od tej pory można używać obu tych zmiennych zamiennie.
Poniższe przypisania dadzą więc ten sam efekt:

innaZmienna = 9;
referencja = 9;

Zobaczmy działanie referencji na konkretnym przykładzie:

int i = 0;
int &ref_i = i;
cout << i;      // wypisuje 0
ref_i = 1;
cout << i;      // wypisuje 1
cout << ref_i;  // wypisuje 1

Porównajmy to z sytuacją, gdybyśmy użyli wskaźników:

int i=0;
int *wsk_i=&i;
cout << i;      // wypisuje 0
*wsk_i = 1;
cout << i;      // wypisuje 1
cout << *wsk_i; // wypisuje 1

Zauważmy, o ile wygodniejsze jest użycie referencji. Nie musimy ani pobierać adresu zmiennej (&i) by przypisać go do referencji ani też używać gwiazdki by dostać wskazywaną wartość.

Jeszcze jedną różnicą ze wskaźnikami jest ograniczenie, że referencji po przypisaniu nie można przestawić na inną zmienną. Referencja musi też być zainicjalizowana w momencie utworzenia:

int a,b;
int *wsk_a = &a, *wsk_b = &b;
int &ref_a = a, &ref_b = b;
int &ref_c; // kompilator nie zezwoli na to - referencja niezainicjalizowana

wsk_b = &a; // ok
ref_b = &a; // tak się nie da

Przykład przypomina też, że analogicznie jak w przypadku wskaźników znak & nie łączy się z typem tylko ze zmienną i przy deklarowaniu kilku referencji na raz trzeba wstawiać & przed każdą z nich:

int &ref_x = x, &ref_y = y;           // referencje
char *wsk_1, *wsk2; // wskazniki

Stałe referencje

edytuj

Możliwe jest zadeklarowanie referencji do obiektów stałych - wtedy obiektu, do którego odnosi się referencja nie będzie można zmienić.

int i=0;
const int &ref_i = i;
cout << ref_i; // wypisze 0
ref_i = 1;     // kompilator nie pozwoli na to i zgłosi błąd

Powody, dla jakich możemy chcieć używać stałych referencji są analogiczne jak dla stałych wskaźników.

Przekazywanie argumentów przez referencję

edytuj

Aby w C zmodyfikować parametr przekazywany do funkcji, musieliśmy używać wskaźników. C++ proponuje bezpieczniejszą i wygodniejszą w użyciu metodę - przekazywanie przez referencję.

Różnica między przekazywaniem przez referencję a przekazywaniem przez wskaźnik jest taka jaka miedzy referencjami i wskaźnikami, nie ma tu żadnej rewolucji. Przykład zastosowania pokazany jest poniżej:

void nie_zwieksz (int i)
{
   ++i; // tak naprawdę funkcja nie robi nic, bo zmieniona zostaje tylko lokalna kopia
}
void zwieksz_c (int *i)
{
   ++(*i); // ta funkcja jest napisana w stylu C
}
void zwieksz_cpp (int& i)
{
   ++i; // ta funkcja wykorzystuje możliwości C++
}
int main ()
{
   int a = 0, b = 0, c = 0;
   nie_zwieksz (a);
   zwieksz_c (&b);
   zwieksz_cpp (c);
   cout << a << " " << b << " " << c; 
   // wypisze "0 1 1"

   return 0;
}