Beruflich Dokumente
Kultur Dokumente
Referencje
W poprzednim rozdziale poznae wskaniki i dowiedziae si, jak za ich pomoc mona
operowa obiektami na stercie oraz jak odwoywa si do obiektw porednio. Referencje maj
prawie te same moliwoci, co wskaniki, ale posiadaj przy tym duo prostsz skadni.
Z tego rozdziau dowiesz si:
czym s referencje,
Odczytuje si to jako: rSomeRef jest referencj do wartoci zmiennej typu int. Ta referencja
zostaa zainicjalizowana tak, aby odnosia si do zmiennej someInt. Sposb 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 oczywicie s ze sob powizane).
Zastosowanie spacji przed operatorem referencji jest obowizkowe, uycie spacji pomidzy
operatorem referencji a nazw zmiennej referencyjnej jest opcjonalne. Tak wic:
int &rSomeRef = someInt; // ok
int & rSomeRef = someInt; // ok
//Listing 9.1
// Demonstruje uycie referencji
#include <iostream>
int main()
{
using namespace std;
int intOne;
int &rSomeRef = intOne;
intOne = 5;
cout << "intOne: " << intOne << endl;
cout << "rSomeRef: " << rSomeRef << endl;
rSomeRef = 7;
cout << "intOne: " << intOne << endl;
cout << "rSomeRef: " << rSomeRef << endl;
return 0;
}
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 odnosia si do zmiennej intOne. Jeli
zadeklarujesz referencj, lecz jej nie zainicjalizujesz, kompilator zgosi bd powstay podczas
kompilacji. Referencje musz by zainicjalizowane.
W linii 11. zmiennej intOne jest przypisywana warto 5. W liniach 12. i 13. s wypisywane
wartoci zmiennej intOne i referencji rSomeRef; s one oczywicie takie same.
W linii 17. referencji rSomeRef jest przypisywana warto 7. Poniewa jest to referencja, czyli
inna nazwa zmiennej intOne, w rzeczywistoci warto ta jest przypisywana tej zmiennej (co
potwierdzaj komunikaty wypisywane w liniach 16. i 17.).
//Listing 9.2
// Demonstruje uycie referencji
#include <iostream>
int main()
{
using namespace std;
int intOne;
int &rSomeRef = intOne;
intOne = 5;
cout << "intOne: " << intOne << endl;
cout << "rSomeRef: " << rSomeRef << endl;
cout << "&intOne: " << &intOne << endl;
cout << "&rSomeRef: " << &rSomeRef << endl;
return 0;
}
Wynik
intOne: 5
rSomeRef: 5
&intOne: 0012FF7C
&rSomeRef: 0012FF7C
Analiza
W tym przykadzie referencja rSomeRef ponownie odnosi si do zmiennej intOne. Tym razem
jednak wypisywane s adresy obu zmiennych; s one identyczne. C++ nie umoliwia dostpu do
adresu samej referencji, gdy jego uycie, w odrnieniu od uycia adresu zmiennej, nie miaoby
sensu. Referencje s inicjalizowane podczas tworzenia i zawsze stanowi synonim dla swojego
obiektu docelowego (nawet gdy zostanie zastosowany operator adresu).
Na przykad, jeli masz klas o nazwie President, jej egzemplarz moesz zadeklarowa
nastpujco:
President 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), bd odnosi si do obiektu
George_Washington.
Naley odrni symbol & w linii 9. listingu 9.2 (deklarujcy referencj o nazwie rSomeRef) od
symboli & w liniach 15. i 16., ktre zwracaj adresy zmiennej cakowitej intOne i referencji
rSomeRef.
Zwykle w trakcie uywania referencji nie uywa si operatora adresu. Referencji uywa si tak,
jak jej zmiennej docelowej. Pokazuje to linia 13.
//Listing 9.3
//Ponowne przypisanie referencji
#include <iostream>
int main()
{
using namespace std;
int intOne;
int &rSomeRef = intOne;
intOne = 5;
cout << "intOne:\t" << intOne << endl;
cout << "rSomeRef:\t" << rSomeRef << endl;
cout << "&intOne:\t" << &intOne << endl;
cout << "&rSomeRef:\t" << &rSomeRef << endl;
int intTwo = 8;
rSomeRef = intTwo; // to nie to o czym mylisz!
cout << "\nintOne:\t" << intOne << endl;
cout << "intTwo:\t" << intTwo << endl;
cout << "rSomeRef:\t" << rSomeRef << endl;
cout << "&intOne:\t" << &intOne << endl;
cout << "&intTwo:\t" << &intTwo << endl;
cout << "&rSomeRef:\t" << &rSomeRef << endl;
25:
26:
return 0;
}
Wynik
intOne: 5
rSomeRef:
&intOne:
&rSomeRef:
5
0012FF7C
0012FF7C
intOne: 8
intTwo: 8
rSomeRef:
&intOne:
&intTwo:
&rSomeRef:
8
0012FF7C
0012FF74
0012FF7C
Analiza
Take w tym programie zostay zadeklarowane (w liniach 8. i 9.) zmienna cakowita i referencja
do niej. W linii 11. zmiennej jest przypisywana warto 5, po czym w liniach od 12. do 15.
wypisywane s wartoci i ich adresy.
W linii 17. tworzona jest nowa zmienna, intTwo, inicjalizowana wartoci 8. W linii 18.
programista prbuje zmieni przypisanie referencji rSomeRef tak, aby odnosia si do zmiennej
intTwo, lecz mu si to nie udaje. W rzeczywistoci referencja rSomeRef w dalszym cigu jest
aliasem dla zmiennej intOne, wic to przypisanie stanowi ekwiwalent dla:
intOne = intTwo;
NIE
Referencje do obiektw s uywane w taki sam sposb, jak obiekty. Dane i funkcje skadowe s
dostpne poprzez ten sam operator dostpu do skadowych (.) i, podobnie jak w typach
wbudowanych, referencja dziaa jak inna nazwa obiektu. Ilustruje to listing 9.4.
Listing 9.4. Referencje do obiektw
0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
// Listing 9.4
// Referencje do obiektw klas
#include <iostream>
class SimpleCat
{
public:
SimpleCat (int age, int weight);
~SimpleCat() {}
int GetAge() { return itsAge; }
int GetWeight() { return itsWeight; }
private:
int itsAge;
int itsWeight;
};
SimpleCat::SimpleCat(int age, int weight)
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
{
itsAge = age;
itsWeight = weight;
}
int main()
{
SimpleCat Mruczek(5,8);
SimpleCat & rCat = Mruczek;
std::cout
std::cout
std::cout
std::cout
return 0;
<<
<<
<<
<<
Wynik
Mruczek ma: 5 lat.
i wazy: 8 funtow.
Analiza
W linii 25. zmienna Mruczek jest deklarowana jako obiekt klasy SimpleCat (prostyzwyky kot).
W linii 26. jest deklarowana referencja rCat do obiektu klasy SimpleCat, ktra odnosi si do
obiektu Mruczek. W liniach 29. i 31. s wykorzystywane akcesory klasy SimpleCat, najpierw
poprzez obiekt klasy, a potem poprzez referencj. Zwr uwag, e dostp do nich jest identyczny.
Take w tym przypadku referencja jest inn nazw (aliasem) rzeczywistego obiektu.
Referencje
Referencj deklaruje si, zapisujc typ, operator referencji (&) oraz nazw referencji. Referencje
musz by inicjalizowane w trakcie ich tworzenia.
Przykad 1
int hisAge;
int &rAge = hisAge;
Przykad 2
CAT Filemon;
CAT &rCatRef = Filemon;
4:
5:
6:
7:
8:
9:
10:
11:
" y:
12:
13:
y: "
14:
15:
16:
17:
18:
19:
20:
21:
<< y
22:
23:
24:
25:
26:
27:
y <<
28:
29:
Wynik
Funkcja
Funkcja
Funkcja
Funkcja
main().
swap().
swap().
main().
Analiza
Wewntrz funkcji main() program inicjalizuje dwie zmienne i przekazuje je funkcji swap()
(zamie), ktra wydaje si je zamienia. Jednak gdy ponownie sprawdzimy ich wartoci w funkcji
main(), okae si, e nie ulegy one zmianie!
Problem polega na tym, e zmienne x i y s przekazywane funkcji swap() poprzez warto.
Oznacza to, e wewntrz tej funkcji s tworzone ich lokalne kopie. Nam potrzebne jest
przekazanie zmiennych x i y przez referencj.
W C++ istniej dwie moliwoci rozwizania tego problemu: parametry funkcji swap() moesz
zamieni na wskaniki do oryginalnych wartoci, lub przekaza referencje do pierwotnych
wartoci.
Wynik
Funkcja
Funkcja
Funkcja
Funkcja
main().
swap().
swap().
main().
Analiza
Udao si! W linii 5. zosta zmieniony prototyp funkcji swap(), w ktrym zadeklarowano e oba
parametry funkcji s wskanikami do zmiennych typu int, a nie zmiennymi tego typu. Gdy w
linii 12. nastpuje wywoanie funkcji swap(), jako argumenty s jej przekazywane adresy
zmiennych x i y.
W linii 19., w funkcji swap(),deklarowana jest lokalna zmienna temp1. Ta zmienna nie musi by
wskanikiem; w czasie ycia funkcji swap() przechowuje ona warto *px (tj. warto zmiennej
x zadeklarowanej w funkcji wywoujcej). Gdy funkcja swap() zakoczy dziaanie, zmienna
temp nie bdzie ju potrzebna.
W linii 24. zmiennej temp przypisywana jest warto wskazywana przez px. W linii 25. zmiennej
wskazywanej przez px przypisywana jest warto wskazywana przez py. W linii 26.
zmiennejwarto umieszczona przechowywana w zmiennej temp (tj. oryginalna warto
wskazywana przez px) jest umieszczana w zmiennej wskazywanej przez py.
Efektem przeprowadzonych przez nas dziaa jest zamiana wartoci tych zmiennych, ktrych
adresy zostay przekazane do funkcji swap().
Ta nazwa jest skrtem od sowa temporary (tymczasowa) i bardzo czsto wystpuje w programach.
przyp.tum.
22:
23:
24:
25:
26:
27:
"
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
Wynik
Funkcja
Funkcja
Funkcja
Funkcja
main().
swap().
swap().
main().
Analiza
Podobnie, jak w przykadzie ze wskanikami, take i tu (w linii 10.) deklarowane s dwie
zmienne, ktrych wartoci wypisywane s w linii 12. W linii 15. nastpuje wywoanie funkcji
swap(), ale zwr uwag, e tym razem nie s przekazywane adresy zmiennych x i y, lecz same
zmienne. Funkcja wywoujca po prostu przekazuje zmienne.
Gdy wywoywana jest funkcja swap(), dziaanie programu przechodzi do linii 23., w ktrej
zmienne zostaj zidentyfikowane jako referencje. Ich wartoci s wypisywane w linii 27., zwr
uwag, e nie wymagaj one przeprowadzania adnych dodatkowych operacji. S to aliasy
oryginalnych wartoci, ktre mog zosta uyte jako te wartoci.
W liniach od 30. do 32. wartoci s zamieniane, a nastpnie ponownie wypisywane w linii 35.
Wykonanie programu wraca do funkcji wywoujcej, zatem funkcja main() (w linii 17.)
ponownie wypisuje wartoci zmiennych. Poniewa parametry funkcji swap() zostay
zadeklarowane jako referencje, wartoci w funkcji main() zostay przekazane przez referencj,
dlatego s zamienione rwnie w tej funkcji.
Referencje uatwiaj korzystanie z normalnych zmiennych, zachowujc przy tym moliwo
przekazywania argumentw poprzez referencj.
przekazywane poprzez warto, czy poprzez referencj? Jako klient (czyli uytkownik) funkcji
swap(), programista musi mie pewno, e funkcja ta faktycznie zamieni swoje parametry.
Oto kolejne zastosowanie prototypw funkcji. Sprawdzajc parametry zadeklarowane w
prototypie, ktry zwykle znajduje si w pliku nagwkowym wraz z innymi prototypami,
programista wie, e wartoci przekazywane do funkcji swap() s przekazywane poprzez
referencj i wie, jak powinien ich uy.
Gdyby funkcja swap() bya czci klasy, informacji tych dostarczyaby deklaracja klasy, take
umieszczana zwykle w pliku nagwkowym.
W jzyku C++ wszystkich informacji potrzebnych klientom klas i funkcji mog dostarczy pliki
nagwkowe; peni one rol interfejsu dla klasy lub funkcji. Implementacja jest natomiast
ukrywana przed klientem. Dziki temu programista moe skupi si na analizowanym aktualnie
problemie i korzysta z klasy lub funkcji bez zastanawiania si, w jaki sposb ona dziaa.
Gdy John Roebling projektowa Most Brookliski, zajmowa si takimi szczegami, jak sposb
wylewania betonu czy metoda produkcji drutu do kabli nonych. Zna kady fizyczny i chemiczny
proces zwizany z tworzeniem materiaw przeznaczonych do budowy mostu. Obecnie
inynierowie oszczdzaj czas, uywajc dobrze znanych materiaw budowlanych, nie
zastanawiajc si, w jaki sposb s one tworzone przez producenta.
Jzyka C++ umoliwia programistom korzystanie z dobrze znanych klas i funkcji, bez
koniecznoci zajmowania si szczegami ich dziaania. Te czci skadowe zostay
zoonemog zosta poczone w celu stworzenia programu (podobnie jak czone s kable, rury,
klamry i inne czci w celu stworzenia mostu czy budynku).
Inynier przegldajcy specyfikacj betonu w celu poznania jego wytrzymaoci, ciaru
wasnego, czasu krzepnicia, itd., a programista przeglda interfejs funkcji lub klasy w celu
poznania usug, jakich ona dostarcza, parametrw, ktrych potrzebuje i wartoci, jakie zwraca.
//Listing 9.8
// Zwracanie kilku wartoci z funkcji
#include <iostream>
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
}
short Factor(int n, int *pSquared, int *pCubed)
{
short Value = 0;
if (n > 20)
Value = 1;
else
{
*pSquared = n*n;
*pCubed = n*n*n;
Value = 0;
}
return Value;
}
Wynik
Wpisz liczbe (0 - 20): 3
liczba: 3
do kwadratu: 9
do trzeciej potegi: 27
Analiza
W linii 10. zostay zadeklarowane trzy krtkie zmienne cakowite: number (liczba), squared (do
kwadratu) oraz cubed (do trzeciej potgi). Warto zmiennej number jest wpisywana przez
uytkownika. Ta liczba oraz adresy zmiennych squared i cubed s przekazywane do funkcji
Factor() (czynnik).
Funkcja Factor() sprawdza pierwszy parametr, ktry jest przekazywany przez warto. Jeli jest
wikszy od 20 (maksymalnej wartoci, jak moe obsuy funkcja), zwracanej wartoci Value
(warto) przypisywany jest prosty kod bdu. Zwr uwag, e warto zwracana funkcjia
Factor() jest zarezerwowana dla moe zwrotuci t albo tej wartoci bdu lubalbo wartoci
0, oznaczajcej, e wszystko poszo dobrze; warto t funkcja zwraca w linii 40.
Obliczane w funkcji wartoci, czyli podniesiona do potgi drugiej i do trzeciej potgi liczba, s
zwracane nie poprzez instrukcj return, ale bezporednio poprzez zmian wartoci zmiennych
wskazywanych przez wskaniki przekazane do funkcji.
W liniach 36. i 37. zmiennym wskazywanym poprzez wartociomwskaniki przypisywane s
wyobliczone wartoci wczeniej. W linii 38. zmiennej Value jest przypisywany kod sukcesu,
ktry jest zwracany w linii 40.
Jednym z ulepsze wprowadzonych do tej funkcji mogaoby by napisaniedeklaracja:
enum ERROR_VALUE { SUCCESS, FAILURE};
Dziki temu, zamiast zwraca wartoci 0 lub 1, program mgby zwraca odpowiedni warto
sta typu wyliczeniowegoa ERROR_VALUE (warto bdu), czyli albo SUCCESS (sukces) lubalbo
FAILURE (poraka).
//Listing 9.9
// Zwracanie kilku wartoci z funkcji
// z zastosowaniem referencji
#include <iostream>
using namespace std;
typedef unsigned short USHORT;
enum ERR_CODE { SUCCESS, ERROR };
ERR_CODE Factor(USHORT, USHORT&, USHORT&);
int main()
{
USHORT number, squared, cubed;
ERR_CODE result;
cout << "Wpisz liczbe (0 - 20): ";
cin >> number;
result = Factor(number, squared, cubed);
if (result == SUCCESS)
{
cout << "liczba: " << number << "\n";
cout << "do kwadratu: " << squared << "\n";
cout << "do trzeciej potegi: " << cubed
<< "\n";
}
else
cout << "Napotkano blad!!\n";
return 0;
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
}
ERR_CODE Factor(USHORT n, USHORT &rSquared, USHORT &rCubed)
{
if (n > 20)
return ERROR;
// prosty kod bdu
else
{
rSquared = n*n;
rCubed = n*n*n;
return SUCCESS;
}
}
Wynik
Wpisz liczbe (0 - 20): 3
liczba: 3
do kwadratu: 9
do trzeciej potegi: 27
Analiza
Listing 9.9 jest prawie identyczny z listingiem 9.8, z dwiema rnicami. Dziki zastosowaniu
wyliczenia ERR_CODE zgaszanie bdw w liniach 36. i 41., a take ich obsuga w linii 22., s
bardziej przejrzyste.
Istotn zmian jest to, e tym razem funkcja Factor() zostaa zadeklarowana jako przyjmujca
referencje, a nie wskaniki, do zmiennych squared i cubed. Dziki temu operowanie tymi
parametrami jest prostsze i bardziej zrozumiae.
//Listing 9.10
// Przekazywanie wskanikw do obiektw
#include <iostream>
using namespace std;
class SimpleCat
{
public:
SimpleCat ();
SimpleCat(SimpleCat&);
~SimpleCat();
};
// konstruktor
// konstruktor kopiujcyi
// destruktor
SimpleCat::SimpleCat()
{
cout << "Konstruktor klasy SimpleCat...\n";
}
SimpleCat::SimpleCat(SimpleCat&)
{
cout << "Konstruktor kopiujcyi klasy SimpleCat...\n";
}
SimpleCat::~SimpleCat()
{
cout << "Destruktor klasy SimpleCat...\n";
}
SimpleCat FunctionOne (SimpleCat theCat);
SimpleCat* FunctionTwo (SimpleCat *theCat);
int main()
{
cout << "Tworze obiekt...\n";
SimpleCat Mruczek;
cout << "Wywoluje funkcje FunctionOne...\n";
FunctionOne(Mruczek);
cout << "Wywoluje funkcje FunctionTwo...\n";
FunctionTwo(&Mruczek);
return 0;
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
}
// FunctionOne, parametr przekazywany poprzez warto
SimpleCat FunctionOne(SimpleCat theCat)
{
cout << "FunctionOne. Wracam...\n";
return theCat;
}
// FunctionTwo, parametr przekazywany poprzez wskanik
SimpleCat* FunctionTwo (SimpleCat *theCat)
{
cout << "FunctionTwo. Wracam...\n";
return theCat;
}
Wynik
Tworze obiekt...
Konstruktor klasy SimpleCat...
Wywoluje funkcje FunctionOne...
Konstruktor kopiujcyi klasy SimpleCat...
FunctionOne. Wracam...
Konstruktor kopiujcyi klasy SimpleCat...
Destruktor klasy SimpleCat...
Destruktor klasy SimpleCat...
Wywoluje funkcje FunctionTwo...
FunctionTwo. Wracam...
Destruktor klasy SimpleCat...
Analiza
W liniach od 6. do 12. zostaa zadeklarowana bardzo uproszczona klasa SimpleCat. Zarwno
konstruktor, jak i konstruktor kopiujcyi oraz destruktor wypisuj odpowiednie dla siebie
komunikaty, dziki ktrym wiadomo, w ktrym momencie zostay wywoane.
W linii 34. funkcja main() wypisuje komunikat widoczny w pierwszej linii wyniku. W linii 35.
tworzony jest egzemplarz obiektu klasy SimpleCat. Powoduje to wywoanie konstruktora tej
klasy, co potwierdza druga linia wyniku.
W linii 36. funkcja main() zgasza (poprzez wypisanie komunikatu w trzeciej linii wydruku), e
wywouje funkcj FunctionOne., ktra wypisuje komunikat w trzeciej linii wyniku. Poniewa ta
funkcja otrzymuje obiekt typu SimpleCat przekazywany poprzez warto, na stosie tworzona jest
lokalna dla tej funkcji kopia obiektu klasy SimpleCat. To powoduje wywoanie konstruktora
kopiujcegoi, ktry wypisuje czwart lini wyniku.
Wykonanie programu przechodzi do wywoywanej funkcji, do linii 46., w ktrej wypisywany jest
komunikat informacyjny, stanowicy pit lini wyniku. Nastpnie funkcja wraca i zwraca obiekt
typu SimpleCat poprzez warto. To powoduje utworzenie kolejnej kopii obiektu (cznie
zpoprzez wywoaniem konstruktora kopiujcegoi, wypisujcego te szst lini wyniku).
Warto zwracana przez funkcj FunctionOne() nie jest niczemu przypisywana, wic
tymczasowy obiekt utworzony na stosie jest odrzucany, co powoduje wywoanie destruktora, ktry
wypisuje sidm lini wyniku. Poniewa dziaanie funkcji FunctionOne() si zakoczyo, jej
lokalna kopia obiektu wychodzi z zakresu i jest niszczona; powoduje to wywoanie destruktora i
wypisanie smej linii wyniku.
Program wraca do funkcji main(), w ktrej zostaje teraz wywoana funkcja FunctionTwo(),
lecz tym razem jej parametr jest przekazywany przez referencj. Nie jest tworzona adna kopia,
dlatego nie jest wypisywany aden komunikat konstruktora. Funkcja FunctionTwo() wypisuje
jedynie wasny komunikat w dziesitej linii wyniku, po czym zwraca obiekt typu SimpleCat,
take poprzez wskanik, zatem take tym razem nie jest wywoywany konstruktor ani destruktor.
Program koczy swoje dziaanie i obiekt Mruczek wychodzi z zakresu, powodujc jeszcze jedno
wywoanie destruktora, wypisujcego komunikat w jedenastej linii wyniku.
Poniewa parametr funkcji FunctionOne() jest przekazywany i zwracany przez warto, jej
wywoanie wie si z dwoma wywoaniami konstruktora kopiujcegoi i dwoma wywoaniami
destruktora; natomiast wywoanie funkcji FunctionTwo() nie wymagao wywoania ani
konstruktora, ani destruktora.
//Listing 9.11
// Przekazywanie wskanikw do obiektw
#include <iostream>
using namespace std;
class SimpleCat
{
public:
SimpleCat();
SimpleCat(SimpleCat&);
~SimpleCat();
int GetAge() const { return itsAge; }
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
Wynik
Tworze obiekt...
Konstruktor klasy SimpleCat...
Mruczek ma 1 lat
Mruczek ma 5 lat
Wywoluje funkcje FunctionTwo...
FunctionTwo. Wracam...
Mruczek ma teraz 5 lat
Mruczek ma 5 lat
Destruktor klasy SimpleCat...
Analiza
Klasa SimpleCat zawiera dwa akcesory: GetAge() w linii 13., bdcy funkcj const oraz
SetAge() w linii 14., nie bdcy funkcj const. Oprcz tego posiada zmienn skadow
itsAge, deklarowan w linii 17.
Konstruktor, konstruktor kopiujcyi oraz destruktor wypisuj odpowiednie komunikaty. Jednak
konstruktor kopiujcyi nie jest wywoywany, gdy obiekt przekazywany jest poprzez referencj i
nie jest tworzona adna kopia. Na pocztku programu, w linii 42., tworzony jest obiekt, a w
liniachi od 43. do 45. jest wypisywany wiek pocztkowy.
W linii 47. zmienna skadowa itsAge jest ustawiana za pomoc akcesora SetAge(), za wynik
jest wypisywany w liniachi od 48. do 50. W tym programie nie jest uywana funkcja
FunctionOne(). i Pposugujemy si tylko funkcj FunctionTwo(). Ulega ona jednak
niewielkiej zmianie; jej nagwek zosta zmodyfikowany tak, e funkcja przyjmuje teraz stay
wskanik do staego obiektu i zwraca stay wskanik do staego obiektu.
Poniewa parametr i warto zwracana otna wci s przekazywane poprzez referencje, nie s
tworzone adne kopie, nie jest zatem wywoywany konstruktor kopiujcyi. Jednak obecnie obiekt
wskazywany w funkcji FunctionTwo() jest obiektem const, wic nie mona wywoywa jego
metod, nie bdcych metodami const, czyli nie mona wywoa jego metody SetAge(). Gdyby
wywoanie tej metody w linii 66. nie zostao umieszczone w komentarzu, program nie
skompilowaby si.
Zwr uwag, e obiekt tworzony w funkcji main() nie jest const, wic moemy dla niego
wywoa funkcj SetAge(). Do funkcji FunctionTwo()przekazywany jest adres tego zwykego
obiektu, ale poniewa deklaracja tej funkcji okrela, e ten parametr jest wskanikiem const do
obiektu const, obiekt ten jest traktowany, jakby by stay!
//Listing 9.12
// Przekazywanie wskanikw do obiektw
#include <iostream>
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
Wynik
Tworze obiekt...
Konstruktor klasy SimpleCat...
Mruczek ma 1 lat
Mruczek ma 5 lat
Wywoluje funkcje FunctionTwo...
FunctionTwo. Wracam...
Mruczek ma teraz 5 lat
Mruczek ma 5 lat
Destruktor klasy SimpleCat...
Analiza
Wynik jest identyczny z wynikiem z listingu 9.11. Jedyn istotn rnic w programie jest to, e
obecnie funkcja FunctionTwo() otrzymuje i zwraca referencj do staego obiektu. Take tym
razem praca z referencjami jest nieco prostsza od pracy ze wskanikami, a na dodatek zapewnia t
sam efektywno oraz bezpieczestwo obiektu const.
Referencje const
Programici C++ zwykle nie uznaj rnicy pomidzy sta referencj do obiektu typu
SimpleCat a referencj do staego obiektu typu SimpleCat. Referencje nigdy nie mog
otrzyma ponownego przypisania i odnosi si do innego obiektu, wic s zawsze stae. Jeli
sowo kluczowe const zostanie zastosowane w odniesieniu do referencji, sprawi, e to obiekt
zwizany z referencj staje si stay.
W tym przykadzie deklarowany jest wskanik pInt do typu int; jest on inicjalizowany adresem
pamici zwracanym przez operator new. Nastpnie jest sprawdzany adres w pInt i jeli nie jest on
pusty, wyuskiwany jest wskanik. Rezultatem wyuskania wskanika do typu int jest obiekt int,
wic referencja rInt jest inicjalizowana jako odnoszca si do tego obiektu. W efekcie, referencja
rInt staje si aliasem do wartoci int o adresie zwrconym przez operator new.
TAK
NIE
UWAGA Biae spacje s cakowicie ignorowane, dlatego wszdzie tam, gdzie mona umieci
spacj, mona take umieci dowoln ilo innych spacji, tabulatorw czy nowych linii.
Argumentem przeciwko przypadkowi 1. jest to, e typem jest klasa CAT. Symbol & jest czci
deklaratora zawierajcego nazw klasy i znak ampersand (&). Jednak umieszczenie & przy
CAT moe spowodowa wystpienie poniszego bdu:
CAT& rMruczek, rFilemon;
Szybkie sprawdzenie tej linii moe doprowadzi ci do odkrycia, e zarwno rMruczek, jak i
rFilemon s referencjami do obiektw klasy CAT, ale w rzeczywistoci tak nie jest. Ta
deklaracja informuje, e rMruczek jest referencj do klasy CAT, za rFilemon (mimo
zastosowanego przedrostka) nie jest referencj, lecz zwykym obiektem klasy CAT. T
deklaracj naley przepisa nastpujco:
CAT
&rMruczek, rFilemon;
Wielu programistw optuje za zastosowaniem operatora porodku, tak jak pokazuje przypadek
2.
Oczywicie, wszystko, co powiedziano dotd na temat operatora referencji (&), odnosi si take
do operatora wskanika (*). Naley zdawa sobie spraw, e styl zapisu zaley od programisty.
Wybierz wic styl, ktry ci odpowiada i konsekwentnie stosuj go w programach; przejrzysto
kodu jest w kocu jednym z twoich gwnych celw.
1.
2.
Wynik
// Listing 9.13
// Zwracanie referencji do obiektu
// ktry ju nie istnieje
#include <iostream>
class SimpleCat
{
public:
SimpleCat (int age, int weight);
~SimpleCat() {}
int GetAge() { return itsAge; }
int GetWeight() { return itsWeight; }
private:
int itsAge;
int itsWeight;
};
SimpleCat::SimpleCat(int age, int weight)
{
itsAge = age;
itsWeight = weight;
}
SimpleCat &TheFunction();
int main()
{
SimpleCat &rCat = TheFunction();
int age = rCat.GetAge();
std::cout << "rCat ma " << age << " lat!\n";
return 0;
}
SimpleCat &TheFunction()
{
SimpleCat Mruczek(5,9);
return Mruczek;
}
Analiza
W liniach od 7. do 17. deklarowana jest klasa SimpleCat. W linii 29. referencja do klasy
SimpleCat jest inicjalizowana rezultatem wywoania funkcji TheFunction(), zadeklarowanej
w linii 25. jako zwracajca referencj do obiektw klasy SimpleCat.
W ciele funkcji TheFunction() jest deklarowany lokalny obiekt typu SimpleCat; konstruktor
inicjalizuje jego wiek i wag. Nastpnie ten obiekt lokalny jest zwracany poprzez referencj.
Niektre kompilatory s na tyle inteligentne, by wychwyci ten bd i nie pozwoli na
uruchomienie programu. Inne pozwol na jego skompilowanie i uruchomienie, co moe
spowodowa nieprzewidywalne zachowanie komputera.
Gdy funkcja TheFunction() koczy dziaanie, jej obiekt lokalny, Mruczek, jest niszczony
(zapewniam, e bezbolenie). Referencja zwracana przez t funkcj staje si aliasem do
nieistniejcego obiektu, a to powany bd.
// Listing 9.14
// Unikanie wyciekw pamici
#include <iostream>
class SimpleCat
{
public:
SimpleCat (int age, int weight);
~SimpleCat() {}
int GetAge() { return itsAge; }
int GetWeight() { return itsWeight; }
private:
int itsAge;
int itsWeight;
};
SimpleCat::SimpleCat(int age, int weight)
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
{
itsAge = age;
itsWeight = weight;
}
SimpleCat & TheFunction();
int main()
{
SimpleCat & rCat = TheFunction();
int age = rCat.GetAge();
std::cout << "rCat ma " << age << " lat!\n";
std::cout << "&rCat: " << &rCat << std::endl;
// jak si go pozbdziesz z pamici?
SimpleCat * pCat = &rCat;
delete pCat;
// a do czego teraz odnosi si rCat??
return 0;
}
SimpleCat &TheFunction()
{
SimpleCat * pMruczek = new SimpleCat(5,9);
std::cout << "pMruczek: " << pMruczek << std::endl;
return *pMruczek;
}
Wynik
pMruczek: 004800F0
rCat ma 5 lat!
&rCat: 004800F0
OSTRZEENIE Ten program kompiluje si, uruchamia i sprawia wraenie, e dziaa
poprawnie. Jest jednak swego rodzaju bomb zegarow, ktra moe w kadej chwili
wybuchn.
si referencja rCat po wykonaniu linii 34.? Jak ju wspomnielimy, referencja zawsze musi
stanowi alias rzeczywistego obiektu; jeli odnosi si do obiektu pustego (tak, jak w tym
przypadku), program jest bdny.
UWAGA Jeszcze raz naley przypomnie, e program z referencj do pustego obiektu moe si
skompilowa, ale jest bdny i jego dziaanie jest nieprzewidywalne.
Istniej trzy rozwizania tego problemu. Pierwszym jest zadeklarowanie obiektu typu SimpleCat
w linii 28. i zwrot tego obiektu z funkcji TheFunction() poprzez warto. Drugim jest
zadeklarowanie w funkcji TheFunction() obiektu SimpleCat na stercie, lecz ze zwrceniem
wskanika. Wtedy funkcja wywoujca moe sama usun ten wskanik gdy, nie bdzie ju
potrzebowa obiektu.
Trzecim rozwizaniem, tym waciwym, jest zadeklarowanie obiektu w funkcji wywoujcej i
przekazanie go funkcji TheFunction() przez referencj.
NIE