Referencja (informatyka) - Google

Referencja (informatyka)

Z Wikipedii

Skocz do: nawigacji, szukaj

Referencja w informatyce oznacza dane, które zawierają informację o położeniu innych danych. Określenie "X jest referencją do Y" oznacza, że na obiekcie X można wykonać każdą operację, którą potencjalnie można wykonać na obiekcie Y. W tym wypadku operacje na X w rzeczywistości są operacjami na Y. W odróżnieniu do wskaźnika, użycie referencji nie wymaga specjalnej składni. Referencje występują w większości języków programowania, jednak szczególne znaczenie mają w języku C++.

Spis treści

[edytuj] Referencja w C++

W języku programowania C++ referencja jest pochodnym typem danych i podobnie jak wskaźnik umożliwia pośrednie odwoływanie się do zmiennych lub obiektów.

Z punktu widzenia programisty, referencja jest alternatywną nazwą innej, istniejącej już zmiennej lub obiektu. Stosowana jest głównie do

  1. przekazywania argumentów do funkcji
  2. przekazywania wartości z funkcji (szczególnie operatorów)
  3. przechowywania w obiekcie informacji o położeniu innych obiektów
  4. skracania zapisu długich wyrażeń

W trzech pierwszych zastosowaniach referencja z zasady implementowana jest jako stały wskaźnik, co w praktyce oznacza, że tak naprawdę funkcja otrzymuje jako argument lub zwraca jako wartość pewien adres. Fakt ten jest jednak zupełnie transparentny dla programisty, gdyż referencji używa się tak, jak zwykłych zmiennych i obiektów, a nie jak wskaźników. W czwartym z powyższych zastosowań, a także podczas opracowywania tzw. funkcji wklejanych (inline), kompilator może tak zoptymalizować referencję, że w programie nie będzie reprezentowana przez żaden obiekt.

Referencje wymagajÄ… inicjalizacji.

[edytuj] Referencje i stałe referencje

W języku C++ rozróżnia się dwa typy referencji: "zwyczajne" i stałe. Zwyczajne referencje umożliwiają zarówno odczyt jak i modyfikację obiektu, do którego się odwołują, natomiast stałe referencje umożliwiają wyłącznie odczyt stanu obiektu. Zwyczajne referencje wymagają podania konkretnego obiektu, do którego się mają odnosić, natomiast stałe referencje mogą się odnosić zarówno do obiektów, jak i wyrażeń. Utworzenie referencji do wyrażenia, które oczywiście nie ma własnego adresu, realizowane jest w ten sposób, że najpierw konstruowany jest tymczasowy obiekt inicjalizowany wartością danego wyrażenia, a następnie referencji przypisuje się adres tego tymczasowego obiektu. Właściwość ta ma kapitalne znaczenie przy definiowaniu argumentów funkcji (a szczególnie funkcji generowanych z szablonów): jako faktycznych argumentów funkcji pobierających argument(y) przez stałą referencję można użyć zarówno nazw zmiennych (np. f(x)), jak i wyrażeń (np. f(x+1)).

Zaleca się, by obiekty klas zdefiniowanych przez użytkownika przekazywać do lub z funkcji poprzez referencję lub stałą referencję zależnie od tego, czy funkcja ma mieć prawo do modyfikowania argumentu/wartości, czy nie. Przekazywanie argumentów przez wartość należy ograniczyć do niewielkich obiektów, np. prostych typów wbudowanych, jak int, double czy int*.

[edytuj] Składnia

Deklaracja referencji składa się z nazwy typu, operatora & i nazwy zmiennej referencyjnej, np.

int i;
...
int & q = i; // q jest referencjÄ… do zmiennej i

Deklaracja stałej referencji zawiera dodatkowo słowo kluczowe const pisane bezpośrednio przed lub tuż za nazwą typu, np.

const int & i_ref1 = i; // i_ref1 jest stałą referencją do zmiennej i typu int
int const & i_ref2 = i; // i_ref2 jest stałą referencją do zmiennej i typu int

Obie konwencje są równoważne.

[edytuj] Referencje a wskaźniki

Jak już wspomniano, referencje implementowane są przy pomocy stałych wskaźników. Analogicznie stałe referencje implementowane są przy pomocy stałych wskaźników na stałe. Ponieważ w przeciwieństwie do wskaźników, referencje muszą być inicjalizowane adresem obiektu, są one dużo bardziej bezpieczne od wskaźników. Nie oznacza to jednak, że referencje gwarantują 100% bezpieczeństwo. Wystarczy, że w programie utworzymy niezainicjowany wskaźnik:

int* p;

