Widziałeś w podręczniku składnie w której definiujesz zmienną, a poniżej definiujesz referencję i inicjalizujesz ją tą zmienną.
Zapomnij o tym, to tylko przykład "akademicki".
Od strony "algorytmicznej" referencje nie wnoszą nic do języka czego nie obsługiwałby zwykły wskaźnik. Jest to cukierek składniowy, który pozwala przekazywać parametry modyfikowalne do procedur bez uciążliwej składni wskaźników.
string zrobCos(string str);
wymaga utworzenia nowego stringa i skopiowania zawartości (z dokładnością do copy on write, ale to już wyższa szkoła jazdy).
Możesz string str modyfikować w funkcji, po wyjściu zmiany będą zapomniane.
string zrobCos(string &str);
pozwala na przekazanie do funkcji zrobCos stringa bez kopiowania.
wszystkie operacje na stringu str będą dotyczyły tego samego stringa który został przekazany i będą widoczne po powrocie z funkcji.
ze zwykłych funkcji raczej nie zwracasz referencji (taj samo jak nie zwracasz wskaźników do elementów na stosie), elementy zostałyby zwolnione ze stosu a przy dostępie do wskaźnika czy elementu wskazywanego przez referencje dostałbyś błąd.
z metod klasy często zwracasz referencji do pól klasy. Jest to prawidłowe, dopóki nie zwolnimy całego obiektu, to jego pola będą dostępne.
class c {
private:
vector<int> _m;
public:
const vector<int> &m() const { return _m; }
};
w ten sposób przekazaliśmy tylko adres obiektu, a nie robiliśmy kopii obiektu gdy go zwracaliśmy, to duża oszczędność na kopiowaniu danych.
Ale to wszystko dało się zrobić z użyciem wskaźników.
Pozostaje najważniejsze zagadnienie.
Referencje są wykorzystywane przy przeciążaniu operatorów.
spójrz na taką deklarację:
ostream &operator<<(ostream &out, c& o);
zwracasz po wykonaniu referencję do ostream, co pozwala na łączenie wywołań w łańcuch:
cout << o << "." << endl;
Podobnie przy takich przeciążeniach jak:
Elem &operator[](size_t idx);
czy:
c &operator=(const c& o);
bez referencji nie mógł byś zwrócić modyfikowalnego obiektu z operatora.
Widziałeś w podręczniku składnie w której definiujesz zmienną, a poniżej definiujesz referencję i inicjalizujesz ją tą zmienną.
Zapomnij o tym, to tylko przykład "akademicki".
Od strony "algorytmicznej" referencje nie wnoszą nic do języka czego nie obsługiwałby zwykły wskaźnik. Jest to cukierek składniowy, który pozwala przekazywać parametry modyfikowalne do procedur bez uciążliwej składni wskaźników.
string zrobCos(string str);
wymaga utworzenia nowego stringa i skopiowania zawartości (z dokładnością do copy on write, ale to już wyższa szkoła jazdy).
Możesz string str modyfikować w funkcji, po wyjściu zmiany będą zapomniane.
string zrobCos(string &str);
pozwala na przekazanie do funkcji zrobCos stringa bez kopiowania.
wszystkie operacje na stringu str będą dotyczyły tego samego stringa który został przekazany i będą widoczne po powrocie z funkcji.
ze zwykłych funkcji raczej nie zwracasz referencji (taj samo jak nie zwracasz wskaźników do elementów na stosie), elementy zostałyby zwolnione ze stosu a przy dostępie do wskaźnika czy elementu wskazywanego przez referencje dostałbyś błąd.
z metod klasy często zwracasz referencji do pól klasy. Jest to prawidłowe, dopóki nie zwolnimy całego obiektu, to jego pola będą dostępne.
class c {
private:
vector<int> _m;
public:
const vector<int> &m() const { return _m; }
};
w ten sposób przekazaliśmy tylko adres obiektu, a nie robiliśmy kopii obiektu gdy go zwracaliśmy, to duża oszczędność na kopiowaniu danych.
Ale to wszystko dało się zrobić z użyciem wskaźników.
Pozostaje najważniejsze zagadnienie.
Referencje są wykorzystywane przy przeciążaniu operatorów.
spójrz na taką deklarację:
ostream &operator<<(ostream &out, c& o);
zwracasz po wykonaniu referencję do ostream, co pozwala na łączenie wywołań w łańcuch:
cout << o << "." << endl;
Podobnie przy takich przeciążeniach jak:
Elem &operator[](size_t idx);
czy:
c &operator=(const c& o);
bez referencji nie mógł byś zwrócić modyfikowalnego obiektu z operatora.
Jeszcze jakieś pytania?