r09.pdf

(315 KB) Pobierz
Szablon dla tlumaczy
Rozdział 9.
Referencje
W poprzednim rozdziale poznałeś wskaźniki i dowiedziałeś się, jak za ich pomocą można
operować obiektami na stercie oraz jak odwoływać się do obiektów pośrednio. Referencje mają
prawie te same możliwości, co wskaźniki, ale posiadają przy tym dużo prostszą składnię.
Z tego rozdziału dowiesz się:
czym są referencje,
czym różnią się od wskaźników,
jak się je tworzy i wykorzystuje,
jakie są ich ograniczenia,
w jaki sposób przekazywać obiekty i wartości do i z funkcji za pomocą referencji.
Czym jest referencja?
Referencja jest aliasem (inną nazwą); gdy tworzysz referencję, inicjalizujesz ją nazwą innego
obiektu, będącego celem referencji. Od tego momentu referencja działa jak alternatywna nazwa
celu. Wszystko, co robisz z referencją, w rzeczywistości dotyczy jej obiektu docelowego.
Referencję tworzy się, zapisując typ obiektu docelowego, operator referencji ( & ) oraz nazwę
referencji.
Nazwy referencji mogą być dowolne, ale wielu programistów woli poprzedzać jej nazwę literą „r”.
Jeśli masz zmienną całkowitą o nazwie someInt , możesz stworzyć referencję do niej pisząc:
int &rSomeRef = someInt;
Odczytuje się to jako: „rSomeRef jest referencją do zmiennej typu int . Ta referencja została
zainicjalizowana tak, aby odnosiła się do zmiennej someInt .” Sposób tworzenia referencji i
korzystania z niej przedstawia listing 9.1.
UWAGA Operator referencji ( & ) ma taki sam symbol, jak operator adresu. Nie są to jednak te
same operatory (choć oczywiście są ze sobą powiązane).
Zastosowanie spacji przed operatorem referencji jest obowiązkowe, użycie spacji pomiędzy
operatorem referencji a nazwą zmiennej referencyjnej jest opcjonalne. Tak więc:
int &rSomeRef = someInt; // ok
int & rSomeRef = someInt; // ok
Listing 9.1. Tworzenie referencji i jej użycie
0: //Listing 9.1
1: // Demonstruje użycie referencji
2:
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int intOne;
9: int &rSomeRef = intOne;
10:
11: intOne = 5;
12: cout << "intOne: " << intOne << endl;
13: cout << "rSomeRef: " << rSomeRef << endl;
14:
15: rSomeRef = 7;
16: cout << "intOne: " << intOne << endl;
17: cout << "rSomeRef: " << rSomeRef << endl;
18:
19: return 0;
20: }
Wynik
intOne: 5
rSomeRef: 5
intOne: 7
rSomeRef: 7
Analiza
W linii 8. jest deklarowana lokalna zmienna intOne . W linii 9. referencja rSomeRef (jakaś
referencja) jest deklarowana i inicjalizowana tak, by odnosiła się do zmiennej intOne . Jeśli
zadeklarujesz referencję, lecz jej nie zainicjalizujesz, kompilator zgłosi błąd powstały podczas
kompilacji. Referencje muszą być zainicjalizowane.
W linii 11. zmiennej intOne jest przypisywana wartość 5 . W liniach 12. i 13. są wypisywane
wartości zmiennej intOne i referencji rSomeRef ; są one oczywiście takie same.
W linii 17. referencji rSomeRef jest przypisywana wartość 7 . Ponieważ jest to referencja, czyli
inna nazwa zmiennej intOne , w rzeczywistości wartość ta jest przypisywana tej zmiennej (co
potwierdzają komunikaty wypisywane w liniach 16. i 17.).
Użycie operatora adresu z referencją
Gdy pobierzesz adres referencji, uzyskasz adres jej celu. Wynika to z natury referencji (są one
aliasami dla obiektów docelowych). Pokazuje to listing 9.2.
Listing 9.2. Odczytywanie adresu referencji
0: //Listing 9.2
1: // Demonstruje użycie referencji
2:
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int intOne;
9: int &rSomeRef = intOne;
10:
11: intOne = 5;
12: cout << "intOne: " << intOne << endl;
13: cout << "rSomeRef: " << rSomeRef << endl;
14:
15: cout << "&intOne: " << &intOne << endl;
16: cout << "&rSomeRef: " << &rSomeRef << endl;
17:
18: return 0;
19: }
Wynik
intOne: 5
rSomeRef: 5
&intOne: 0012FF7C
&rSomeRef: 0012FF7C
UWAGA W twoim komputerze dwie ostatnie linie mogą wyglądać inaczej.
Analiza
W tym przykładzie referencja rSomeRef ponownie odnosi się do zmiennej intOne . Tym razem
jednak wypisywane są adresy obu zmiennych; są one identyczne. C++ nie umożliwia dostępu do
adresu samej referencji, gdyż jego użycie, w odróżnieniu od użycia adresu zmiennej, nie miałoby
sensu. Referencje są inicjalizowane podczas tworzenia i zawsze stanowią synonim dla swojego
obiektu docelowego (nawet gdy zostanie zastosowany operator adresu).
Na przykład, jeśli masz klasę o nazwie President , jej egzemplarz możesz zadeklarować
następująco:
President George_Washington;
Możesz wtedy zadeklarować referencję do klasy President i zainicjalizować ją tym obiektem:
President &FatherOfOurCountry = George_Washington;
Istnieje tylko jeden obiekt klasy President ; oba identyfikatory odnoszą się do tego samego
egzemplarza obiektu tej samej klasy. Wszelkie operacje, jakie wykonasz na zmiennej
FatherOfOurCountry (ojciec naszego kraju), będą odnosić się do obiektu
George_Washington .
Należy odróżnić symbol & w linii 9. listingu 9.2 (deklarujący referencję o nazwie rSomeRef ) od
symboli & w liniach 15. i 16., które zwracają adresy zmiennej całkowitej intOne i referencji
rSomeRef .
Zwykle w trakcie używania referencji nie używa się operatora adresu. Referencji używa się tak,
jak jej zmiennej docelowej. Pokazuje to linia 13.
Nie można zmieniać przypisania referencji
Nawet doświadczonym programistom C++, którzy wiedzą, że nie można zmieniać przypisania
referencji, gdyż jest ona aliasem swojego obiektu docelowego, zdarza się próba zmiany jej
przypisania. To, co wygląda w takiej sytuacji na ponowne przypisanie referencji, w rzeczywistości
jest przypisaniem nowej wartości obiektowi docelowemu. Przedstawia to listing 9.3.
Listing 9.3. Przypisanie do referencji
0: //Listing 9.3
1: //Ponowne przypisanie referencji
2:
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int intOne;
9: int &rSomeRef = intOne;
10:
11: intOne = 5;
12: cout << "intOne:\t" << intOne << endl;
13: cout << "rSomeRef:\t" << rSomeRef << endl;
14: cout << "&intOne:\t" << &intOne << endl;
15: cout << "&rSomeRef:\t" << &rSomeRef << endl;
16:
17: int intTwo = 8;
18: rSomeRef = intTwo; // to nie to o czym myślisz!
19: cout << "\nintOne:\t" << intOne << endl;
20: cout << "intTwo:\t" << intTwo << endl;
21: cout << "rSomeRef:\t" << rSomeRef << endl;
22: cout << "&intOne:\t" << &intOne << endl;
23: cout << "&intTwo:\t" << &intTwo << endl;
24: cout << "&rSomeRef:\t" << &rSomeRef << endl;
25: return 0;
26: }
Wynik
intOne: 5
rSomeRef: 5
&intOne: 0012FF7C
&rSomeRef: 0012FF7C
intOne: 8
intTwo: 8
rSomeRef: 8
&intOne: 0012FF7C
&intTwo: 0012FF74
&rSomeRef: 0012FF7C
Analiza
Także w tym programie zostały zadeklarowane (w liniach 8. i 9.) zmienna całkowita i referencja
do niej. W linii 11. zmiennej jest przypisywana wartość 5 , po czym w liniach od 12. do 15.
wypisywane są wartości i ich adresy.
W linii 17. tworzona jest nowa zmienna, intTwo , inicjalizowana wartością 8 . W linii 18.
programista próbuje zmienić przypisanie referencji rSomeRef tak, aby odnosiła się do zmiennej
intTwo , lecz mu się to nie udaje. W rzeczywistości referencja rSomeRef w dalszym ciągu jest
aliasem dla zmiennej intOne , więc to przypisanie stanowi ekwiwalent dla:
intOne = intTwo;
Potwierdzają to wypisywane w liniach 19. do 21. komunikaty, pokazujące wartości zmiennej
intOne i referencji rSomeRef . Ich wartości są takie same, jak wartość zmiennej intTwo . W
rzeczywistości, gdy w liniach od 22. do 24. są wypisywane adresy, okazuje się, że rSomeRef w
dalszym ciągu odnosi się do zmiennej intOne , a nie do zmiennej intTwo .
TAK
NIE
W celu stworzenia aliasu do obiektu używaj
referencji.
Nie zmieniaj przypisania referencji.
Nie myl operatora adresu z operatorem
referencji.
Inicjalizuj wszystkie referencje.
Do czego mogą odnosić się referencje?
Referencje mogą odnosić się do każdego z obiektów, także do obiektów zdefiniowanych przez
użytkownika. Zwróć uwagę, że referencja odnosi się do obiektu, a nie do klasy, do której ten
obiekt należy. Nie możesz napisać:
int & rIntRef = int; // źle
2971112.001.png
Zgłoś jeśli naruszono regulamin