Ze zględu na brak inicjalizacji zmiennej p, wyrażenie *p nie reprezentuje żadnego obiektu. Tym niemniej może ono przypadkowo zostać przekazane do funkcji przyjmującej argument przez referencję.

f(*p);  // f jest funkcjÄ… o sygnaturze void f(int & x);

W tym wypadku funkcja otrzyma referencję do zmiennej znajdującej się pod nieważnym adresem p. Jakiekolwiek użycie takiego argumentu może zakończyć się katastrofą. Wina nie leży jednak po stronie referencji, lecz po stronie wskaźników.


[edytuj] Przykłady

[edytuj] Użycie referencji w argumentach funkcji

Poniższy kod zawiera typową (nieco uproszczoną) implementację standardowego szablonu funkcji swap, która zamienia wartości swoich argumentów.

template <typename T>
  inline void
  swap(T& a, T& b)
  {
    const T tmp = a;
    a = b;
    b = tmp;
  }

Ponieważ funkcja modyfikuje argumenty, są jej one przekazywane przez referencję.

[edytuj] Użycie referencji w wartości funkcji

Podstawowym miejscem zastosowania typów referencyjnych w wartościach funkcji jest implementacja operatorów. Oto przykład (szablonu) operatora strumieniowego, który wyświetla zawartość dowolnego standardowego wektora:

template <typename T>
  std::ostream & 
  operator<<(std::ostream & out, std::vector<T> const& v)
  {
     out << '(';
     for (size_t i = 0; i + 1 < v.size(); ++i)
        out << v[i] << ", ";
 
     if (!v.empty())
        out << v.back();
 
     out << ')';
 
     return out;          
  }

Dzięki przekazaniu jako wartości operatora << referencji do strumienia, operatora tego można użyć w ciągu, np.

std::cout << v << ", ";    // v jest typu std::vector<T>

Ponieważ operator<< jest lewostronnie łączny, w instrukcji tej najpierw zostanie opracowane wyrażenie std::cout << v. Jego wartość, czyli (referencja do) std::cout, zostanie następnie użyta jako lewy argument drugiego operatora<<. W sumie więc powyższa instrukcja równoważna jest dwóm instrukcjom

std::cout << v;
std::cout << ", ";

Referencja w powyższym przykładzie jest konieczna, gdyż strumieni nie można kopiować, w związku z czym nie można ich przekazywać z funkcji przez wartość.

[edytuj] Referencje jako składowe klas lub struktur

Referencje mogą być używane jako składowe klas lub struktur. Oto dość ogólny przykład:

class X
{
...
};
 
class X_iterator
{
   X const& _x;
public:
   X_iterator (X const& x) 
   : _x(x)
   { ... }
...
};

W powyższym kodzie zdefiniowano dwie klasy: X i X_iterator, przy czym druga z nich posiada składową referencyjną _x. Ponieważ każda referencja wymaga inicjalizacji, niezbędny do tego parametr przekazywany jest w preambule konstruktora (wyrażenie _x(x)) (zobacz Lista inicjalizacyjna konstruktora). Zastosowanie składowej referencyjnej wiąże się z następującymi faktami:

  • Każdy obiekt klasy X_iterator ma dostÄ™p do oryginaÅ‚u pewnego obiektu klasy X.
  • Zastosowanie staÅ‚ej referencji gwarantuje, że obiekty klasy X_iterator nie bÄ™dÄ… zmieniać stanu przypisanych im obiektów klasy X.
  • Każdy konstruktor klasy X_iterator musi inicjalizować (w swojej preambule) skÅ‚adowÄ… referencyjnÄ… _x.
  • Inicjalizacja skÅ‚adowych referencyjnych odbywa sie przed wywoÅ‚aniem ciaÅ‚a konstruktora.

[edytuj] Zastosowanie referencji do upraszczania notacji i unikania zbędnych obliczeń

Rozpatrzmy następujący fragment kodu:

std::map<std::string, std::vector<int> > mapa;  
   ...
std::vector<int> & w = mapa["ala"];

Referencja udostępnia element mapa["ala"] poprzez alternatywną "nazwę" w. W związku z tym wyrażenie mapa["ala"].resize(100) jest równoważne wyrażeniu w.resize(100), z tym że to drugie jest prostsze (w więc czytelniejsze) oraz bardziej efektywne, gdyż nie wymaga dość kosztownego obliczania adresu wyrażenia mapa["ala"].

[edytuj] Zastosowanie stałych referencji w argumentach i wartości funkcji

Oto typowa implementacja szablonu max obliczającego większy z dwóch obiektów a, b dowolnego typu T

template <typename T>
  inline const T&
    max(const T& a, const T& b)
    {
      if (a < b)
        return b;
      return a;
    }

W powyższym przykładzie:

  • Stałą referencjÄ™ zastosowano zarówno do przekazania argumentów do funkcji, jak i jej wartoÅ›ci do punktu jej wywoÅ‚ania;
  • DziÄ™ki zastosowaniu staÅ‚ej referencji, funkcji max można używać nawet tak, jakby argumenty byÅ‚y do niej przekazywane przez wartość:
int n = 9, k = 10;
...
int m = max(n, 100 - k);

W przykładzie tym pierwszy argument (obiekt n) zostanie przekazany do funkcji max przez (stałą) referencję, natomiast drugi argument (wyrażenie 100 - k) zostanie wpierw skopiowany (jakby był przekazywany przez wartość) i dopiero adres tej kopii zostanie przekazany do funkcji.

  • Gdyby argumenty byÅ‚y do funkcji przekazywane przez wartość, funkcja niepotrzebie tworzyÅ‚aby ich lokalne kopie, co mogÅ‚oby być szczególnie niekorzystne dla tych typów T, które posiadajÄ… nietrywialne, zÅ‚ożone konstruktory kopiujÄ…ce
  • Gdyby argumenty formalne przekazywano przez zwykłą referencjÄ™, faktycznymi argumentami funkcji nie mogÅ‚yby być ani staÅ‚e obiekty, ani jakiekolwiek wyrażenia.
  • W wypadku zastosowania agresywnej optymalizacji, dziÄ™ki zastosowaniu atrybutu inline wyrażenia typu max(n, 100 - k) przestanÄ… być interpretowane jak wywoÅ‚ania funkcji, a sposób przekazywania "argumentów" straci jakiekolwiek znaczenie. DziÄ™ki temu wartoÅ›ci wyrażeÅ„ typu max(n, 100) bÄ™dÄ… obliczane z najwiÄ™kszÄ… możliwÄ… prÄ™dkoÅ›ciÄ…, bez żadnego narzutu zwiÄ…zanego z przekazaniem literaÅ‚u przez referencjÄ™.

[edytuj] Zobacz też

[edytuj] Referencja w innych językach programowania

W pewnych językach programowania referencje w ogóle nie występują. Na przykład w języku C argumenty i wartości funkcji przekazywane są wyłącznie przez wartość.

Istnieją też języki, np. Fortran 77, w których co prawda nie ma typów referencyjnych, ale referencje wykorzystywane są (niejawnie) do przekazywania argumentów i wartości funkcji. Własność ta w niektórych starszych implementacjach fortranu (np. Lahey 77) prowadziła do nieoczekiwanego efektu: programy mogły modyfikować wartości literałów (sic!).

W innych językach programowania, takich jak Python czy Java, referencja ma semantykę bliższą wskaźnikom z C++. Każde przypisanie do referencji powoduje, że wskazuje ona na nowo podany obiekt. Pustą referencję (nie wskazującą na żaden obiekt) oznacza się specjalnym słowem kluczowym (null w Javie, nil w Smalltalku) lub specjalnym obiektem pustym (ang. null object; w Pythonie jest to None). Od wskaźnika tak rozumiana referencja różni się tym, że nie może wskazywać na błędny, przypadkowy adres w pamięci. Zawsze jest albo pusta, albo wskazuje na konkretny obiekt. Eliminuje to całą kategorię błędów wynikających z próby interpretacji przypadkowego fragmentu pamięci jako obszaru zawierającego konkretne, użyteczne dane.


Polsat: Euro w pubach od 1348 zł
Polsat udziela praw do publicznego pokazywania meczów nachodzących Mistrzostw Europy. Najtańszy wariant cenowy to 1348 zł.
N ma już 320 tys. abonentów
Platforma satelitarna N na koniec I kwartału 2008 r. miała 320 tys. abonentów - poinformował ITI Holdings.
27 maja Kraśko poprowadzi "Wiadomości"
27 maja Piotr KraÅ›ko zadebiutuje jako prowadzÄ…cy „WiadomoÅ›ci” w TVP1.
Skandal we włoskiej telewizji publicznej
Wydział personalny składający się z ponad 250 urzędników zajmuje się 13 248 stałymi pracownikami i 43 000 współpracowników RAI, która zatrudnia m.in. 114 fryzjerów i 67 garderobianych - tak rzymski tygodnik "L'Espresso" opisał włoską telewizję publiczną RAI.
Segment pism dla rodziców zanotował wzrost sprzedaży
Wszystkie miesiÄ™czniki dla rodziców zanotowaÅ‚y w lutym br. wyższÄ… sprzedaż ogółem w porównaniu z lutym ub.r. – wynika z danych ZwiÄ…zku Kontroli Dystrybucji Prasy.
Linki: Strona g³ówna