Professional Documents
Culture Documents
programowania
Pawe Mikoajczak
Lublin 2011
Wydawca
Uniwersytet Marii Curie-Skodowskiej w Lublinie
Instytut Informatyki
pl. Marii Curie-Skodowskiej 1, 20-031 Lublin
Redaktor serii: prof. dr hab. Pawe Mikoajczak
www: informatyka.umcs.lublin.pl
email: dyrii@hektor.umcs.lublin.pl
Druk
ESUS Agencja Reklamowo-Wydawnicza Tomasz Przybylak
ul. Ratajczaka 26/8
61-815 Pozna
www: www.esus.pl
ISBN: 978-83-62773-11-4
Spis treci
Przedmowa
ix
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
2
4
6
7
7
11
13
14
16
17
23
24
Wstp . . . . . . . . . . . . .
Paradygmaty programowania
Obiekty i klasy . . . . . . . .
Hermetyzacja danych . . . . .
Dziedziczenie . . . . . . . . .
Polimorzm . . . . . . . . . .
Podsumowanie terminologii .
rodowisko programistyczne .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
27
28
28
30
34
35
36
37
38
. . . . . .
. . . . . .
obiektu)
. . . . . .
. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
42
42
43
43
47
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 Klasy i obiekty
3.1.
3.2.
3.3.
3.4.
3.5.
Wstp . . . . . . . . . . . . . .
Deklaracja i denicja klasy . .
Wystpienie klasy (deniowanie
Dostp do elementw klasy . .
Metody klasy . . . . . . . . . .
vi
SPIS TRECI
3.6. Klasa z akcesorami . . . . . . . . . . . . . . . . . . . . . . . . 50
3.7. Funkcje skadowe const . . . . . . . . . . . . . . . . . . . . . 53
4 Konstruktory i destruktory
4.1. Wstp . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2. Inicjalizacja obiektu klasy . . . . . . . . . . . . . . .
4.3. Konstruktory i destruktory domylne . . . . . . . . .
4.4. Konstruktor jawny . . . . . . . . . . . . . . . . . . .
4.5. Wywoywanie konstruktorw i destruktorw . . . . .
4.6. Rozdzielenie interfejsu od implementacji . . . . . . .
4.7. Wskanik do obiektu this . . . . . . . . . . . . . . .
4.8. Wskanik this kaskadowe wywoania funkcji . . . .
4.9. Tablice obiektw . . . . . . . . . . . . . . . . . . . .
4.10. Inicjalizacja tablic obiektw nie bdcych agregatami
4.11. Tablice obiektw tworzone dynamicznie . . . . . . .
4.12. Kopiowanie obiektw . . . . . . . . . . . . . . . . . .
4.13. Klasa z obiektami innych klas . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
57
58
58
59
61
63
65
67
68
71
73
75
77
78
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
83
84
84
88
92
98
104
117
Wstp . . . . . . . . . . . . . . . . . . . . .
Hierarchiczna struktura dziedziczenia . . . .
Notacja UML (Unied Modeling Language)
Proste klasy pochodne . . . . . . . . . . . .
Konstruktory w klasach pochodnych . . . .
Dziedziczenie kaskadowe . . . . . . . . . . .
Dziedziczenie wielokrotne bezporednie . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6 Funkcje zaprzyjanione
6.1.
6.2.
6.3.
6.4.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
121
122
123
134
141
Wstp . . . . . . . . . . . . . . . . . . . . . . . . . . .
Denicje . . . . . . . . . . . . . . . . . . . . . . . . . .
Przeciony operator dodawania ( + ) . . . . . . . . .
Przeciony operator mnoenia ( * ) . . . . . . . . . .
Funkcja operatorowa w postaci niezalenej funkcji . .
Przecianie operatorw rwnoci i nierwnoci . . . .
Przecianie operatora przypisania ( = ) . . . . . . . .
Przecianie operatora wstawiania do strumienia ( )
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
143
144
144
147
150
154
161
165
173
Wstp . . . . . . . . . . . . . . . . . . . . . .
Funkcja niezalena zaprzyjaniona z klas . .
Funkcja skadowa zaprzyjaniona z inn klas
Klasy zaprzyjanione . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7 Przecianie operatorw
7.1.
7.2.
7.3.
7.4.
7.5.
7.6.
7.7.
7.8.
vii
SPIS TRECI
Wstp . . . . . . . . . .
Metody i dane statyczne
Polimorzm . . . . . . .
Funkcje wirtualne . . . .
Funkcje abstrakcyjne . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
177
178
178
183
187
194
203
9.1. Wstp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
9.2. Klasy wirtualne . . . . . . . . . . . . . . . . . . . . . . . . . . 204
9.3. Klasy zagniedone . . . . . . . . . . . . . . . . . . . . . . . . 212
10 Wskaniki do klas
217
10.1. Wstp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
10.2. Wskaniki klasowe . . . . . . . . . . . . . . . . . . . . . . . . 218
10.3. Wskaniki skadowych klas . . . . . . . . . . . . . . . . . . . 224
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
229
230
230
234
237
239
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
265
266
266
276
292
12 Szablony w C++
12.1. Wstp . . . . . . . .
12.2. Przecianie funkcji
12.3. Szablony funkcji . .
12.4. Szablony klas . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Sownik angielsko-polski
307
Skorowidz
325
Przedmowa
Jzyk C++ jest uznawany za najlepszy jzyk do programowania obiektowego. Jest on nadzbiorem jzyka C. Powsta w Bell Labs (USA), gdzie
Bjarne Stroustrup stworzy go na pocztku lat osiemdziesitych. Potem jzyk C++ by wiele lat rozwijany. Prace nad standardem jzyka zaczy si
w roku 1990. Ostatecznie, po rozszerzeniu jzyka o wyjtki, RTTI, szablony
oraz bibliotek STL w 1998 roku przyjto midzynarodowy standard (ISO/IEC 14882:1998). W roku 2003 pojawia si druga wersja standardu, ale
zawieraa ona tylko drobne korekty.
Trudno sobie obecnie wyobrazi zawodowego programist, ktry nie zna
przynajmniej podstaw jzyka C++.
Jzyk C++ jest jzykiem skomplikowanym i trudnym, niemniej moliwym do opanowanie. Jak pisze twrca jzyka, B. Stroustrup w swoim najnowszym podrczniku opublikowanym w 2009 roku, z ponad tysica studentw pierwszego roku, ktrych uczy w Texas A&M University wikszo z
nich odniosa sukces (zdaa egzaminy) mimo, e 40 procent z nich nie miao
nigdy do czynienia z programowaniem. To wyznanie wybitnego fachowca
jest optymistyczne. Naley pamita, e klasyczne i kompletne podrczniki
programowania s bardzo obszerne (podrcznik B. Stroustrupa liczy sobie 1106 stron, a podrcznik S. Prata 1303 strony). Skrypt akademicki
z natury rzeczy jest tylko wprowadzeniem do tematyki przedmiotu, ma za
zadnie przedstawi wstpne i elementarne zagadnienia zwizane z omawian
dziedzin, std jego objto nie moe by zbyt dua.
Na potrzeby studentw kierunku informatyka opracowalimy ten podrcznik wybierajc do arbitralnie prezentowany materia. Prezentujc jzyk C++, zakadamy, e studenci znaj jzyk C.
Niniejszy podrcznik jest przeznaczony gwnie dla studentw 3-letnich
studiw zawodowych (licencjatw) a take dla studentw 2-letnich studiw
uzupeniajcych. Podrcznik powsta na podstawie notatek wykorzystywanych przeze mnie w trakcie prowadzenia wykadw z przedmiotw Jzyki
programowania, oraz Jzyk C i C++ dla studentw UMCS w latach
1995 2005 i dalszych.
W podrczniku przyjem zasad, e nauka programowania oparta jest
Przedmowa
na licznych przykadach, ktre ilustruj praktyczne zastosowania paradygmatw programowania i skadni jzyka C++.
Podrcznik ma by pomoc dydaktyczn wspierajc nauk programowania realizowana w ramach 30-godzinnego wykadu i wicze laboratoryjnych. Naley pamita, e podrcznik nie stanowi penego kompendium
wiedzy o jzyku C++, jest jedynie prostym i przystpnym wprowadzeniem
w fascynujcy wiat pisania efektywnych i efektownych programw komputerowych. Wszystkie przykady programw, ktre zamieciem w tym
podrczniku zostay przetestowane za pomoc kompilatora C++ Builder 6
rmy Borland, na platformie Windows. Wikszo programw sprawdzana
take bya na platformie Linuksowej oraz QT.
Rozdzia 1
Specyficzne elementy jzyka C++
1.1.
1.2.
1.3.
1.4.
1.5.
1.6.
1.7.
1.8.
1.9.
1.10.
1.11.
1.12.
1.13.
Wstp . . . . . . . . . . . . . . . . . . . . . .
Rys historyczny . . . . . . . . . . . . . . . .
Strumienie wejcia/wyjcia w C++ . . . . .
Nowa posta komentarzy . . . . . . . . . . .
Dowolne rozmieszczenie deklaracji . . . . . .
Przekazywanie argumentw przez referencje .
Argumenty domniemane . . . . . . . . . . .
Przecianie funkcji . . . . . . . . . . . . . .
Wzorce funkcji . . . . . . . . . . . . . . . . .
Funkcje inline . . . . . . . . . . . . . . . . .
Dynamiczne zarzdzanie pamici . . . . . .
Sowa kluczowe jzyka C++ . . . . . . . . .
Schemat programu w jzyku C++ . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
4
6
7
7
11
13
14
16
17
23
24
1.1. Wstp
Jzyk C oraz C++ s najbardziej popularnymi jzykami programowania.
W orodkach akademickich, a take w duych korporacjach komputerowych,
tworzone s co jaki czas nowe jzyki programowania wysokiego poziomu, ale
jak uczy dowiadczenie, wszystkie te pomysy s z pocztku entuzjastycznie przyjmowane przez programistw, nowe jzyki maj swoich zagorzaych
wielbicieli, a po jakim czasie powoli odchodz w niepami, lub s marginalizowane. Moda na dany jzyk przemija. Jedynie jzyk C trwa i ma si
bardzo dobrze, dziki take swoim modykacjom (jzyk C++ jest nadzbiorem jzyka C). Naley przypomnie, e jzyk C jest integralnie zwizany z
systemem Unix. Jzyk C++ zosta zaprojektowany jako obiektowa wersja
jzyka C. W jzyku C++ powstao i powstaje wiele znaczcego oprogramowania: systemy operacyjne, programy narzdziowe, programy uytkowe,
programy do obsugi sieci komputerowych. Na rynku pracy, programista
bez znajomoci jzyka C/C++ i systemu Unix, ma nike szanse. Obecnie od programisty wymaga si znajomoci przynajmniej dwch jzykw
programowania i znajomoci minimum dwch systemw operacyjnych, jzyk C/C++ i Unix s bezwzgldnie wymagane, znajomo innych jzykw
programowania oraz innych systemw (systemy operacyjne rodziny Windows) jest dodatkowym atutem. Obecnie (rok 2010) dominujcym jest jzyk
C++ integralnie zwizany z programowaniem obiektowym. Jzyk C++ jest
nadzbiorem jzyka C. Zwykle podrczniki dydaktyczne do nauki programowania w jzyku C++ zawieraj obszern cz powicon programowaniu
w jzyku C. Aby przyspieszy projektowanie i implementowanie programw,
zostay opracowane specjalne systemy wspomagajce prace programistw.
Doskonaymi narzdziami do tworzenia aplikacji s pakiety takie jak np.
C++ Builder rmy Borland czy Visual C++ rmy Microsoft. Te produkty
nale do systemw szybkiego projektowania aplikacji (ang. RAD - Rapid
Application Development). Korzystajc z tych pakietw moemy efektywnie
konstruowa 32-bitowe programy pracujce w systemie Windows.
Wydaje si rozsdne, aby dobrze wyksztacony informatyk opanowa
nastpujce narzdzia programistyczne:
jzyk C
jzyk C++
jzyk Java
11
13
15
i n t main ( )
{
int x ;
float y ;
p r i n t f ( " \ nPodaj l i c z b e c a l k o w i t a : " ) ;
s c a n f ( "%d" ,&x ) ;
p r i n t f ( " \ nPodaj l i c z b e r z e c z y w i s t a : " ) ;
s c a n f ( "%f " ,&y ) ;
p r i n t f ( " \n%d r a z y %f =%f " , x , y , xy ) ;
getch () ;
return 0 ;
}
11
i n t main ( )
{
int x ;
float y ;
c o u t << " Podaj l i c z b e c a l k o w i t a : " ;
c i n >> x ;
c o u t << " Podaj l i c z b e r z e c z y w i s t a : " ;
c i n >> y ;
c o u t << x << " r a z y " << y << " = " << xy ;
13
15
Naley zwrci uwag na istotne rnic pomidzy pokazanymi programami. Po pierwsze, w programie w konwencji jzyka C++ naley wczy
inny plik nagwkowy:
#include <i o s t r e a m . h>
Zamiast instrukcji:
p r i n t f ( " \ nPodaj l i c z b e c a l k o w i t a : " ) ;
mamy instrukcj:
c o u t << " Podaj l i c z b e c a l k o w i t a : " ;
acuch Podaj liczbe calkowita zosta przesany do strumienia przy pomocy operatora . W zwykym C jest to operator przesunicia bitw w
lew stron, ktry dziaa na wartociach cakowitych przesuwajc ich bity
o podan ilo miejsc. Podobnie dziaa operator przesunicia bitw w praw stron , ktry przesuwa bity w prawo. W C++ operatory i dalej
funkcjonuj jako operatory przesuwania bitw, ale mog mie take inne
znaczenie, zalene od kontekstu. W pokazanym przykadzie, znak moe
take by uyty do wysania danych wyjciowych do strumienia cout i z tego powodu nazywany jest operatorem umieszczajcym (ang. insertor), tzn.
umieszcza dane w strumieniu cout. Podobnie znak moe by wykorzystany
do odczytania danych wejciowych pochodzcych ze strumienia cin, dlatego
zwany jest operatorem pobierajcym (ang. extractor). Ta zdolno nadawania nowego znaczenia typowym operatorom nazywana jest przecieniem
operatorw ( ang. operator overloading) jest charakterystyczn cech jzyka
C++.
a;
10;
b;
100;
y = a b;
11
13
15
17
19
21
23
25
11
13
15
17
19
21
23
25
10
warto przechowywana w zmiennej temp jest przypisana zmiennej wskazywanej przez py. Dziki takim mechanizmom dokonywana jest prawdziwa
zamiana wartoci zmiennych x i y co wida po uruchomieniu programu:
Funkcja
Funkcja
Funkcja
Funkcja
10
12
14
16
18
20
22
24
gdzie zmienne zostan zidentykowane jako referencje. W dalszych instrukcjach funkcji zamiana() nastpuje rzeczywista zamiana wartoci zmiennych.
Poniewa parametry funkcji zamiana() zostay zadeklarowane jako referencje, wartoci w funkcji main() zostay przekazane przez referencj, dlatego
s zamienione rwnie w tej funkcji. Wynikiem dziaania programu jest
komunikat:
Funkcja
Funkcja
Funkcja
Funkcja
11
12
11
13
15
17
19
i n t main ( )
{
objetosc () ;
objetosc (10) ;
o b j e t o s c (10 , 10) ;
o b j e t o s c (10 , 10 , 10) ;
getch () ;
return 0 ;
}
void o b j e t o s c ( i n t x , i n t y , i n t z )
{
int vol ;
vol = x y z ;
c o u t << "x= " << x << " y= " << y << " z= " << z << e n d l ;
c o u t << " o b j e t o s c = " << v o l << e n d l ;
}
Uytkownik nie okreli argumentw, wszystkie trzy argumenty s domniemane, wobec tego w tym wywoaniu argumenty maj wartoci:
x = 1;
y = 1;
z = 1;
W kolejnym wywoaniu:
objetosc (10) ;
z = 1;
Moemy zdeniowa dwie wersje funkcji kwadrat() i w zalenoci od typu przesyanego argumentu (int lub double) C++ wybierze odpowiedni
wersj funkcji. Oczywicie wersji funkcji moe by wicej ni dwie. Przecianie funkcji jest proste; w naszym przypadku musimy tylko utworzy dwie
denicje funkcji. Program pokazany na wydruku 1.7. uywa przecionej
funkcji kwadrat() do obliczania kwadratu liczby typu int i typu double.
Funkcje przeciane s odrniane przez swoje sygnatury sygnatura jest
poczeniem nazwy funkcji i typw jej parametrw.
Listing 1.7. Przecianie funkcji
2
10
12
i n t main ( )
{
c o u t << " kwadrat l i c z b y c a l k o w i t e j 3 = "
<< kwadrat ( 3 ) ;
c o u t << " \ nkwadrat l i c z b y r z e c z y w i s t e j 3 . 3 = "
<< kwadrat ( 3 . 3 ) ;
13
14
14
15
10
12
14
16
18
20
22
24
26
28
5.5
16
i n l i n e i n t Kwadrat ( i n t ) ;
i n t main ( )
{
i n t kw ;
c o u t << " \ nPodaj l i c z b e : " ;
c i n >> kw ;
kw = Kwadrat (kw) ;
c o u t << "Kwadrat t e j l i c z b y t o " << kw ;
getch () ;
return 0 ;
}
10
12
14
16
18
20
i n t Kwadrat ( i n t kw )
{
return kw kw ;
}
Obsuga wywoania funkcji jest bardzo kosztowna w szczeglnoci potrzebny jest kod niezbdny do umieszczania wartoci na stosie, wywoania
funkcji, pobrania parametru ze stosu i zakoczenia dziaania funkcji. Jeeli
funkcja zostanie zadeklarowana ze sowem kluczowym inline, kompilator nie
tworzy prawdziwej funkcji tylko kopiuje kod z funkcji rozwijalnej bezporednio do kodu funkcji wywoujcej (w miejscu wywoania funkcji inline).Nie
jest wykonywany aden skok, program dziaa tak, jakby w tym miejscu byy instrukcje funkcji. Na wydruku 1.9 pokazano uycie funkcji typu inline.
Funkcja Kwadrat() jest deklarowana jako funkcja typu inline, otrzymujca
parametr typu int i zwracajca warto typu int. Program kompiluje si do
kodu, ktry ma posta tak, jakby w kadym miejscu wystpienia instrukcji:
kw = Kwadrat ( kw ) ;
Tego typu deklaracja powoduje zarezerwowanie dla tablicy liczby 100 jednostek pamici, z ktrych kada jest w stanie przechowywa warto typu
int. W jzyku C mona rezerwowa pami w trakcie dziaania programu.
Suy do tego celu funkcja malloc(), ktra pobiera tylko jeden argument
ilo potrzebnej pamici w bajtach. Znajduje ona odpowiedni obszar wolnej pamici i zwraca adres jego pierwszego bajtu. Rozpatrzmy nastpujce
instrukcje wykorzystujce malloc() do utworzenia tablicy:
double wsk ;
wsk = ( double ) m a l l o c ( 30 s i z e o f ( double ) ) ;
Powyszy kod rezerwuje pami dla trzydziestu wartoci typu double. Jak
pamitamy, nazwa tablicy jest adresem jej pierwszego elementu, std przypisanie wskanikowi wsk adresu pierwszej wartoci double sprawia, e mona
z niego korzysta tak jak ze zwykej nazwy tablicy wsk[]. Schemat postpowania jest nastpujcy:
17
18
10
12
14
16
18
20
22
24
26
28
W instrukcji:
wsk = ( double ) m a l l o c (max s i z e o f ( double ) ) ;
Funkcja free() zwalnia zarezerwowan pami. Funkcje malloc() i free() zarzdzaj razem pamici komputera. Dziki tablicom dynamicznym wykorzystujemy optymalnie pami. Jeeli wiemy, e program wikszo czasu
bdzie potrzebowa tylko 100 elementw a od czasu do czasu albo tylko
jeden raz bdzie potrzebowa 10000 elementw to naley stosowa tablice
dynamiczne. Bez moliwoci korzystania z tablic dynamicznych naleaoby utworzy tablic o rozmiarze 10000 elementw. Taka tablica cay czas
zajmowaaby niepotrzebnie pami. Jest to zwyke marnowanie pamici.
Poniewa dynamiczne przydzielanie pamici jest istotne dla tworzenia
wydajnych programw C++ oferuje dodatkowe, bardzo wygodne narzdzia
jakim s operatory new (przydzia) i delete (zwalnianie). Skadnia wyrae
z tymi operatorami ma posta:
new ty p_obiek tu
delete wsk aznik _obiek tu
19
20
Tak okrelony wskanik zachowuje si jak kady inny wskanik, wobec tego
moemy np. przypisa obiektowi na stercie dowoln warto:
wsk = 9 9 ;
11
13
15
17
19
i n t main ( )
{
i n t zmienna = 5 5 ;
i n t zm = &zmienna ;
i n t s t e r t a = new i n t ;
sterta = 155;
c o u t << " zmienna : " <<
c o u t << " zm : " << zm
c o u t << " s t e r t a : " <<
delete s t e r t a ;
s t e r t a = new i n t ;
sterta = 111;
c o u t << " s t e r t a : " <<
delete s t e r t a ;
getch () ;
return 0 ;
}
zmienna << \n ;
<< \n ;
s t e r t a << \n ;
s t e r t a << \n ;
55
155
111
W instrukcjach
i n t zmienna = 5 5 ;
i n t zm = &zmienna ;
deklarowany jest wskanik sterta inicjalizowany wartoci uzyskan w wyniku wywoania operatora new. Powoduje to zaalokowanie na stercie miejsca
dla wartoci typu int. W instrukcji:
21
22
Wspominalimy ju o zjawisku wycieku pamici. Taka sytuacja moe nastpi, gdy ponownie przypiszemy wskanikowi adres bez wczeniejszego
zwolnienia pamici, na ktr on wskazuje. Rozwamy fragment programu:
i n t wsk = new i n t ;
wsk = 1 1 1 ;
wsk = new i n t ; // e r r o r
wsk = 9 9 9 ;
Na pocztku tworzymy wskanik wsk i przypisujemy mu adres rezerwowanego na stercie obszaru pamici. W tym obszarze umieszczamy warto 111.
W trzeciej instrukcji przypisujemy wskanikowi wsk adres innego obszaru
pamici a czwarta instrukcja umieszcza w tym obszarze warto 999. Nie
ma sposobu na odzyskanie pierwszego obszaru pamici. Poprawna posta
tego kodu moe by nastpujca:
i n t wsk = new i n t ;
wsk = 1 1 1 ;
delete wsk ;
wsk = new i n t ;
wsk = 9 9 9 ;
23
bool
class
default
else
false
if
namespace
or
register
sizeof
template
typedef
using
while
break
compl
delete
enum
oat
inline
new
or eq
reinterpret cast
static
this
typeid
virtual
xor
case
const
do
explicit
for
int
not
private
return
static cast
throw
typename
void
xor eq
catch
const cas
double
export
friend
long
not eq
protected
short
struct
true
union
volatile
char
continue
dynamic cast
extern
goto
mutable
operator
public
signed
switch
try
unsigned
wchar t
wracamy uwag, e w standardzie ANSI/ISO C++ do jzyka C++ dodano nowy typ bool. Zmienna typu bool (zmienna logiczna) moe przyjmowa jedn z dwch wartoci : true (prawda) lub false (fasz). Pierwotnie
jzyk C++ tak jak i jzyk C nie posiada typu logicznego. Jak pamitamy,
warto zero interpretowana bya jako fasz, kada warto rna od zera
bya interpretowana jako prawda. Posta instrukcji z typem bool moe mie
posta:
i n t cena = true ;
i n t odpowiedz = f a l s e ;
24
nagwek
deklaracje klas bazowych
deklaracje klas pochodnych
prototypy funkcji oglnych
prototypy funkcji
ciao funkcji main()
denicje funkcji oglnych
biblioteka
Jzyk C
Jzyk C++, stara wersja
Jzyk C++, nowa wersja
nazewnictwo
<nazwa pliku.h>
<nazwa pliku.h>
<nazwa pliku>
przykad
<stdio.h>
<iostream.h>
<iostream>
Jest jeszcze jedna istotna rnica, gdy chcemy stosowa now bibliotek jzyka C++ (zamiast np. pliku iostream.h, wczamy plik nagwkowy
iostream). Stosowanie nowej biblioteki wymaga udostpnienia dyrektywy
przestrzeni nazw, aby udostpni elementy np. strumienia iostream w programach:
using namespace s t d :
25
Rozdzia 2
Wprowadzenie do programowania
obiektowego
2.1.
2.2.
2.3.
2.4.
2.5.
2.6.
2.7.
2.8.
Wstp . . . . . . . . . . . . . .
Paradygmaty programowania .
Obiekty i klasy . . . . . . . . .
Hermetyzacja danych . . . . .
Dziedziczenie . . . . . . . . . .
Polimorzm . . . . . . . . . .
Podsumowanie terminologii . .
rodowisko programistyczne .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
28
28
30
34
35
36
37
38
28
2.1. Wstp
Ponad trzydzieci lat temu (w latach osiemdziesitych) Bjarne Stroustrup tworzy jzyk C++ jako ulepszona wersje jzyka C. Zasadnicza nowoci jzyka C++ byo:
eliminacja istotnych wad jzyka C
Wprowadzenie obsugi obiektw i moliwo
realizacji programowania obiektowego
Programowanie obiektowe byo nowoci w latach osiemdziesitych, stosunkowo niedawno powstay propozycje teoretyczne nowego paradygmatu
programowania i powsta pierwszy jzyk realizujcy ten paradygmat - jzyk
Smalltalk. W programowaniu obiektowym analizuje si zadanie do rozwizania a nastpnie opracowuje si reprezentacje tego problemu w postaci klasy.
Konstrukcja klasy (wywodzca si pierwotnie ze struktury jzyka C) umoliwia realizacje paradygmatu programowania obiektowego i jest centralnym
elementem programw obiektowych pisanych w jzyku C++.
29
30
Deklaracja ta opisuje struktur zoon z dwch tablic i jednej zmiennej typu int. Nie tworzy ona rzeczywistego obiektu w pamici, a jedynie okrela,
z czego skada si taki obiekt. Opcjonalna etykieta (znacznik, ang. structure tag) pracownik jest nazw przyporzdkowan strukturze. Ta nazwa jest
wykorzystywana przy deklaracji zmiennej:
struct pracownik k i e r o w n i k ;
Deklaracja ta stwierdza, e kierownik jest zmienn strukturaln o budowie pracownik. Nazwy zdeniowane wewntrz nawiasw klamrowych denicji struktury s skadowymi struktury (elementami struktury, polami
struktury). Denicja struktury pracownik zawiera trzy skadowe, dwie typu
char nazwisko i imie oraz jedna typu int rok urodzenia. Dostp do skadowych struktur jest moliwy dziki operatorom dostpu do skadowych.
Mamy dwa takie operatory: operator kropki (.) oraz operator strzaki (->).
Za pomoc operatora kropki moliwy jest dostp do skadowych struktury przez nazw lub referencj do obiektu. Np. aby wydrukowa skadow
rok urodzenia moemy posuy si wyraeniem:
31
32
Skadowe tej samej struktury musz mie rne nazwy, jednak dwie rne
struktury mog zawiera skadowe o tej samej nazwie. Skadowe struktury
mog by dowolnego typu. Struktury danych umoliwiaj przechowywanie
cech (waciwoci, atrybutw) obiektw. Ale jak wiemy z obiektem zwizane
s rwnie najrniejsze dziaania. Dziaania te tworz interfejs obiektu. W
jzyku C nie ma moliwoci umieszczenia w strukturze atrybutw obiektu i
operacji, jakich mona na obiekcie wykona. W jzyku C++ wprowadzono
nowy abstrakcyjny typ danych, ktry umoliwia przechowywanie atrybutw
i operacji. Tworzenie abstrakcyjnych typw danych odbywa si w jzyku
C++ (tak samo jak w innych jzykach programowania obiektowego) za pomoc klas. Klasa stanowi implementacj abstrakcyjnego typu danych.
Na kady projektowany obiekt skadaj si dane (atrybuty) i dobrze
okrelone operacje (dziaania). Do danych nie mona dotrze bezporednio,
naley do tego celu wywoa odpowiedni metod. Dziki temu chronimy
dane przed niepowoanym dostpem. Komunikacja uytkownika z obiektem
(a take komunikacja wybranego obiektu z innym obiektem) zaczyna si
od wysania do niego odpowiedniego dania (ang. request). Po odebraniu dania (inaczej komunikatu) obiekt reaguje wywoaniem odpowiedniej
metody lub wysya komunikat, e nie moe dania obsuy.
Klasa opisuje obiekt. Z formalnego punktu widzenia klasa stanowi typ.
W programie moemy tworzy zmienne typu okrelonego przez klas. Zmienne te nazywamy instancjami (wcieleniami). Instancje stanowi realizacj
obiektw opisanych przez klas.
Jak ju wiemy, operacje wykonywane na obiektach nosz nazw metod.
Aby wykona konkretn operacj, obiekt musi otrzyma komunikat, ktry przetwarzany jest przez odpowiedni metod. Cech charakterystyczn
programw obiektowych jest przesyanie komunikatw pomidzy obiektami.
Poniewa jzyk C++ jest w istocie jzykiem hybrydowym, panuje pewne
zamieszanie w stosowanej terminologii. Metody nosz nazw funkcji (procedury i podprogramy w jzyku C++ nosz te nazwy funkcji), a instancje
nazywana s obiektami konkretnymi. W specykacji jzyka C++ pojcie
instancji nie jest w ogle uywane. Dla okrelania instancji klasy uywa si
po prostu terminu obiekt. Rwnie istnieje dua rnorodno, jeeli chodzi
o terminologi stosowan w opisie elementw klasy. Mamy takie terminy
jak: elementy, skadowe, atrybuty, dane. Operacje nazywane s metodami,
funkcjami skadowymi lub po prostu funkcjami.
W jzyku C++ klasa jest struktur, ktrej skadowe mog take by
funkcjami (metodami).
Deklaracja klasy precyzuje, jakie dane i funkcje publiczne s z ni zwi-
33
i n i t ( i n t xp , i n i t yp )
34
2.5. Dziedziczenie
ny jest sposb dostpu okrelony specykatorem private. Hermetyzacja ma
ogromne znaczenie dla przenonoci programw i optymalizowania nakadw potrzebnych na ich modykacje. Wpywa take dodatnio na osiganie
niezawodnoci w projektach programistycznych.
2.5. Dziedziczenie
Jedna z najistotniejszych cech programowania zorientowanego obiektowo jest dziedziczenie (ang. inheritance). Mechanizm dziedziczenia suy w
jzykach obiektowych do odwzorowania wystpujcych czsto w naturze powiza typu generalizacja - specjalizacja. Umoliwia programicie deniowanie potomkw istniejcych obiektw. Kady potomek dziedziczy przy tym
(wszystkie lub wybrane) pola i metody obiektu bazowego, lecz dodatkowo
uzyskuje pewne pola i wasnoci unikatowe, nadajce mu nowy charakter.
Typ takiego obiektu potomnego moe sta si z kolei typem bazowym do
zdeniowania kolejnego typu potomnego. Bjarne Stroustrup w podrczniku
Jzyk C++ rozwaajc zalenoci wystpujce pomidzy klas gura i
klas okrg, tak zdeniowa paradygmat programowania obiektowego:
zdecyduj, jakie chcesz mie klasy;
dla kadej klasy dostarcz peny zbir operacji;
korzystajc z mechanizmu dziedziczenia, jawnie wska to, co jest wsplne
Typowe aplikacje operuj na wielu obiektach, programici zmuszeni s
do projektowania wielu klas, ktre powstaj dziki duemu nakadowi czasu
i kosztw. Stosunkowo szybko zorientowano si, e nie zawsze trzeba projektowa klas od pocztku, mona wykorzysta istniejce ju inne, przetestowane i sprawdzone klasy. W praktyce okazao si take, e wiele klas
ma zazwyczaj kilka wsplnych cech. Naturalnym jest wic denie, aby
analizujc podobne klasy wyodrbni wszystkie wsplne cechy i stworzy
uoglniona klas, ktra bdzie zwiera tylko te atrybuty i metody, ktre
s wsplne dla rozwaanych klas. W jzykach obiektowych tak uoglnion
klas nazywamy superklas lub klas rodzicielsk, a kada z analizowanych
klas nazywa si podklas lub klas potomn.
W jzyku C++ wprowadzono koncepcje klasy bazowej (ang. base class)
i klasy pochodnej (ang. derived class). Klasa bazowa (klasa podstawowa)
zawiera tylko te elementy skadowe, ktre s wsplne dla wyprowadzonych
z niej klas pochodnych. Podczas tworzenia nowej klasy, zamiast pisania cakowicie nowych danych oraz metod, programista moe okreli, e nowa klas
odziedziczy je z pewnej, uprzednio zdeniowanej klasy podstawowej. Kada
taka klasa moe w przyszoci sta si klas podstawow. W przypadku
dziedziczenia jednokrotnego klasa tworzona jest na podstawie jednej klasy
podstawowej. W sytuacji, gdy nowa klasa tworzona jest w oparciu o wiele
35
36
2.6. Polimorzm
Drug istotn cech (obok dziedziczenia) programowania zorientowanego obiektowo jest polimorzm (ang. polimorphism). Sowo polimorzm
oznacza dosownie wiele form. Polimorzm, stanowicy uzupenienie dziedziczenia sprawia, e moliwe jest pisanie kodu, ktry w przyszoci bdzie
wykorzystywany w warunkach nie dajcych si jeszcze przewidzie. Mechanizm polimorzmu wykorzystuje si te do realizacji pewnych metod w trybie nakazowym, abstrahujcym od szczegowego typu obiektu. Zachowanie
polimorczne obiektu zaley od jego pozycji w hierarchii dziedziczenia. Jeli dwa lub wicej obiektw maj ten sam interfejs, ale zachowuj si w
odmienny sposb, s polimorczne. Jest to bardzo istotna cecha jzykw
obiektowych, gdy pozwala na zrnicowanie dziaania tej samej funkcji w
zalenoci od rodzaju obiektu. Zagadnienie polimorzmu jest trudne pojciowo, w klasycznych podrcznikach programowania jest omawiane zazwyczaj razem z funkcjami wirtualnymi (ang. virtual function). Przy zastosowaniu funkcji wirtualnych i polimorzmu moliwe jest zaprojektowanie
aplikacji, ktra moe by w przyszoci prosto rozbudowywana. Jako przykad rozwamy zbir klas modelujcych gury geometryczne takie jak koo,
trjkt, prostokt, kwadrat, itp. Wszystkie s klasami pochodnymi klasy
bazowej gura. Kada z klas pochodnych ma moliwo narysowania siebie,
dziki metodzie rysuj. Poniewa mamy rne gury, funkcja jest inna w
i powinna by umieszczona w klasie bazowej gura. Powyszy prototyp deklaruje funkcj rysuj jako sta, nie zawierajc argumentw, nie zwracajc
adnej wartoci i wirtualn. Jeeli funkcja rysuj zostaa zadeklarowana w
klasie bazowej jako wirtualna, to gdy nastpnie zastosujemy wskanik w
klasie bazowej lub referencj do obiektu w klasie pochodnej i wywoamy
funkcje rysuj stosujc ten wskanik to program powinien wybra dynamicznie waciw funkcje rysuj.
Polimorzm umoliwia tworzenie w typach potomnych tzw. metod wirtualnych, nazywajcych si identycznie jak w typach bazowych, lecz rnicych si od swych odpowiednikw pod wzgldem znaczeniowym.
37
38
Wywietlaniem napisw na ekranie zajmuje si klasa iostream. Klasa iostream obsuguje podstawowe operacje wejcia/wyjcia wykorzystujc mechanizm strumieni (ang. stream). Do wysyania danych do standardowego wyjcia (ekran) suy klasa cout, do pobierania danych wejciowych ze standardowego wejcia (klawiatura) suy klasa cin. Do obsugi strumieni potrzebne
s dwa operatory: wstawiania i pobierania . Naley zaznaczy, e uycie strumienia cout ma sens tylko w aplikacjach tekstowych. W aplikacjach
gracznych do wyprowadzania informacji potrzebne s inne mechanizmy.
Gdy odwoujemy si do strumienia cout, naley wczy plik nagwkowy
<iostream.h>. Wykorzystujc edytor tekstowy piszemy tekst naszego programu. Zazwyczaj usuwamy zbdne dyrektywy sugerowane przez RAD oraz
zbdne argumenty. Program po poprawkach przedstawiony jest na wydruku
2.1.
Listing 2.1. Pierwszy program testowy
1
i n t main ( )
{
c o u t << " Zaczynamy programowanie wC++ B u i l d e r " ;
getch () ;
return 0 ;
39
40
realizuje tzw. przytrzymanie ekranu. Bez tej instrukcji (albo innej np.
PAUSE) w wikszoci RAD nie jestemy w stanie zobaczy wyniku dziaania programu. Funkcja getch() odczytuje nacinicie pojedynczego klawisza.
Dopki nie naciniemy dowolnego klawisza, program jest wstrzymany, dziki
temu moemy oglda wynik dziaania programu na ekranie.
Tak napisany program powinien by zapisany na dysku w wybranym katalogu. Podczas zapisywania projektu RAD tworzy rne pliki. Plik projektu ( ang. project le) zawieraj informacje potrzebne do generowania kodu
wynikowego. Plik rdowy (ang. source le) o rozszerzeniu zazwyczaj .cpp
zawiera tekst rdowy programu. Widzimy, e tworzenie aplikacji w jzyku C++ tworzy si w zintegrowanym rodowisku programistycznym (IDE)
stosunkowo atwo. Programowanie w takim rodowisku ma wiele zalet:
rodowisko generuje szkielet programu
Zazwyczaj wbudowany edytor podwietla sowa kluczowe,
Zazwyczaj kompilacja i uruchomienie programu realizowane s w jednym
kroku
rodowisko ma wbudowany debuger, wykrywanie prostych bdw jest
efektywne
W Internecie mona znale wiele bezpatnych rodowisk programistycznych. Wybr rodowiska jest kwesti gustu. Jeeli wemiemy pod uwag
wielko pamici potrzebnej do zainstalowania takiego rodowiska to rekomendujemy doskonae rodowisko Dev-C++ przeznaczone dla systemu
operacyjnego Windows. Dev-C++ jest tzw. oprogramowaniem otwartym
(open-source). Jeeli chcemy pracowa z tym rodowiskiem, moemy je pobra nieodpatnie z witryny Bloodshed Software (www.bloodshed.net).
Rozdzia 3
Klasy i obiekty
3.1.
3.2.
3.3.
3.4.
3.5.
3.6.
3.7.
Wstp . . . . . . . . . . . . . . . . . . . .
Deklaracja i denicja klasy . . . . . . . .
Wystpienie klasy (deniowanie obiektu)
Dostp do elementw klasy . . . . . . . .
Metody klasy . . . . . . . . . . . . . . . .
Klasa z akcesorami . . . . . . . . . . . .
Funkcje skadowe const . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
43
43
47
50
53
42
3. Klasy i obiekty
3.1. Wstp
Zasadnicz cech jzyka C++ jest moliwo uywania klas (ang. class).
Niezbyt precyzyjnie mwic, klasa jest bardzo podobna do typu strukturalnego znanego z jzyka C. Zasadnicza rnica midzy klas a struktur
polega na fakcie, e struktura przechowuje jedynie dane, natomiast klasa
przechowuje zarwno dane jak i funkcje. Klasa jest typem deniowanym
przez uytkownika. Gdy deklarowane s zmienne tego typu, s one obiektami. Mwic krtko: w jzyku C++ klasy s formalnymi typami, obiekty
s specycznymi zmiennymi tego typu. Klasa jest waciwie zbiorem zmiennych, czsto rnego typu i skojarzonymi z tymi danymi metodami, czyli
funkcjami wykorzystywanymi wycznie dla cile okrelonych danych. Operowanie obiektami w programie jest istot programowania obiektowego.
Zostaa utworzona klasa o nazwie Kot, zawiera ona dane: wiek i waga a
ponadto jedn funkcje Glos(). Taka deklaracja nie alokuje pamici, informuje jedynie kompilator, czym jest typ Kot, jakie zawiera dane i funkcje.
43
44
3. Klasy i obiekty
Filemon . waga = 1 0 ;
11
45
13
15
17
19
21
23
25
oraz
c = &b ;
46
3. Klasy i obiekty
Listing 3.2. Dostp do publicznych skadowych klasy
10
12
14
16
18
20
10
12
14
16
18
20
22
24
26
28
30
i n t main ( )
{
Kot Filemon ;
Filemon . wiek = 3 ;
Filemon . waga = 5 ;
Filemon . k o l o r = " rudy " ;
c o u t << " Filemon t o " << Filemon . k o l o r << " k ot " << e n d l ;
c o u t << "Wazy " << Filemon . waga << " k ilogramow " ;
c o u t << " i ma " << Filemon . wiek << " l a t a " << e n d l ;
c o u t << " gdy j e s t glodny t o " ;
Filemon . Glos ( ) ;
getch () ;
return 0 ;
}
47
48
3. Klasy i obiekty
W denicji klasy Kot pojawia si funkcja skadowa (metoda) o nazwie
Glos():
c l a s s Kot
{
public :
i n t wiek ;
i n t waga ;
char k o l o r ;
void Glos ( ) ;
};
Elementami klasy s dane i metody, nale one do zasigu klasy. Zasig klasy
oznacza, e wszystkie skadowe klasy s dostpne dla funkcji skadowych
poprzez swoj nazw. Poza zasigiem klasy, do skadowych odnosimy si
najczciej za porednictwem tzw. uchwytw referencji i wskanikw. Jak
ju mwilimy, w jzyku C++ mamy dwa operatory dostpu do skadowych
klasy:
Operator kropki (.) stosowany z nazw obiektu lub referencj do niego
Operator strzaki (->) stosowany w poczeniu ze wskanikiem do obiektu
Operator strzaki ( ang. arrow member selection operator) jest nazywany w polskiej literaturze rnie, najczciej jako skrtowy operator zasigu
lub krtko operator wskazywania. Aby przykadowo wydrukowa na ekranie skadow sekunda klasy obCzas, z wykorzystaniem wskanika czasWs,
stosujemy instrukcj:
czasWs = &obCzas ;
c o u t << czasWs > sekunda ;
Wyraenie:
czasWs > sekunda
jest odpowiednikiem
( czasWs ) . sekunda
To wyraenie dereferuje wskanik, a nastpnie udostpnia skadow sekunda za pomoc operatora kropki. Konieczne jest uycie nawiasw okrgych,
poniewa operator kropki ma wyszy priorytet ni operator dereferencji.
Poniewa taki zapis jest do skomplikowany, w jzyku C++ wprowadzono
skrtowy operator dostpu poredniego operator strzaki. Uywanie operatora strzaki jest preferowane przez wikszo programistw. W prostym
programie zademonstrujemy korzystanie z klasy Liczba dla zilustrowania
sposobw dostpu do skadowych klasy za pomoc omwionych operatorw
wybierania skadowych.
49
50
3. Klasy i obiekty
Listing 3.4. Dostp do skadowych: operator kropki i strzaki
1
11
13
15
17
19
21
23
25
27
29
31
class Liczba
{
public :
float x ;
void pokaz ( ) { c o u t << x << e n d l ; }
};
i n t main ( )
{
L i c z b a nr ;
L i c z b a nrWsk ;
L i c z b a &nrR ef = nr ;
// o b i e k t nr t y p u L i c z b a
// w s k a z n i k
// r e f e r e n c j a
nr . x = 1 . 0 1 ;
c o u t << " nazwa o b i e k t u , sk ladowa xma w a r t o s c : " ;
nr . pokaz ( ) ;
nrR ef . x = 2 . 0 2 ;
c o u t << " r e f e r e n c j a o b i e k t u , sk ladowa xma w a r t o s c : " ;
nrR ef . pokaz ( ) ;
nrWsk = &nr ;
nrWsk > x = 3 . 0 3 ;
c o u t << " wsk aznik o b i e k t u , sk ladowa xma w a r t o s c : " ;
nrWsk > pokaz ( ) ;
getch () ;
return 0 ;
}
51
11
13
15
17
19
21
23
25
27
29
int Prost
int Prost
void P r o s t
void P r o s t
: : get_a ( ) { return
: : get_b ( ) { return
: : set_a ( i n t aw )
: : set_b ( i n t bw)
a ;}
b ;}
{ a = aw ; }
{b = bw ; }
i n t main ( )
{ P r o s t p1 ;
int p ole ;
p1 . set_a ( 5 ) ;
p1 . set_b ( 1 0 ) ;
p o l e = p1 . get_a ( ) p1 . get_b ( ) ;
//
p o l e = p1 . a p1 . b ;
// ni epoprawna i n s t r u k c j a
c o u t << " P o w i e r z c h n i a = " << p o l e << e n d l ;
getch () ;
return 0 ;
}
W tym programie po wprowadzeniu dugo i szeroko prostokta obliczamy jego pole powierzchni. Klasa o nazwie Prost ma nastpujc deklaracj:
class Prost
{ private :
int a ;
int b ;
public :
i n t get_a ( ) ;
i n t get_b ( ) ;
void set_a ( i n t aw ) ;
void set_b ( i n t bw) ;
};
52
3. Klasy i obiekty
Dane skadowe a i b s danymi prywatnymi. Wszystkie funkcje skadowe s
akcesorami publicznymi. Dwa akcesory ustawiaj zmienne skadowe:
void set_a ( i n t aw ) ;
void set_b ( i n t bw) ;
a dwa inne:
i n t get_a ( ) ;
i n t get_b ( ) ;
: : get_a ( ) { return
: : get_b ( ) { return
: : set_a ( i n t aw )
: : set_b ( i n t bw)
a ;}
b ;}
{ a = aw ; }
{b = bw ; }
53
zadeklarowano stay obiekt nr klasy Liczba z jednoczesn inicjacj. Podobnie deklarowane mog by funkcje skadowe. W prototypie i w denicji funkcji skadowej naley uy sowa kluczowego const. Na przykad nastpujca
denicja funkcji skadowej klasy Liczba
i n t L i c z b a : : pokazWartosc ( ) const { return x ; } ;
Wraz z modykatorem const czsto deklarowane s akcesory. Deklarowanie funkcji jako const jest bardzo dobrym zwyczajem programistycznym,
efektywnie pomaga wykrywa bdy przy przypadkowej prbie modykowania staych obiektw. Funkcje skadowe zadeklarowane jako const nie
mog modykowa danych obiektu, poniewa nie dopuci do tego kompilator. Obiekt stay nie moe by modykowany za pomoc przypisa, ale moe
by zainicjalizowany. W takim przypadku mona wykorzysta odpowiednio
zbudowany konstruktor. Do konstruktora musi by dostarczony odpowiedni
inicjator, ktry zostanie wykorzystany jako warto pocztkowa staej danej
klasy. W kolejnym przykadzie klasa Punkt posiada trzy dane prywatne, w
tym jedn typu const. Do celw dydaktycznych w programie umieszczono
dwa konstruktory (w danym momencie moe by czynny tylko jeden z nich).
Jeeli program uruchomimy z konstruktorem w postaci:
Punkt : : Punkt ( i n t xx , i n t yy , i n t k )
{ x = 1 ; y = 1 ; sk ok = k ; }
//ERROR ! ! !
54
3. Klasy i obiekty
zostanie wygenerowany nastpujcy komunikat:
[ C++ Warning ] f t e s t 1 . cpp [ 2 3 ] : W8038 Constant member
Punkt : : sk ok i s not i n i t i a l i z e d
[ C++ E r r o r ] f t e s t 1 . cpp [ 2 3 ] : E2024 Cannot modify
a const o b j e c t
10
12
14
16
18
20
22
24
Punkt : : Punkt ( i n t xx , i n t yy , i n t k ) : sk ok ( k )
{ x = 1; y = 1;}
// k o n s t r u k t o r
/
Punkt : : Punkt ( i n t xx , i n t yy , i n t k )
{ x = 1; y = 1; skok = k ; }
//ERROR ! ! !
/
void Punkt : : pokaz ( ) const
// d e f i n i c j a metody
{ c o u t << " sk ok = " << sk ok << " x= "
<< x << " y= " << y << e n d l ;
}
26
28
30
32
34
36
38
i n t main ( )
{
Punkt p1 ( 1 , 1 , 2 ) ;
// d e k l a r a c j a o b i e k t u
// p1 t y p u Punkt
c o u t << " dane poczatk owe " << e n d l ;
p1 . pokaz ( ) ;
c o u t << " zmiana w s p o l r z e d n e j y " << e n d l ;
f o r ( i n t j = 0 ; j <5; j ++)
{
p1 . przesunY ( ) ;
p1 . pokaz ( ) ;
}
55
40
// k o n s t r u k t o r
W denicji konstruktora wida notacj zapoyczon przez C++ z jzyka Simula. Inicjowanie jakiej zmiennej x wartoci np. 44 najczciej ma
posta:
int x = 44;
powoduje zainicjowanie skadowej skok wartoci k. Moemy mie list zmiennych skadowych z wartociami inicjalnymi :
Punkt : : Punkt ( i n t a , i n t b ) : xx ( a ) , yy ( b )
Rozdzia 4
Konstruktory i destruktory
4.1.
4.2.
4.3.
4.4.
4.5.
4.6.
4.7.
4.8.
4.9.
4.10.
4.11.
4.12.
4.13.
Wstp . . . . . . . . . . . . . . . . . . . . . . . . . . .
Inicjalizacja obiektu klasy . . . . . . . . . . . . . . . .
Konstruktory i destruktory domylne . . . . . . . . .
Konstruktor jawny . . . . . . . . . . . . . . . . . . . .
Wywoywanie konstruktorw i destruktorw . . . . .
Rozdzielenie interfejsu od implementacji . . . . . . .
Wskanik do obiektu this . . . . . . . . . . . . . . . .
Wskanik this kaskadowe wywoania funkcji . . . .
Tablice obiektw . . . . . . . . . . . . . . . . . . . . .
Inicjalizacja tablic obiektw nie bdcych agregatami
Tablice obiektw tworzone dynamicznie . . . . . . . .
Kopiowanie obiektw . . . . . . . . . . . . . . . . . .
Klasa z obiektami innych klas . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
58
58
59
61
63
65
67
68
71
73
75
77
78
58
4. Konstruktory i destruktory
4.1. Wstp
Klasa jest typem deniowanym przez uytkownika. Gdy deklarowane s
zmienne tego typu, s one obiektami. Mamy nastpujce denicje.
Klasa jest zdeniowanym przez uytkownika typem danych, zawiera pola
danych oraz funkcje.
Obiekt jest egzemplarzem utworzonego (na podstawie klasy) typu.
Klasa ma charakter unikatowy i istnieje tylko jedna posta klasy
Moe istnie wiele obiektw okrelonego typu (utworzonego na podstawie
konkretnej klasy)
Sama denicja klasy nie deniuje adnych obiektw. W denicji klasy,
dane nie mog by inicjalizowane (nie mona przypisa im wartoci). Dane mona inicjalizowa dla konkretnego obiektu (egzemplarza klasy). Po
utworzeniu klasy mona powoa do ycia jej egzemplarz, czyli obiekt. Dobr praktyk programistyczn jest tworzenie obiektw z zainicjalizowanymi
danymi. Inicjalizacja obiektw nie jest zadaniem trywialnym.
59
11
13
15
17
19
21
23
25
27
29
c l a s s Punkt
{ public :
void ustaw ( int , i n t ) ;
void p r z e s u n ( int , i n t ) ;
void pokaz ( ) ;
private :
int x , y ;
};
// d e k l a r a c j a k l a s y p u n k t
// s k l a d o w e p u b l i c z n e
// s k l a d o w e prywatne
60
4. Konstruktory i destruktory
p1 . pokaz ( ) ;
getch () ;
return 0 ;
31
33
10
20
// d e k l a r a c j a k l a s y p u n k t
// s k l a d o w e p u b l i c z n e
// s k l a d o w e prywatne
Deklaracja klasy skada si z prywatnych danych ( wsprzdne kartezjaskie punktu x i y) oraz z publicznych funkcje skadowych: ustaw(), przesun()
i pokaz(). Denicja klasy skada si z denicji wszystkich funkcji skadowych
(metod) i ma posta:
void Punkt : : ustaw ( i n t a , i n t b )
{ x = a;
y = b;
}
// d e f i n i c j a metody
Wewntrz denicji wszystkie dane i funkcje skadowe s bezporednio dostpne (bez wzgldu na to czy s publiczne czy prywatne). Dane skadowe
nalece do klasy pamici typu static s dostpne dla wszystkich obiektw
danej klasy. Przez domniemanie , dane statyczne s inicjalizowane wartoci
zerow. Jak ju mwilimy, jeeli nie stworzymy konstruktora, kompilator
stworzy konstruktor domylny bezparametrowy. Potrzeba tworzenia kon-
Jeeli konstruktor jest domylny (nie ma adnych parametrw) mona opuci nawiasy i napisa po prostu:
Punkt p1 ;
Jest to wyjtek od reguy, ktra mwi, e funkcje musz mie nawiasy nawet
wtedy, gdy nie maj parametrw. Dlatego zapis:
Punkt p1 ;
jest interpretowany przez kompilator jako wywoanie konstruktora domylnego w tym przypadku nie wysyamy parametrw i nie piszemy nawiasw.
Gdy deklarowany jest konstruktor, powinien by deklarowany destruktor,
nawet gdy nic nie robi.
61
62
4. Konstruktory i destruktory
5
11
13
15
17
19
21
23
25
27
Punkt ( int , i n t ) ;
~Punkt ( ) ;
void p r z e s u n ( int , i n t ) ;
void pokaz ( ) ;
private :
int x , y ;
// k o n s t r u k t o r
// d e s t r u k t o r
};
// i m p l e m e n t a c j a k o n s t r u k t o r a
Punkt : : Punkt ( i n t a , i n t b )
// k l a s y Punkt
{ x = a;
y = b;
}
Punkt : : ~Punkt ( )
{ }
// i m p l e m e n t a c j a d e s t r u k t o r a
void Punkt : : p r z e s u n ( i n t da , i n t db )
{ x = x + da ;
y = y + db ;
}
void Punkt : : pokaz ( )
{ c o u t << " w s p o l r z e d n e : " << x << " " << y << e n d l ;
}
i n t main ( )
{
Punkt p1 ( 5 , 1 0 ) ;
p1 . pokaz ( ) ;
p1 . p r z e s u n ( 1 5 , 1 5 ) ;
p1 . pokaz ( ) ;
getch () ;
return 0 ;
}
// u s t a w i a p u n k t
// p o k a z u j e w s p o l r z e d n e
// p r z e s u w a p u n k t
// p o k a z u j e nowe w s p o l r z e d n e
// k o n s t r u k t o r
// d e s t r u k t o r
// k o n s t r u k t o r k l a s y Punkt
// i m p l e m e n t a c j a d e s t r u k t o r a
// u s t a w i a p u n k t
c l a s s Kon_Des
{ public :
Kon_Des( i n t ) ;
~Kon_Des ( ) ;
private :
i n t nr ;
};
// k o n s t r u k t o r
// d e s t r u k t o r
11
13
63
64
4. Konstruktory i destruktory
c o u t << " k o n s t r u k t o r o b i e k t u " << nr ;
15
17
19
21
23
25
27
29
31
33
35
37
39
41
43
45
47
49
51
}
Kon_Des : : ~Kon_Des ( )
{ c o u t << " d e s t r u k t o r o b i e k t u " << nr << e n d l ;
}
void fun_ob ( void ) ;
// p r o t o t y p f u n k c j i t w o r z a c e j
// nowe o b i e k t y
Kon_Des ob1 ( 1 ) ;
// o b i e k t g l o b a l n y
void fun_ob ( void )
{Kon_Des ob5 ( 5 ) ;
// o b i e k t l o k a l n y
c o u t << " ( automatyczny o b i e k t l o k a l n y w fun_ob ( ) ) "
<< e n d l ;
s t a t i c Kon_Des ob6 ( 6 ) ;
// o b i e k t l o k a l n y
c o u t << " ( s t a t y c z n y o b i e k t l o k a l n y w fun_ob ( ) ) "
<< e n d l ;
Kon_Des ob7 ( 7 ) ;
// o b i e k t l o k a l n y
c o u t << " ( automatyczny o b i e k t l o k a l n y w fun_ob ( ) ) "
<< e n d l ;
}
i n t main ( )
{ c o u t << " ( g l o b a n y utworzony p r z e d f u n k c j a main ( ) ) "
<< e n d l ;
Kon_Des ob2 ( 2 ) ;
// o b i e k t l o k a l n y
c o u t << " ( automatyczny o b i e k t l o k a l n y w main ( ) ) "
<< e n d l ;
s t a t i c Kon_Des ob3 ( 3 ) ;
// o b i e k t l o k a l n y
c o u t << " ( s t a t y c z n y o b i e k t l o k a l n y w main ( ) ) "
<< e n d l ;
fun_ob ( ) ;
// w y w o l a n i e f u n k c j i t w o r z a c e j o b i e k t y
Kon_Des ob4 ( 4 ) ;
// o b i e k t l o k a l n y
c o u t << " ( automatyczny o b i e k t l o k a l n y w main ( ) "
<< e n d l ;
getch () ;
return 0 ;
}
obiektu
obiektu
obiektu
obiektu
obiektu
4
2
6
3
1
11
13
// p l i k f t e s t 1 . cpp , program w y k o r z y s t u j e k l a s e p u n k t
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include " punkt1 . h"
i n t main ( )
{
punkt p1 ( 0 . 5 , 1 . 5 ) ;
p1 . w y s w i e t l ( ) ;
p1 . p r z e s u n ( 0 . 5 , 0 . 5 ) ;
p1 . w y s w i e t l ( ) ;
getch () ;
return 0 ;
}
65
66
4. Konstruktory i destruktory
2
10
12
#i f n d e f _PUNKT1_H
#define _PUNKT1_H
c l a s s punkt
{ private :
float x , y ;
public :
punkt ( f l o a t , f l o a t ) ;
void p r z e s u n ( f l o a t , f l o a t ) ;
void w y s w i e t l ( ) ;
};
#endif
// d e f i n i c j e k l a s y p u n k t
#include " punkt1 . h"
#include <i o s t r e a m . h>
punkt : : punkt ( f l o a t xx , f l o a t yy )
{ x = xx ; y = yy ;
}
10
void punkt : : p r z e s u n ( f l o a t dx , f l o a t dy )
{ x = x + dx ;
y = y + dy ;
}
12
14
void punkt : : w y s w i e t l ( )
{ c o u t << " x i y= " << x << " " << y << e n d l ;
}
_PUNKT1_H
_PUNKT1_H
................
................
................
67
gdzie X klas jest nazw klasy. Ten wskanik jest inicjalizowany wskanikiem
do obiektu, dla ktrego wywoano funkcj skadow. Wskanik this zadeklarowany jest jako *const, co oznacza, e nie mona go zmienia. Wskanik
this jest niejawnie uywany podczas odwoywania si zarwno do danych jak
i funkcji skadowych obiektu. Wskanik this jest przekazywany do obiektu
jako niejawny argument kadej niestatycznej funkcji skadowej wywoywanej
na rzecz obiektu. Uywanie wskanika this w odwoaniach nie jest konieczne, ale czsto stosuje si go do pisania metod, ktre dziaaj bezporednio
na wskanikach. Wskanik this moe by wykorzystywany take w sposb
jawny, co jest pokazane w kolejnym przykadzie.
Listing 4.7. Wskaznik this
1
class Liczba
{ public :
Liczba ( f l oa t = 0) ;
void pokaz ( ) ;
private :
f l o a t nr ;
};
// k o n s t r u k t o r domyslny
11
L i c z b a : : L i c z b a ( f l o a t xx ) { nr = xx ; } // k o n s t r u k t o r
13
15
17
19
21
23
void L i c z b a : : pokaz ( )
{
c o u t << " nr = " << nr << e n d l ;
c o u t << " t h i s >= " << t h i s > nr << e n d l ;
c o u t << " ( t h i s ) . nr = " << ( t h i s ) . nr << e n d l ;
}
i n t main ( )
{ Liczba ob iek t ( 1 . 0 1 ) ;
o b i e k t . pokaz ( ) ;
getch () ;
68
4. Konstruktory i destruktory
return 0 ;
25
Funkcja skadowa pokaz() trzy razy wywietla warto danej skadowej nr.
W pierwszej instrukcji mamy klasyczn metod bezporedni dostp do
warto nr. Funkcja skadowa ma dostp nawet do prywatnych danych swojej klasy. W drugiej instrukcji:
c o u t << " t h i s >= " << t h i s > nr << e n d l ;
wykorzystano operator strzaki do wskanika. Jest to popularna jawna metoda stosowania wskanika this. W nastpnej instrukcji zastosowano operator
kropki dla wskanika po dereferencji:
c o u t << " ( t h i s ) . nr = " << ( t h i s ) . nr << e n d l ;
zwraca *this typu Punkt3D &. Biorc po uwag, e operator kropki wie
od strony lewej do prawej, wiemy, e w wyraeniu:
p1 . ustawX ( 1 0 ) . ustawY ( 2 0 ) . ustawZ ( 3 0 ) ;
// k l a s a Punkt3D
#i f n d e f _PUNKT3D_H
#define _PUNKT3D_H
11
13
c l a s s Punkt3D
{
public :
Punkt3D ( i n t = 0 , i n t = 0 , i n t = 0 ) ; // k o n s t r u k t o r
Punkt3D &ustawPunkt ( int , int , i n t ) ; // u s t a w i a
// c a l y p u n k t
// u s t a w i a X
Punkt3D &ustawX ( i n t ) ;
Punkt3D &ustawY ( i n t ) ;
// u s t a w i a Y
// u s t a w i a Z
Punkt3D &ustawZ ( i n t ) ;
15
17
19
i n t p o b i e r z X ( ) const ; // p o b i e r a X
i n t p o b i e r z Y ( ) const ; // p o b i e r a Y
i n t p o b i e r z Z ( ) const ; // p o b i e r a Z
void pokaz ( ) const ;
// p o k a z u j e w s p o l r z e d n e
// punktu X, Y, Z
21
23
25
27
private :
int x ;
int y ;
int z ;
};
#endif
69
70
4. Konstruktory i destruktory
Listing 4.9. Kaskadowe wywoanie metod - denicja klasy - plik
punkt3D.cpp
1
// d e f i n i c j e k l a s y Punkt3D
#include "punkt3D . h"
#include <i o s t r e a m . h>
11
13
15
17
19
21
Punkt3D : : Punkt3D ( i n t xx , i n t yy , i n t z z )
{ ustawPunkt ( xx , yy , z z ) ; }
Punkt3D &Punkt3D : : ustawPunkt ( i n t x1 , i n t y1 , i n t z1 )
{
ustawX ( x1 ) ;
ustawY ( y1 ) ;
ustawZ ( z1 ) ;
return t h i s ;
}
Punkt3D
{ x =
Punkt3D
{ y =
Punkt3D
{ z =
&Punkt3D : : ustawX ( i n t x1 )
x1 ; return t h i s ; }
&Punkt3D : : ustawY ( i n t y1 )
y1 ; return t h i s ; }
&Punkt3D : : ustawZ ( i n t z1 )
z1 ; return t h i s ; }
23
25
i n t Punkt3D : : p o b i e r z X ( ) const
i n t Punkt3D : : p o b i e r z Y ( ) const
i n t Punkt3D : : p o b i e r z Z ( ) const
{ return x ; }
{ return y ; }
{ return z ; }
27
29
31
10
12
// Kaskadowe w y w o l a n i e metod
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include "punkt3D . h"
i n t main ( )
{ Punkt3D p1 , p2 ;
p1 . ustawX ( 1 0 ) . ustawY ( 2 0 ) . ustawZ ( 3 0 ) ;
c o u t << " punkt s t a r t o w y p1" << e n d l ;
p1 . pokaz ( ) ;
c o u t << " nowy punkt p1 " << e n d l ;
p1 . ustawPunkt ( 5 0 , 5 0 , 5 0 ) . pokaz ( ) ;
71
14
16
// 50 elementowa t a b l i c a t y p u i n t
// 100 elementowa t a b l i c a t y p u f l o a t
// 10 elementowa t a b l i c a wskazni kow do c h a r
W podobny sposb tworzona jest tablica obiektw jakiej klasy. Niech klasa
punkt ma bardzo prost posta;
c l a s s punkt
{ public :
int x ; int y ;
};
Powysz denicj moemy czyta w nastpujcy sposb: ptab jest 10 elementow tablic obiektw klasy punkt. W tej prostej klasie wszystkie dane
s publiczne, wic dostp do nich jest nastpujcy:
72
4. Konstruktory i destruktory
c o u t << ptab [ 1 ] . x ;
c o u t << ptab [ 5 ] . y ;
Dostp do elementu tablicy realizowany jest przy pomocy dwch operatorw. Waciwy element tablicy jest wskazywany za pomoc operatora
indeksu [], po czym stosujemy operator kropki (.) wydzielajcy okrelon zmienn skadow obiektu. Moemy take zdeniowa wskanik, ktry
bdzie wskazywa na obiekty klasy punkt:
punkt
pWsk ;
W instrukcji:
pWsk = & ptab [ 5 ] ;
> x ;
Zasadniczym problemem jest inicjalizacja, czyli nadawanie wartoci pocztkowych w momencie denicji obiektu. Mog wystpi trzy przypadki:
tablica jest agregatem
tablica nie jest agregatem
tablica jest tworzona dynamicznie ( operator new)
Agregatem nazywamy tablic obiektw bdcych egzemplarzami klasy, ktra nie ma danych prywatnych i nie ma konstruktorw. W naszym
przykadzie mamy do czynienia z agregatem. W takim przypadku tablica
obiektw jest inicjalizowana do prosto: list inicjalizatorw umieszczamy
w nawiasach klamrowych i oddzielamy przecinkami:
punkt ptab [ 5 ] =
{ 0, 0,
1, 1,
2, 2,
3, 3,
1 0 , 20
};
// i n i c j a l i z a c j a
// x i y d l a p t a b
// x i y d l a p t a b
// x i y d l a p t a b
// x i y d l a p t a b
// x i y d l a p t a b
tablicy
[0]
[1]
[2]
[3]
[4]
11
13
15
17
19
21
23
i n t main ( )
{ punkt pWsk ;
// w s k a z n i k na o b i e k t t y p u p u n k t
punkt ptab [ 5 ] =
// i n i c j a l i z a c j a t a b l i c y
{ 0, 0,
// x i y d l a p t a b [ 0 ]
1, 1,
// x i y d l a p t a b [ 1 ]
2, 2,
// x i y d l a p t a b [ 2 ]
3, 3,
// x i y d l a p t a b [ 3 ]
1 0 , 20
// x i y d l a p t a b [ 4 ]
};
c o u t << "x y " << e n d l ;
f o r ( i n t i =0; i <5; i ++)
c o u t << ptab [ i ] . x << " " << ptab [ i ] . y << e n d l ;
pWsk = &ptab [ 4 ] ;
// w s k a z n i k i n i c j a l i z o w a n y adresem
c o u t << " d o s t e p p r z e z wsk aznik x= " << pWsk > x ;
getch () ;
return 0 ;
}
73
74
4. Konstruktory i destruktory
Listing 4.12. Tablica obiektw - tablica nie jest agregatem
2
10
12
14
16
18
20
22
24
26
28
30
// i l o s c punktow , r o z m i a r t a b l i c y
// t a b l i c a o b i e k t o w
// k o n s t r u k t o r
// k o n s t r u k t o r domyslny
Widzimy, e pomidzy nawiasami klamrowymi umieszczona jest lista inicjalizatorw, oddzielonych przecinkami. Poszczeglne wywoania konstruktorw inicjalizuj tablic obiektw. Konstruktor domniemany nie jest konieczny, ale jest dobrym zwyczajem umieszcza go. W sytuacji, gdy na przykad
tablica ma 10 elementw a na licie inicjalizatorw umieszczono jedynie 5
konstruktorw, wtedy kompilator niejawnie wywouje dla pozostaych elementw tablicy obiektw konstruktor domylny. Gdy w takiej sytuacji nie
bdzie konstruktora domylnego, kompilator zasygnalizuje bd.
W powyszym fragmencie mamy deklaracj tablicy twsk zawierajc 5 elementw typu punkt. Caa tablica jest tworzona na stercie, za pomoc wywoania new punkt[nr]. Usunicie tablicy twsk automatycznie zwrci ca
przydzielon pami gdy zostanie uyty operator delete:
delete [ ] twsk ;
75
76
4. Konstruktory i destruktory
#include <c o n i o . h>
3
11
13
c l a s s punkt
{
private :
int x , y ;
public :
punkt ( )
void s e t
{
i n t getX
i n t getY
};
{};
// k o n s t r u k t o r domyslny
( i n t xx , i n t yy )
// f u n k c j a s k l a d o w a
x = xx ; y = yy ; }
( ) { return x ; }
// a k c e s o r
( ) { return y ; }
15
17
19
21
23
25
27
29
31
33
i n t main ( )
{
const i n t nr = 5 ;
punkt twsk ;
twsk = new punkt [ nr ] ;
i f ( ! twsk )
{ c e r r << " nieudana a l o k a c j a \n" ;
return 1 ;
}
f o r ( i n t i = 0 ; i < nr ; i ++)
{
twsk [ i ] . s e t ( i +1 , i +3) ;
c o u t << twsk [ i ] . getX ( )<< " " << twsk [ i ] . getY ( ) <<e n d l ;
}
delete [ ] twsk ;
getch () ;
return 0 ;
}
Obiekt d1 jest inicjalizowany jawnym konstruktorem, za obiekt d2 inicjalizowany jest konstruktorem domylnym.
Listing 4.14. Kopiowanie obiektw
1
11
13
15
17
19
21
23
25
27
77
78
4. Konstruktory i destruktory
d2 . pokaz ( ) ;
c o u t << " tworzy o b i e k t d3 = " ;
Data d3 = d1 ;
d3 . pokaz ( ) ;
getch () ;
return 0 ;
29
31
33
35
W instrukcji:
d2 = d1 ;
Konstruktor ma sze parametrw, znak dwukropka rozdziela list parametrw od listy inicjatorw skadowych. Inicjatory skadowych przekazuj
argumenty konstruktora Osoba do konstruktorw obiektw skadowych. Na
kolejnych wydrukach pokazano deklaracje klas Osoba i Data, ich denicje i
w pitym pliku pokazano program testujcy. W klasie Osoba:
10
12
14
16
// pracow . h
// d e k l a r a c j a k l a s y Pracownik
#i f n d e f PRACOW1_H
#define PRACOW1_H
#include " data1 . h"
c l a s s Osoba
{ public :
Osoba ( char , char , char , int , int , i n t ) ;
void pokaz ( ) const ;
private :
char i m i e [ 3 0 ] ;
char nazwisk o [ 3 0 ] ;
char m i a s t o [ 3 0 ] ;
const Data d a t a U r o d z e n i a ;
};
#endif
// pracow . cpp
// d e f i n i c j e f u n k c j i s k l a d o w y c h k l a s y Pracownik
#include <i o s t r e a m . h>
#include <s t r i n g . h>
#include " pracow . h"
#include " data1 . h"
79
80
4. Konstruktory i destruktory
8
10
12
14
16
18
20
22
24
26
11
// d a t a 1 . h ,
d e k l a r a c j a k l a s y Data
#i f n d e f DATA1_H
#define DATA1_H
c l a s s Data
{ public :
Data ( i n t = 1 , i n t = 1 , i n t = 1 9 0 0 ) ;
void pokaz ( ) const ;
private :
int dzien ;
int miesiac ;
i n t rok ;
};
#endif
// d a t a 1 . cpp , d e f i n i c j e f u n k c j i s k l a d o w y c h k l a s y Data
#include <i o s t r e a m . h>
#include " data1 . h"
Data : : Data ( i n t d , i n t m, i n t r )
{ dzien = d ;
m i e s i a c = m;
rok = r ;
}
11
// k l a s a z o b i e k t e m i n n e j k l a s y
#include <v c l . h>
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include " pracow . h"
10
12
i n t main ( )
{ Osoba o s ( "Ewa" , " F a s o l a " , " L u b l i n " , 1 3 , 3 , 1 9 6 6 ) ;
c o u t << \n ;
o s . pokaz ( ) ;
getch () ;
return 0 ;
}
81
Rozdzia 5
Dziedziczenie i hierarchia klas
5.1.
5.2.
5.3.
5.4.
5.5.
5.6.
5.7.
Wstp . . . . . . . . . . . . . . . . . . . . . .
Hierarchiczna struktura dziedziczenia . . . .
Notacja UML (Unied Modeling Language) .
Proste klasy pochodne . . . . . . . . . . . .
Konstruktory w klasach pochodnych . . . . .
Dziedziczenie kaskadowe . . . . . . . . . . .
Dziedziczenie wielokrotne bezporednie . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 84
. 84
. 88
. 92
. 98
. 104
. 117
84
5.1. Wstp
Najistotniejszymi cechami programowania zorientowanego obiektowo s
dziedziczenie (ang. inheritance), polimorzm (ang. polimorphism) i dynamiczne zwizywanie (ang. dynamic binding). Dziedziczenie jest form ponownego uywania kodu uprzednio opracowanej klasy w nowotworzonej klasie pochodnej. Deniowana na nowo klasa przejmuje pewne cechy od innych
zdeniowanych ju klas, dziedziczc po nich wybrane metody i pola. W procesie tworzenia nowej klasy, programista moe wykorzysta istniejc ju
klas i okreli, e nowa klasa odziedziczy pewne dane i funkcje skadowe.
Istniejc star klas nazywamy klas podstawow lub bazow (ang. base
class, parent class, or superclass), a nowa klasa dziedziczca z klasy podstawowej dane i funkcje nazywana jest klas pochodn (ang. derived class,
child class, or subclass). Mwimy, e klasa pochodna dziedziczy wszystkie
cechy swojej klasy bazowej. Dziki mechanizmowi dziedziczenie w klasie
pochodnej moemy:
doda nowe zmienne i funkcje skadowe
przedeniowa funkcje skadowe klasy bazowej
Wynika z tego, e klasa pochodna jest bardziej szczegowa od bazowej i
jest wiksza, w tym sensie, e moe mie wicej danych i funkcji skadowych
ni klasa bazowa.
W jzyku C++ moliwe s trzy rodzaje dziedziczenia: publiczne, chronione oraz prywatne. Nas najbardziej bdzie interesowa dziedziczenie publiczne. Klasy pochodne nie maj dostpu do tych skadowych klasy bazowej, ktre zostay zadeklarowane jako prywatne (private). W przypadku
dziedziczenia publicznego dane i metody klasy bazowej zadeklarowane jako
public i private staj si odpowiednio skadowymi publicznymi oraz prywatnymi klasy pochodnej. Naley zwrci take uwag na fakt, e funkcje
zaprzyjanione z klas nie s dziedziczone, take konstruktory i destruktory
nie s dziedziczone. Obiekty klas pochodnych mog by traktowane jako
obiekty klasy podstawowej.
85
86
Moemy opracowa bardziej zoony program, np. program rysujcy walec. Elementem gury geometrycznej takiej jak walec jest koo. Rysujc koo
na ekranie, musimy zna pooenie rodka koa. Wygodnie jest wiec opracowa klas Punkt. Taka klasa bdzie obsugiwaa wsprzdne kartezjaskie
punktu. Tworzc klas obsugujc koo (nazwijmy t klas Kolo)moemy
wykorzysta klas Punkt. Klasa Punkt bdzie klas bazow klasy pochodnej Kolo. W tym przypadku mamy do czynienia z dziedziczeniem prostym
(klasa Kolo dziedziczy bezporednio cechy klasy Punkt). Majc opracowana
Opracowujc program obsugi walca, mamy take moliwo innego zaprojektowania klasy Walec. Klasa Walec moe jednoczenie dziedziczy cechy klasy Punkt i cechy klasy Kolo. W takim przypadku mamy do czynienia
z dziedziczeniem mnogim. Tego typu hierarchia dziedziczenia pokazana jest
na rysunku 5.5
Moliwe s bardziej skomplikowane struktury dziedziczenia mnogiego.
Przykad takiej rozbudowanej hierarchii pokazany jest na rysunku 5.6.
W praktyce hierarchiczne struktury dziedziczenia mog by bardzo skomplikowane. Rozwamy program obsugujcy osoby zwizane z uczelni. Najbardziej oglny podzia to pracownicy uczelni i studenci. Pracownicy uczelni
mog dzieli si na pracownikw administracji i nauczycieli akademickich.
Tworzc model osb zwizanych z uczelni (opracowujc odpowiednie klasy) moemy zaprojektowa struktur dziedziczenia pokazan na rysunku
5.7. Naley zwrci uwag na klas P Funkcyjny. Ta klasa ma za zadanie obsuy pracownikw uczelni, ktrzy sprawuj funkcje takie jak np.
rektor czy dziekan. S oni bardzo czsto jednoczenie pracownikami dydaktycznymi i pracownikami administracji. W tym przypadku projektujc
87
88
89
90
c l a s s Punkt
{
private :
int x ;
int y ;
public :
void i n i t ( int , i n t ) ;
void p r z e s u n ( int , i n t ) ;
void pokaz ( ) ;
};
91
92
93
Specykator dostpu oznacza rodzaj dziedziczenia: public, private lub protected. Jeeli mamy do czynienia z dziedziczeniem publicznym to piszemy:
c l a s s Kwadrat : public Punkt
{
// . . . . . . . . . . . . . . . . . . . . . . .
};
Podamy przykad zastosowania klasy pochodnej. Niech klasa bazowa o nazwie punkt ma dwie skadowe prywatne x i y, ktre s wsprzdnymi punktu na paszczynie oraz funkcje skadowe do obsugi tego punktu. Naley
utworzy now klas pochodn punktB od klasy bazowej punkt. Klasa pochodna punktB powinna zawiera now funkcj, dziki ktrej bdzie mona obliczy odlego punktu od pocztku ukadu wsprzdnych. Jeeli
wsprzdne punktu wynosz x i y to odlego punktu od pocztku ukadu
wsprzdnych wyraa si wzorem:
r=
x2 + y 2
(5.1)
10
12
14
c l a s s punkt
{ double x , y ;
public :
void i n i t ( double xx =0.0 , double yy =0.0)
{ x = xx ;
y = yy ;
}
void pokaz ( )
{ c o u t << " w s p o l r z e d n e : " << x << " " << y << e n d l ;
}
double wsX ( ) { return x ; }
double wsY ( ) { return y ; }
};
94
11
// d z i e d z i c z e n i e p u b l i c z n e
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include <math . h>
#include " punkt . h"
c l a s s punktB : public punkt
{
public :
double promien ( )
{ return s q r t ( wsX( ) wsX( ) + wsY( ) wsY( ) ) ;
};
13
15
17
19
21
i n t main ( )
{
punktB a ;
a. init ( 3, 4 ) ;
a . pokaz ( ) ;
c o u t << " promien : " << a . promien ( ) ;
getch () ;
return 0 ;
}
Zawiera ona funkcj skadow promien (), ktra oblicza pierwiastek kwadratowy z sumy kwadratw wsprzdnych punku. Jest to odlego punktu
od pocztku ukadu wsprzdnych.
W liniach:
punktB a ;
Rysunek 5.10. Diagram klasy punkt i punktB w notacji UML. Klasa punktB dziedziczy skadowe klasy punkt.
a. init ( 3, 4 ) ;
a . pokaz ( ) ;
95
96
// d e k l a r a c j a k l a s y p u n k t
c l a s s punkt
{ protected :
double x , y ;
public :
void i n i t ( double xx =0.0 , double yy =0.0)
{ x = xx ;
y = yy ;
}
void pokaz ( )
{ c o u t << " w s p o l r z e d n e : " << x << " " << y << e n d l ;
}
11
13
15
};
10
12
14
16
18
20
22
24
#include
#include
#include
#include
<i o s t r e a m . h>
<c o n i o . h>
<math . h>
" punkt . h"
W tym przykadzie widzimy, e funkcja skadowa promien() klasy pochodnej punktB ma prost posta:
double promien ( )
{ return s q r t ( x x + y y ) ;
}
97
98
c l a s s punkt
// k l a s a bazowa
{ public :
punkt ( double xx , double yy ) {x = xx ; y = yy ; }
void pokazXY ( ) ;
private :
double x , y ;
};
11
13
15
17
19
21
23
25
};
27
i n t main ( )
{punktB p1 ( 5 , 5 0 , 5 0 ) ;
p1 . pokaz ( ) ;
getch () ;
return 0 ;
}
29
31
y = 50
99
100
Konstruktor punktB musi wywoa konstruktor punkt, ktry jest odpowiedzialny za zainicjowanie tej czci obiektu klasy punktB, ktra pochodzi z
klasy podstawowej punkt. W tym celu zosta wykorzystany tzw. inicjator
skadowej. Zastosowano rozszerzon posta deklaracji konstruktora klasy
pochodnej, ktra pozwala przekazywa argumenty do jednego lub kilku konstruktorw klasy bazowej.
Formalna posta rozszerzonej deklaracji jest nastpujca:
k onstruk tor_k lasa_pochodna ( l i s t a _ p a r a m e t r o w )
: nazwa_klasy_pochodnej_1 ( l i s t a argumentow ) ,
: nazwa_klasy_pochodnej_2 ( l i s t a argumentow ) ,
.......................................
: nazwa_klasy_pochodnej_N ( l i s t a argumentow ) ,
{
// c i a l o k o n s t r u k t o r a k l a s y p o c h o d n e j
}
Jak wiec widzimy, nasz konstruktor klasy pochodnej jest funkcj o trzech
argumentach, dwa argumenty musz by przesane do konstruktora klasy
bazowej. W momencie utworzenia obiektu p1:
punktB
p1 ( 5 , 5 0 , 50 ) ;
a nastpnie wykonanie konstruktora klasy pochodnej. Bardzo czsto zachodzi sytuacja, gdy tworzone s obiekty klasy pochodnej a klasa bazowa i
klasa pochodna zawieraj rne skadowe. Jeeli tworzony jest obiekt klasy
pochodnej to kolejno wywoywania konstruktorw jest nastpujca:
Wywoywane s konstruktory obiektw klasy bazowej
Wywoywany jest konstruktor klasy bazowej
// p l i k " p u n k t . h"
// d e k l a r a c j a k l a s y p u n k t
11
13
#i f n d e f _PUNKT_H
#define _PUNKT_H
c l a s s punkt
{
public :
punkt ( double = 0 . 0 , double = 0 . 0 ) ; // k o n s t r u k t o r domyslny
~punkt ( ) ;
// d e s t r u k t o r
protected :
double x , y ;
};
15
#endif
// p l i k p u n k t . cpp
// d e f i n i c j a k l a s y p u n k t
101
102
10
12
14
16
punkt : : ~ punkt ( )
{
c o u t << " d e s t r u k t o r o b i e k t u k l a s y punkt " ;
c o u t << " x= " << x << " y= " << y << e n d l ;
}
// p l i k " k o l o . h"
// d e k l a r a c j a k l a s y k o l o
#i f n d e f _KOLO_H
#define _KOLO_H
11
13
15
17
19
c l a s s k o l o : public punkt
{
public :
k o l o ( double r =0.0 , double xx =0.0 , double yy =0.0) ;
// k o n s t r u k t o r domyslny
~kolo ( ) ;
// d e s t r u k t o r
private :
double pr ;
};
#endif
11
13
15
17
19
kolo : : ~ kolo ()
{
c o u t << " d e s t r u k t o r o b i e k t u k l a s y k o l o " ;
c o u t << " pr= " << pr << " x= " << x <<
" y= " << y << e n d l ;
}
10
12
14
103
104
16
18
Na pocztku tworzony jest egzemplarz obiektu klasy punkt. Wywoywany jest konstruktor a potem destruktor. Nastpnie tworzony jest obiekt k1
klasy pochodnej kolo. Wywoywany jest najpierw konstruktor klasy punkt,
ktry pokazuje przekazane wartoci a nastpnie wywoany jest konstruktor
klasy kolo, ktry te pokazuje przekazane wartoci. Kolejno wywoywany
jest destruktor klasy koo i destruktor klasy punkt (wywoywanie destruktorw odbywa si w odwrotnej kolejnoci). Nastpnie utworzony zostaje
kolejny obiekt k2 klasy kolo.
105
106
10
12
// p l i k p u n k t . h
// d e f i n i c j a k l a s y p u n k t
#i f n d e f PUNKT_H
#define PUNKT_H
c l a s s punkt
{ public :
punkt ( double = 0 . 0 , double = 0 . 0 ) ; // k o n s t r u k t o r domyslny
void pokaz ( ) ;
// p o k a z u j e w s p o l r z e d n e
protected :
double wX, wY;
};
#endif
10
// p l i k p u n k t . cpp
// f u n k c j e s k l a d o w e k l a s y p u n k t
#include " punkt . h"
#include <i o s t r e a m . h>
punkt : : punkt ( double xx , double yy ) // k o n s t r u k t o r k l a s y p u n k t
{ wX = xx ; wY = yy ;
}
void punkt : : pokaz ( )
{ c o u t << "x= " << wX << " y= " << wY << e n d l ;
}
10
12
// p l i k f t e s t 1 . cpp
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include " punkt . h"
i n t main ( )
{
double wX,wY;
c o u t << " Podaj X= " ;
c i n >> wX;
c o u t << " Podaj Y= " ;
c i n >> wY;
punkt p1 (wX, wY) ;
14
16
Rozbudujemy nasz projekt o moliwo obsuenia obiektu typu koo. Najpierw tworzymy klas punkt a potem na podstawie tej klasy tworzymy klas
kolo. Realizujemy model dziedziczenia jednokrotnego (prostego).
107
108
Rysunek 5.13. Diagram klasy punkt i kolo w notacji UML. Klasa kolo dziedziczy
skadowe klasy punkt.
// p l i k p u n k t . h
// d e f i n i c j a k l a s y p u n k t
#i f n d e f PUNKT_H
#define PUNKT_H
11
13
c l a s s punkt
{ public :
punkt ( double = 0 . 0 , double = 0 . 0 ) ;
// k o n s t r u k t o r domyslny
void pokazP ( ) ;
// p o k a z u j e w s p o l r z e d n e
protected :
double wX, wY;
};
#endif
// p l i k p u n k t . cpp
// f u n k c j e s k l a d o w e k l a s y p u n k t
#include " punkt . h"
#include <i o s t r e a m . h>
10
12
14
// k o n s t r u k t o r k l a s y p u n k t
punkt : : punkt ( double xx , double yy )
{ wX = xx ; wY = yy ;
}
void punkt : : pokazP ( )
{ c o u t << "x= " << wX << " y= " << wY << e n d l ;
}
// p l i k k o l o . h
// d e f i n i c j a k l a s y k o l o
#i f n d e f KOLO_H
#define KOLO_H
10
12
14
16
18
20
c l a s s k o l o : public punkt
{
public :
k o l o ( double r = 0 . 0 , double wX = 0 . 0 , double wY = 0 . 0 ) ;
// k o n s t r u k t o r
void pokazK ( ) ;
// p o k a z u j e w s p o l r z e d n e
double poleK ( ) const ;
protected :
double pr ;
};
#endif
// p l i k k o l o . cpp
// d e f i n i c j e f u n k c j i s k a l d o w y c h k l a s y k o l o
#include <i o s t r e a m . h>
#include " k o l o . h"
109
110
12
14
16
18
void k o l o : : pokazK ( )
{ c o u t << "x= " << wX << " y= " << wY << " r= "
<< pr << e n d l ;
}
double k o l o : : poleK ( ) const
{ return 3 . 1 4 1 5 9 2 6 pr pr ;
}
10
12
14
16
18
// program t e s t u j a c y k l a s e k o l o
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include " punkt . h"
#include " k o l o . h"
i n t main ( )
{ double wX, wY, pr ;
c o u t << " Podaj X= " ;
c i n >> wX;
c o u t << " Podaj Y= " ;
c i n >> wY;
c o u t << " Podaj promien , pr= " ;
c i n >> pr ;
k o l o k1 ( pr , wX, wY) ;
k1 . pokazK ( ) ;
c o u t <<" p o w i e r z c h n i a k o l a = " << k1 . poleK ( ) << e n d l ;
getch () ;
return 0 ;
}
Rysunek 5.14. Fizyczne diagramy komponentw (wydruki 5.14, 5.15, 5.16 i 5.17) i
pliku ftest2.cpp (wydruk 5.18). Program z dziedziczeniem prostym.
Rysunek 5.15. Diagram klasy punkt, kolo i walec w notacji UML. Jest to przykad
dziedziczenia kaskadowego. Klasa walec dziedziczy skadowe klasy kolo, klasa kolo
dziedziczy skadowe klasy punkt.
111
112
11
// p l i k p u n k t . h , d e f i n i c j a k l a s y p u n k t
#i f n d e f PUNKT_H
#define PUNKT_H
c l a s s punkt
{
public :
punkt ( double = 0 . 0 , double = 0 . 0 ) ; // k o n s t r u k t o r domyslny
void pokazP ( ) ;
// p o k a z u j e w s p o l r z e d n e
protected :
double wX, wY;
};
13
#endif
10
12
{ wX = xx ;
}
wY = yy ;
#i f n d e f KOLO_H
#define KOLO_H
#include " punkt . h"
10
12
14
16
c l a s s k o l o : public punkt
{
public :
k o l o ( double r = 0 . 0 , double wX = 0 . 0 , double wY = 0 . 0 ) ;
// k o n s t r u k t o r
void pokazK ( ) ;
// p o k a z u j e w s p o l r z e d n e
double poleK ( ) const ;
protected :
double pr ;
};
#endif
// p l i k k o l o . cpp , d e f i n i c j e f u n k c j i s k l a d o w y c h k l a s y k o l o
#include <i o s t r e a m . h>
#include " k o l o . h"
11
13
15
17
void k o l o : : pokazK ( )
{ c o u t << "x= " << wX << " y= " << wY << " r= " << pr
<< e n d l ;
}
double k o l o : : poleK ( ) const
{ return 3 . 1 4 1 5 9 2 6 pr pr ;
}
// p l i k w a l e c . h , d e f i n i c j a k l a s y w a l e c
#i f n d e f WALEC_H
#define WALEC_H
11
13
c l a s s w a l e c : public k o l o
{
public :
w a l e c ( double h = 0 . 0 , double r = 0 . 0 ,
double wX = 0 . 0 , double wY = 0 . 0 ) ;
// k o n s t r u k t o r domyslny
void pokazW ( ) ;
113
114
15
17
19
21
};
#endif
// p l i k w a l e c . cpp , d e f i n i c j e f u n k c j i s k l a d o w y c h k l a s y w a l ec
#include <i o s t r e a m . h>
#include " w a l e c . h"
11
13
15
17
19
21
23
25
void w a l e c : : pokazW ( )
{
c o u t << "x= " << wX << " y= " << wY << e n d l ;
c o u t << " promien= " << pr << " wy sok osc= " << wy sok osc
<< e n d l ;
}
double w a l e c : : poleW ( ) const
{
return 2 k o l o : : poleK ( ) +
2 3 . 1 4 1 5 9 2 6 pr wy sok osc ;
}
double w a l e c : : objetoscW ( ) const
{
return k o l o : : poleK ( ) wy sok osc ;
}
// Program t e s t u j a c y k l a s e walec
#include <i o s t r e a m . h>
#include <c o n i o . h>
i n t main ( )
{
double wX, wY, pr , wy sok osc ;
c o u t << " Podaj X= " ;
c i n >> wX;
c o u t << " Podaj Y= " ;
c i n >> wY;
c o u t << " Podaj promien , pr= " ;
c i n >> pr ;
c o u t << " Podaj wysokosc , wy sok osc= " ;
c i n >> wy sok osc ;
w a l e c w1 ( wysokosc , pr , wX, wY) ;
w1 . pokazW ( ) ;
c o u t << " p o w i e r z c h n i a walca= " << w1 . poleW ( ) << e n d l ;
c o u t << " o b j e t o s c walca= " << w1 . objetoscW ( ) << e n d l ;
12
14
16
18
20
22
24
getch () ;
return 0 ;
26
a nastpnie posugujc si funkcjami pokazW(), poleW() i objetoscW() pokazuje dane i oblicza pole i objto utworzonego walca. Kompletny wydruk
otrzymany po uruchomieniu programu testujcego ma posta:
115
116
117
public bazowa_2
W tym przykadzie klasa pochodna nosi nazw pochodna 1, nazwy publicznych klas bazowych to bazowa 1 i bazowa 2. Widzimy, e w przypadku
dziedziczenia mnogiego, wystpuje lista klas bazowych, kolejne klasy bazowe
poprzedzone s swoimi specykatorami dostpu i oddzielone s przecinkami.
Krtki przykad zilustruje zasad dziedziczenia mnogiego. Utworzymy
dwie klasy bazowe o nazwach bazowa 1 oraz bazowa 2, a nastpnie klas
pochodna o nazwie pochodna 1. Klasa pochodna 1 dziedziczy za pomoc
dziedziczenia wielokrotnego z klas bazowa 1 i bazowa 2. Diagram klas pokazany jest na rysunku 5.17.
118
// d z i e d z i c z e n i e mnogie
#include <i o s t r e a m . h>
#include <c o n i o . h>
10
12
14
16
18
20
c l a s s bazowa_1
{
protected :
double x ;
public :
bazowa_1 ( double xx )
{ x = xx ; }
double pokazX ( ) { return x ; }
};
c l a s s bazowa_2
{ protected :
double y ;
public :
bazowa_2 ( double yy )
{ y = yy ; }
double pokazY ( ) { return y ; }
};
// k o n s t r u k t o r
// zwraca x
// k o n s t r u k t o r 2
// zwraca y
22
24
26
28
30
32
34
36
38
40
42
44
119
46
48
z =111
// k o n s t r u k t o r
// k o n s t r u k t o r 2
// k o n s t r u k t o r 3
a konkretnie konstruktor
public :
pochodna_1 ( 1 , 1 1 , 111 ) ;
// k o n s t r u k t o r 3
W rezultacie zostaje utworzony i zainicjalizowany obiekt ob1 klasy pochodna 1 z pod-obiektami klas bazowych. Stosowanie dziedziczenia wielokrotnego w praktyce jest do skomplikowane. Wielu autorw zaleca umiar w
tworzeniu klas z dziedziczeniem wielokrotnym, wielu te zaleca unikanie
takich konstrukcji.
Rozdzia 6
Funkcje zaprzyjanione
6.1.
6.2.
6.3.
6.4.
Wstp . . . . . . . . . . . . . . . . . . . . . . .
Funkcja niezalena zaprzyjaniona z klas . . .
Funkcja skadowa zaprzyjaniona z inn klas
Klasy zaprzyjanione . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
122
123
134
141
122
6. Funkcje zaprzyjanione
6.1. Wstp
W jzyku C++ to klasa jest chroniona przed nieuprawnionym dostpem
a nie obiekt. W takiej sytuacji funkcja skadowa konkretnej klasy moe uywa wszystkich skadowych prywatnych dowolnego obiektu tej samej klasy.
W jzykach czysto obiektowych mamy do czynienia z mechanizmem odbioru komunikatu przez obiekt. Wywoujc funkcj obsugujc dany obiekt,
wysyamy odpowiedni komunikat, obiekt peni rol odbiorcy komunikatu.
Treci komunikatu s wartoci zmiennych, adresy, itp. przekazywane jako argumenty aktualne wywoywanej funkcji. Jeeli mamy klas o nazwie
punkt, oraz funkcj skadow klasy punkt, ktrej prototyp ma posta int
ustaw( int, int ), to deklaracja obiektu a moe mie posta:
punkt a ;
Dostp do publicznej skadowej funkcji klasy uzyskujemy przy uyciu operatora dostpu, np.
a . ustaw ( 5 , 5 ) ;
Rysunek 6.1. Do danych prywatnych klasy dostp maj jedynie funkcje skadowe
(metody) klasy
Z drugiej strony wiemy, e do skadowych prywatnych utworzonego obiektu nie maj dostpu funkcje niezalene ani funkcje innej klasy. W wielu jednak przypadkach istnieje potrzeba dostpu do chronionych danych obiektu.
Jzyk C++ znany ze swojej elastycznoci ma specjalny mechanizm pozwala-
W tym wywoaniu, obiekt a jest traktowany tak samo jak pozostae argumenty funkcji. Tym specjalnym mechanizmem w jzyku C++ jest mechanizm deklaracji zaprzyjanienia. Deklaracja zaprzyjanienia (ang. friend)
pozwala zadeklarowa w danej klasie funkcje, ktre bd miay dostp do
skadowych prywatnych, ale nie s funkcjami skadowymi klasy. Wyrniamy nastpujce sytuacje:
Funkcja niezalena zaprzyjaniona z klas X
Funkcja skadowa klasy Y zaprzyjaniona z klas X
Wszystkie funkcje klasy Y s zaprzyjanione z klas X (klasa zaprzyjaniona)
Funkcje zaprzyjanione maj dostp do wszystkich skadowych klasy
zadeklarowanych jako private lub protected. Z formalnego punktu widzenia,
funkcje zaprzyjanione ami zasad hermetyzacji (ukrywania) danych, ale
czasami ich uycie moe by korzystne.
123
124
6. Funkcje zaprzyjanione
punkt ( i n t xx = 0 , i n t yy = 0 )
{x = xx ;
y = yy ; }
friend void pokaz ( punkt ) ;
};
11
13
15
17
19
21
Dziki specykacji friend funkcja pokaz() staje si funkcj zaprzyjanion, ma dostp do prywatnych danych klasy punkt. Funkcja pokaz() wywietla aktualne wartoci danych prywatnych x i y. Funkcja pokaz() jest
samodzieln funkcj w stylu jzyka C nie jest ona funkcj skadow klasy
punkt:
void pokaz ( punkt p1 )
{ c o u t << " P o z y c j a X= " << p1 . x << e n d l ;
c o u t << " P o z y c j a Y= " << p1 . y << e n d l ;
}
W naszym przykadzie funkcja pokaz() pobiera jako argument a1. Pamitamy, e gdyby funkcja pokaz() bya funkcj skadow klasy punkt, to wywoanie jej miaoby posta:
a1 . pokaz ( ) ;
Rysunek 6.2. Funkcja zaprzyjaniona z klas deniowana jest na zewntrz jej zasigu, a mimo tego ma dostp do jej skadowych prywatnych
125
126
6. Funkcje zaprzyjanione
Na pokazanym diagramie widzimy dwie metody dostpu do prywatnych
danych klasy przy pomocy funkcji skadowych klasy lub przy pomocy
funkcji zewntrznych, ale zaprzyjanionych z klas. W kolejnym przykadzie opracujemy klas punkt, ktra ma jedn funkcj skadow inicjujc
dane klasy (ustalimy wsprzdne punktu na paszczynie) i jedn funkcj
zaprzyjanion (obliczy ona odlego punktu od pocztku ukadu wsprzdnych). W omwionym przykadzie (program z wydruku 6.2), tworzymy
obiekt p1 a nastpnie przy pomocy funkcji skadowej ustaw() przypisujemy
wartoci prywatnym zmiennym skadowym:
p1 . ustaw ( 1 . 0 , 1 . 0 ) ;
Funkcja zaprzyjaniona promien() nie moe odwoa si do zmiennych skadowych bezporednio, wykorzystuje do tego celu obiekt n klasy punkt. W
wywoaniu:
c o u t << " O d l e g l o s c = " << promien ( p1 ) << e n d l ;
11
13
15
17
19
21
23
11
13
15
17
19
127
128
6. Funkcje zaprzyjanione
argumentu i ta kopia przekazywana jest do wywoywanej funkcji. Moemy
dokonywa dowolnych zmian na kopii, oryginalna warto jest bezpieczna
nie ulega zmianom. Przekazywanie argumentw przez warto jest bezpieczne, ale bardzo wolne. Przekazywanie parametrw funkcji przez referencje
jest korzystne ze wzgldu na wydajno jest po prostu procesem bardzo
szybkim. Za kadym razem, gdy przekazywany jest obiekt do funkcji przez
warto, tworzona jest kopia tego obiektu. Za kadym razem, gdy zwracany
jest obiekt z funkcji tworzona jest kolejna kopia. Obiekty kopiowane s na
stos. Wymaga to sporej iloci czasu i pamici. W przypadku zdeniowanych przez programist duych obiektw, ten koszt staje si bardzo duy.
Rozmiar obiektu umieszczonego na stosie jest sum rozmiarw wszystkich
jego zmiennych skadowych. Stosowanie referencji jest korzystne, poniewa
eliminuje koszty zwizane z kopiowaniem duych iloci danych.
Parametr referencji jest aliasem (synonimem) do odpowiadajcego mu
argumentu. Referencja jest specjalnym, niejawnym wskanikiem, ktry dziaa jak alternatywna nazwa dla zmiennej. Zmienn o podanej po nazwie typu
z przyrostkiem &, np.
T& nazwa
11
13
15
17
}
19
W programie parametr funkcji Kwadrat() jest przekazywany przez referencj, co wida wyranie w prototypie:
void Kwadrat ( i n t & ) ;
Kwadrat ( a ) ;
nie jest wstanie zorientowa si, czy parametry przekazywane s przez warto czy przez referencj (tak pewno ma tylko wtedy, gdy zanalizuje prototyp funkcji). Moe to prowadzi do nieoczekiwanych skutkw ubocznych,
co demonstruje nasz program. Po pierwszym wywoaniu funkcji Kwadrat()
zmienna x zmieniaa warto. Aby unikn takich skutkw wielu programistw przesya niemodykowalne argumenty stosujc referencj do staych.
Poniewa zagadnienia wydajnoci s istotne, w kolejnym programie demonstrujcym wykorzystanie funkcji zaprzyjanionych, zastosujemy przekazywanie argumentw przez referencj.
Mamy nastpujc klas:
c l a s s Demo_1
{
friend void ustawA ( Demo_1 &, i n t ) ;
// f u n k c j a z a p r z y j a z n i o n a
public :
Demo_1( ) { a = 0 ; }
void pokaz ( ) const { c o u t << a << e n d l ; }
private :
int a ;
};
W tej klasie jest zadeklarowana dana skadowa a, ktra jest prywatna, czyli
129
130
6. Funkcje zaprzyjanione
jest niedostpna dla funkcji nie bdcych skadowymi klasy. Aby funkcja zewntrzna ustawA() miaa dostp do tej danej, zadeklarowano j jako friend:
friend void ustawA ( Demo_1 &, i n t ) ;
// f u n k c j a z a p r z y j a z n i o n a
Funkcja ustawA() ma moliwo modykacji danej a poniewa jest zaprzyjaniona z klas Demo 1. Cay program ma posta przedstawiona na wydruku 6.1.
Listing 6.5. Dostp do skadowych prywatnyc; funkcje zaprzyjanione
1
11
13
15
17
19
21
23
Aby mie dostp do prywatnych danych klasy punkt, co jest nam potrzebne,
aby wywietli wsprzdne punktu x i y, musimy dysponowa zewntrzn
funkcj pokaz(), zaprzyjanion z klas punkt. Argumenty do tej funkcji
musz by przekazane jawnie. Wobec tego prototyp tej funkcji moe mie
posta:
void pokaz ( punkt ) ;
( punkt & ) ;
gdy chcemy przekaza argumenty przez referencj. Moemy usprawni funkcj pokaz() wiedzc, e ta funkcja nie zmienia wsprzdnych i zastosowa
modykator const:
void pokaz ( const punkt & ) ;
131
132
6. Funkcje zaprzyjanione
Modykacja pierwotnej klasy punkt moe mie posta pokazan na wydruku
6.6.
Listing 6.6. Dostp do skadowych prywatnych; funkcje zaprzyjanione
1
11
// d e k l a r a c j a k l a s y punkt ,
p l i k punkt1 . h
#i f n d e f _PUNKT1_H
#d e f i n e _PUNKT1_H
c l a s s punkt
{ int x , y ;
public :
friend void pokaz ( const punkt & ) ;
punkt ( i n t xx = 0 , i n t yy = 0 )
{ x = xx ;
y = yy ;
}
};
#e n d i f
Funkcja pokaz() jest zaprzyjaniona z klas punkt i ma dostp do prywatnych danych x i y. Jeeli powstanie obiekt klasy punkt o nazwie p to moemy
uzyska dostp do jego skadowych klasycznie:
p.x
p.y
// d e f i n i c j a k l a s y p u n k t , p l i k p u n k t 1 . cpp
#include " punkt1 . h"
#include <i o s t r e a m . h>
void pokaz ( const punkt &p )
{ c o u t << " w s p o l r z e d n e punktu : " << p . x << " " << p . y
<< " \n" ;
}
// program t e s t u j a c y f u n k c j e z a p r z y j a z n i o n e
#include <i o s t r e a m . h>
#include <c o n i o . h>
#include <punkt1 . h>
10
12
14
i n t main ( )
{
c o u t << " zmienna automatyczna : " << e n d l ;
punkt p1 ( 1 0 , 2 0 ) ;
pokaz ( p1 ) ;
c o u t << " zmienna dynamiczna : " << e n d l ;
punkt wsk ;
wsk = new punkt ( 2 0 , 4 0 ) ;
pokaz ( wsk ) ;
getch () ;
return 0 ;
}
W pokazanym programie przypomniano take tworzenie obiektw dynamicznych. W jzyku C++ dla kadego programu przydzielany jest pewien
obszar pamici dla alokacji obiektw tworzonych dynamicznie. Obszar ten
jest zorganizowany w postaci kopca (ang. heap). Na kopcu alokowane s
obiekty dynamiczne. Obiekty dynamiczne tworzone s przy pomocy operatora new. Operator new alokuje (przydziela) pami na kopcu. Gdy obiekt
nie jest ju potrzebny naley go zniszczy przy pomocy operatora delete.
Aby mona byo zastosowa operator new naley najpierw zadeklarowa
zmienn wskanikow, dla przykadu:
int
wsk ;
lub
wsk = new typ ( w a r t o s c ) ;
gdzie typ oznacza typ zmiennej (np. int, oat, itp.) Moemy deklaracj
zmiennej wskanikowej poczy z tworzeniem zmiennej dynamicznej:
typ wsk = newt typ ;
133
134
6. Funkcje zaprzyjanione
i n t wsk = new i n t ;
11
13
15
17
class prostokat
{ i n t xp , yp , xk , yk ;
public :
p r o s t o k a t ( i n t xpo , i n t ypo , i n t xko , i n t yko ) ;
void m i e j s c e ( punkt &p ) ;
};
c l a s s punkt
{ i n t x1 , y1 ;
public :
punkt ( i n t ax , i n t ay ) ;
friend void p r o s t o k a t : : m i e j s c e ( punkt &p ) ;
};
21
23
25
punkt : :
punkt ( i n t ax , i n t ay )
{ x1 = ax ;
y1 = ay ;
}
27
29
31
33
35
37
39
41
43
i n t main ( )
{ p r o s t o k a t pr ( 0 , 0 , 1 0 0 , 1 0 0 ) ;
punkt pu ( 1 0 , 1 0 ) ;
pr . m i e j s c e ( pu ) ;
getche () ;
return 0 ;
}
Funkcja miejsce() jest zwyk funkcj skadow klasy prostokat. W deklaracji funkcji skadowej miejsce() argumentem jest obiekt klasy punkt, dlatego konieczna jest deklaracja zapowiadajca klas punkt. Deklaracja klasy
punkt ma posta:
c l a s s punkt
{ i n t x1 , y1 ;
public :
punkt ( i n t ax , i n t ay ) ;
135
136
6. Funkcje zaprzyjanione
friend void p r o s t o k a t : : m i e j s c e ( punkt &p ) ;
};
// p l i k w e k t o r 1 . h , d e k l a r a c j a k l a s y wekt
#i f n d e f _WEKTOR1_H
#define _WEKTOR1_H
class macierz ;
7
c l a s s wekt
{
// d e k l a r a c j a z a p o w i a d a j a c a
11
13
15
17
double v [ 4 ] ;
// w e k t o r o 4 s k l a d o w y c h
public :
wekt ( double v1 =0 , double v2 =0 ,
double v3 =0 , double v4=0)
{v [ 0 ] = v1 ; v [ 1 ] = v2 ; v [ 2 ] = v3 ; v [ 3 ] = v4 ; }
friend wekt i l o c z y n ( const m a c i e r z &, const wekt &) ;
void pokaz ( ) ;
};
#endif
Podobnie w deklaracji klasy macierz musimy zastosowa deklaracje zapowiadajc klasy wekt. Deklaracja klasy macierz do obsugi macierzy moe
mie posta:
Listing 6.11. Iloczyn macierzy przez wektor; funkcje zaprzyjanione
// p l i k m a c i e r z . h , d e k l a r a c j a k l a s y m a c i e r z
2
#i f n d e f _MACIERZ_H
#define _MACIERZ_H
c l a s s wekt ;
class macierz
{
double mac [ 4 ] [ 4 ] ;
// m a c i e r z 4 x4
public :
m a c i e r z ( ) ; // k o n s t r u k t o r z i n i c j a c j a na 0
m a c i e r z ( double t [ 4 ] [ 4 ] ) ; // k o n s t r u k t o r , dane z t a b l i c y
friend wekt i l o c z y n ( const m a c i e r z &, const wekt &) ;
};
10
12
14
16
#endif
// p l i k p o k a z . cpp , d e f i n i c j a s k l a d o w e j p o k a z ( )
#include <i o s t r e a m . h>
#include " wek tor1 . h"
void wekt : : pokaz ( )
{ int i ;
f o r ( i =0; i < 4 ; i ++)
c o u t << " \n" ;
}
137
138
6. Funkcje zaprzyjanione
W klasie macierz jest zadeklarowany konstruktor macierz(). Jego denicja
moe mie posta:
Listing 6.13. Iloczyn macierzy przez wektor; funkcje zaprzyjanione
1
11
// p l i k konmac . cpp , d e f i n i c j a k o n s t r u k t o r a
#include <i o s t r e a m . h>
#include " m a c i e r z . h"
k l a s y macierz
m a c i e r z : : m a c i e r z ( double t [ 4 ] [ 4 ] )
{ int i ;
int j ;
f o r ( i =0; i <4; i ++)
f o r ( j =0; j <4; j ++)
mac [ i ] [ j ] = t [ i ] [ j ] ;
}
// p l i k i l o c z y n . cpp
// d e f i n i c j a f u n k c j i i l o c z y n
#include " wek tor1 . h"
#include " m a c i e r z . h"
11
13
15
Moemy zacz testowa mnoenie macierzy przez wektor. Funkcje iloczyn() moemy wykorzysta do realizacji przeksztace w przestrzeni 3D. W
grace komputerowej wykorzystuje si tzw. wsprzdne jednorodne. W tych
wsprzdnych punkt (x,y,z) reprezentowany jest jako punkt w przestrzeni
4-wymiarowej (x,y,z,1). Poszczeglne przeksztacenia takie jak przesuniecie,
skalowanie czy obroty reprezentowane s macierzami 4x4. Aby otrzyma
pooenie nowego punktu P naley punkt pocztkowy P pomnoy przez
macierz przeksztacenia M:
P = M P
(6.1)
139
T (dx , dy , dz ) =
1
0
0
0
0
1
0
0
0 dx
0 dy
1 dz
0 1
(6.2)
S(sx , sy , sz ) =
sx 0 0
0 sy 0
0 0 sz
0 0 0
0
0
0
1
(6.3)
Mamy trzy macierze reprezentujce obroty wok osi x,y i z. Obrt wok
osi z przedstawia si nastpujco:
Rz () =
cos sin 0 0
sin cos 0 0
0
0
1 0
0
0
0 1
1
0
0
0
0 cos sin 0
0 sin cos 0
0
0
0
1
cos
0
sin
0
(6.4)
Rx () =
(6.5)
Ry () =
0 sin 0
1
0
0
0 cos 0
0
0
1
(6.6)
Te transformacje mona atwo zwerykowa: wynikiem obrotu o 90o jednostkowego wektora osi x
h
1 0 0 1
iT
iT
osi y.
0 1 0 1
Oglnie mamy do czynienia z mnoeniem macierzy przez wektor:
x
y
z
1
M11
M21
M31
M41
M12
M22
M32
M42
M13
M23
M33
M43
M14
M24
M34
M44
x
y
z
1
(6.7)
140
6. Funkcje zaprzyjanione
Listing 6.15. iloczyn macierzy przez wektor; funkcje zaprzyjanione
10
12
14
16
18
20
22
24
26
28
30
32
34
36
141
Zapis:
friend c l a s s A ;
oznacza, e wszystkie funkcje skadowe klasy A maja dostp do danych prywatnych i chronionych klasy B. Jeeli klasa A ma by zaprzyjaniona z klas
B, to deklaracja klasy A musi poprzedza deklaracj klasy B. Zagadnienie
to zilustrujemy przykadem.
Listing 6.16. Klasy zaprzyjanione
1
using namespace s t d ;
5
11
c l a s s Dane
{
i n t x1 , x2 ;
public :
Dane ( i n t a , i n t b ) { x1 = a ;
friend c l a s s Test ;
};
x2 = b ; }
13
15
17
19
21
23
c l a s s Test
{
public :
i n t min ( Dane a ) { return a . x1 < a . x2 ? a . x1 : a . x2 ; }
i n t i l o c z y n ( Dane a ) { return a . x1 a . x2 ; }
};
i n t main ( )
{
Dane l i c z b y ( 1 0 , 2 0 ) ;
Test t ;
142
6. Funkcje zaprzyjanione
c o u t << " m n i e j s z a t o : " << t . min ( l i c z b y ) << e n d l ;
c o u t << " i l o c z y n = " << t . i l o c z y n ( l i c z b y ) << e n d l ;
25
27
getche () ;
return 0 ;
29
W pokazanym programie klasa Test ma dwie funkcje skadowe (jedna wyznacza mniejsz z dwch liczb, druga wylicza iloczyn dwch liczb) i ma
dostp do danych prywatnych klasy Dane. Naley pamita, e klasy zaprzyjanione stosuje si stosunkowo rzadko w praktyce.
Rozdzia 7
Przecianie operatorw
7.1.
7.2.
7.3.
7.4.
7.5.
7.6.
7.7.
7.8.
Wstp . . . . . . . . . . . . . . . . . . . . . . . . .
Denicje . . . . . . . . . . . . . . . . . . . . . . .
Przeciony operator dodawania ( + ) . . . . . . .
Przeciony operator mnoenia ( * ) . . . . . . . .
Funkcja operatorowa w postaci niezalenej funkcji
Przecianie operatorw rwnoci i nierwnoci . .
Przecianie operatora przypisania ( = ) . . . . .
Przecianie operatora wstawiania do strumienia (
. .
. .
. .
. .
. .
. .
. .
)
.
.
.
.
.
.
.
.
144
144
147
150
154
161
165
173
144
7. Przecianie operatorw
7.1. Wstp
Programujc w jzyku C++ programista moe korzysta zarwno z typw wbudowanych jak i typw stworzonych przez siebie. W jzyku C++
zosta zdeniowany zestaw operatorw, wszystkie one s uywane w poczeniu z typami wbudowanymi. W jzyku C++ nie mona tworzy nowych
operatorw. Czsto jednak zachodzi potrzeba zmienienia sposobu dziaania
konkretnego operatora. Jzyk C++ pozwala na przecianie istniejcych
operatorw, to znaczy moemy nadawa im nowe znaczenie dla operacji
wykonywanych czciowo lub w caoci na obiektach typu klasa. By moe
przecianie operatorw na pierwszy rzut oka jest do egzotyczn technik,
ale wielu programistw nie zdaje sobie sprawy z tego, e uywa przecionych operatorw. Przykadem przecionego operatora jest operator dodawania (+). Operator dodawania dziaa inaczej na danych typu int i inaczej
na danych typu double. Takie dziaanie jest moliwe, poniewa operator
dodawania zosta przeciony w samym jzyku C++.
7.2. Denicje
Aby przeciy operator deniuje si funkcj (z nagwkiem i ciaem) w
zwykej postaci z wyjtkiem tego, e nazw funkcji jest sowo kluczowe operator poprzedzajce symbol przecianego operatora. Na przykad nazwa
funkcji operator+ mogaby by uyta do przecienie operatora dodawania
(+). Przecianie operatora realizowane jest:
albo w postaci niezalenej funkcji (zazwyczaj zaprzyjanionej z jedn
lub kilkoma klasami)
albo w postaci funkcji skadowej
W pierwszym przypadku, jeeli oper jest operatorem dwuargumentowym,
to zapis:
x oper y
145
7.2. Denicje
Tabela 7.1. Tabela operatorw, ktre mog by przeciane
Operator
()
[]
>
New
Delete
++
*
*
/
%
+
<<
>>
<
<=
>
>=
==
!=
&&
||
&
|
=
+= -= *=
/= %= &=
= | =
<<=>>=
,
Opis
Wywoanie funkcji (Function call)
Element tablicy (Array element)
Operator dostepu (Structure memeber pointer reference)
Dynamicznie alokowana pami (Dynamically allocate memory)
Dynamicznie usuwana pami (Dynamically deallocated memory)
Inkrementacja (Increment)
Dekrementacja (Decrement)
Minus (Unary minus)
Logiczna negacja (Logical negation)
Dopenienie logiczne (Ones complement)
Dereferencja (Indirection)
Mnoenie (Multiplication)
Dzielenie (Division)
Modulo (Modulus (remainder))
Dodawanie (Addition)
Odejmowanie (Subtraction)
Przesunicie (Left shift)
Przesunicie (Right shift)
Mniej ni (Less than)
Mniej niz lub rowne (Less than or equel to)
Wiksze ni (Greater than)
Wiksze ni lub rwne (Greater than or equel to)
Rwne (Equal to)
Rne (Not equal to)
Logiczne AND (Logical AND)
Logiczne OR (Logical OR)
Bitowe AND (Bitwise AND)
Bitowe XOR (Bitwise exlusive OR)
Bitowe OR (Bitwise inclusive OR)
Przypisanie (assignment)
Przypisanie (assignment)
Przypisanie (assignment)
Przypisanie (assignment)
Przypisanie (assignment)
Przecinek (Comma)
146
7. Przecianie operatorw
mog by przeciane jest pokazana w tabeli. Kady z tych operatorw moe
by przeciony w dowolny sposb (np. operator dodawania po przecieniu
wcale nie musi wykonywa operacji dodawania). Oczywicie s pewne cise
reguy. Operator binarny musi pozosta binarnym, a operator unarny musi
pozosta unarnym.
Dziaanie symboli pokazanych w tabeli moe by przedeniowane tak,
aby jak najlepiej obsugiwa swoj klas.
Naley pamita o nastpujcych ograniczeniach:
Symbole, ktre nie s umieszczone w tabeli nie mog by przeciane.
Odnosi si to takich symboli jak:
operator kropka(.)
operator wyuskania wskanika do skadowej (.*)
operator zasigu (::)
operator warunkowy (?:)
operator rozmiaru (sizeof)
symbole # i ##
Nie mona kreowa nowych symboli operatorw. Np. nie jest operatorem w C++, nie moe by kreowany jako operator klasy. W jzyku
C++ nie istnieje operator potgowania, wic prba tworzenia wasnego
operatora potgowania nie wydaje si konieczna, w jzyku istnieje odpowiednia funkcja, ktr naley wywoa w celu wykonania potgowania.
Ani kolejno (ang. precedence) ani czno (ang. associativity) operatorw C++ nie moe by modykowana. Nie mona np. operatorowi
dodawania nada wyszy priorytet ni ma operator dzielenia.
Nie mona redeniowa operatorw dla typw wbudowanych
Operator unarny nie moe by zmieniany na binarny, a binarny nie moe
by zmieniany na unarny.
Operator musi by albo czonkiem klasy albo by tak deniowany, aby
przynajmniej jeden czonek klasy by jego operandem.
Zazwyczaj konstruujc jak klas naley okreli, czy potrzebne bd
specjalne operatory. Na przykad, aby porwna dwa trjwymiarowe wektory, naley kolejno sprawdzi czy odpowiednie skadowe (x, y oraz z) dwch
wektorw s rwne. Zdeniowana przez uytkownika operacja (operator)
jest projektowana jako funkcja, ktra redeniuje wbudowany w C++ symbol operatora i moe by wykorzystana przez klas. Funkcja, ktra deniuje
operacje na obiektach klasy i wykorzystuje wbudowane w C++ symbole operatorw nosi nazw funkcji operatorowej (operator function). Funkcje operatorowe s deklarowane i implementowane w taki sam sposb, jak wszystkie
funkcje skadowe, z jednym wyjtkiem wszystkie funkcje operatorowe maj
nazw postaci:
gdzie op jest jednym z symboli pokazanych w tabeli przecianych operatorw. Na przykad, nazwa funkcji operator + jest nazw funkcji dodawania,
a nazwa funkcji operator == jest nazw funkcji porwnujcej rwny z.
11
13
15
17
19
21
23
25
27
29
31
33
35
147
148
7. Przecianie operatorw
// suma_wek = a . o p e r a t o r +(b ) ;
suma_wek . pokaz ( ) ;
getche () ;
return 0 ;
37
39
41
Ten przykadowy program uywa klasy wek i funkcji skadowej, ktra jest
przecionym operatorem dodawania. W omawianym programie wykonywane jest dodawanie dwch wektorw (dwuwymiarowych), w wyniku otrzymuje trzeci wektor. Operacja dodawania wektorw polega na oddzielnym
dodawaniu skadowych poszczeglnych wektorw:
a+b=c
(7.1)
ax + bx = cx
(7.2)
ay + by = cy
(7.3)
Zadanie dodawania wektorw zrealizujemy za pomoc operatora +, przecionego w taki sposb, aby wykona pokazan metod dodawanie skadowych wektorw. W konwencji uywanej przez ten program po dodaniu
wektora a do wektora b otrzymamy wektor c. Opracowana klasa ma posta:
c l a s s wek
{ i n t vx , vy ;
public :
void ustaw ( i n t ux , i n t uy ) ;
void pokaz ( ) ;
wek operator+ ( wek v ) ;
};
W deklaracji klasy wek widzimy dwie dane prywatne vx i vy, oraz trzy
funkcje skadowe. Funkcja dostpu ustaw() suy do inicjowania skadowych
wektora, funkcja pokaz() suy do wywietlania skadowych wektora. W deklaracji klasy wek mamy take funkcj operatorow (funkcja przecionego
operatora), ktra nadaje nowe znaczenie operatowi +:
wek operator+ ( wek v ) ;
oznacza this > vx, czyli element vx obiektu, ktry spowodowa wywoanie
funkcji operatora. Naley zapamita nastpujc regu:
Obiekt powodujcy wywoanie funkcji operatora znajduje si
zawsze po lewej stronie operacji. Obiekt wystpujcy po prawej
stronie jest przekazywany funkcji w postaci argumentu.
Jeeli funkcja operatorowa jest skadow klasy to przypadku przecienia
operatora jednoargumentowego nie ma potrzeby przesyania argumentw.
W obu sytuacjach (przecianie operatorw jednoargumentowych i przecianie operatorw dwuargumentowych) obiekt powodujcy wywoanie funkcji operatora jest jej niejawnie przekazywany za pomoc wskanika this. W
denicji funkcji operatorowej w instrukcji:
wek tor . vx = vx + v . vx ;
149
150
7. Przecianie operatorw
Z kolei w funkcji testujcej instrukcj:
suma_wek = a + b ;
wektor suma wek otrzymuje wartoci skadowych, ktre s sum poszczeglnych skadowych wektorw a i b.
Wykonanie pokazanej instrukcji skada si z dwch operacji:
1. Najpierw wywoany jest operator dodawania (+), ktrego dwoma argumentami s wektory a i b. Operator dodawania zwraca nowy tymczasowy
obiekt wektora jako wynik swojego dziaania.
2. Nowy wektor zwrcony przez operator dodawania jest przypisany wektorowi suma wek za pomoc domylnego operatora przypisania, ktry
kopiuje skadowe wektorw.
Niektre operatory posiadaj specjalne waciwoci, do takich naley
operator przypisania (=). Domylny operator przypisania zdeniowany jest
dla kadej klasy. Przypisuje on poszczeglne skadowe obiektw i zwraca
obiekt, ktremu przypisana zostaa nowa warto. Przecianie operatora
przypisania wymaga rozwizania kilku wanych kwestii, szczeglnie, gdy
skadowymi klas s wskaniki. Istniej take inne operatory automatycznie
generowane dla kadej klasy. S to:
operator pobrania adresu ( & ), obiektu danej klasy
operator przecinka ( , )
operator tworzenia obiektw dynamicznych ( new )
operator niszczenia obiektw dynamicznych ( delete )
using namespace s t d ;
5
11
13
15
c l a s s ulamek
{ i n t l i , mia ;
public :
ulamek ( ) ;
ulamek ( int , i n t ) ;
ulamek operator ( ulamek ) ;
void pokaz ( ) ;
};
ulamek : : ulamek ( ) :
{ }
17
19
21
23
25
ulamek : : ulamek ( i n t a , i n t b )
{ i f ( b == 0 )
{ c e r r << " mianownik = 0 " << e n d l ;
e x i t (EXIT_FAILURE) ;
}
li = a;
mia = b ;
}
27
29
31
33
35
37
39
41
43
45
47
i n t main ( )
{ ulamek u1 ( 3 , 4 ) , u2 ( 5 , 6 ) ;
c o u t << " ulamek 1 = " ;
u1 . pokaz ( ) ;
c o u t << " ulamek 2 = " ;
u2 . pokaz ( ) ;
ulamek wynik ;
wynik = u1 u2 ;
c o u t << " i l o c z y n ulamkow= " ;
wynik . pokaz ( ) ;
getche () ;
return 0 ;
}
151
152
7. Przecianie operatorw
ulamek 2 = 5/6
i l o c z y n ulamkow = 15/24
i inicjuje uamek 0/1 ( licznik jest rwny zeru, mianownik jest rwny jeden). W denicji konstruktora wykorzystalimy konstrukcj z tak zwan
list inicjacji. Po nazwie konstruktora umieszczamy znak dwukropka, po
nim pojawia si lista inicjacji atrybutw. Powysza denicja konstruktora
rwnowana jest klasycznej konstrukcji:
ulamek : : ulamek ( )
{
l i = 0;
mia = 1 ;
}
Widzimy, e pierwszym argumentem operatora mnoenia jest uamek, poniewa deklaracja funkcji operatorowej jest wewntrz klasy ulamek. Drugim
argumentem operatora jest take uamek, poniewa ten typ zosta umieszczony na licie argumentw operatora. Rezultatem zastosowania operatora
mnoenia bdzie nowy uamek, poniewa typ ulamek poprzedza sowo kluczowe operator. Implementacja operatora mnoenia ma posta:
ulamek ulamek : : operator ( ulamek x )
{
return ulamek ( l i x . l i , mia x . mia ) ;
}
Operator mnoenia zdeniowany w klasie ulamek jest operatorem dwuargumentowym. Zastosowanie operatora zakresu postaci ulamek :: sprawia,
e pierwszym argumentem operatora mnoenia jest obiekt klasy ulamek,
na rzecz ktrego operator zosta wywoany. Drugi argument jest umieszczony na licie argumentw operatora mnoenia jest to take obiekt klasy
ulamek. W programie testujcym tworzone s dwa uamki, korzystamy z
konstruktorw:
ulamek u1 ( 3 , 4 ) , u2 ( 5 , 6 ) ;
Tworzone s dwa obiekty: u1 (uamek 3/4 ) oraz u2 (uamek 5/6 ). Deklarujemy jeszcze jeden uamek i wykonujemy mnoenie uamkw:
ulamek wynik ;
wynik = u1 u2 ;
153
154
7. Przecianie operatorw
moc parametru uamkiem. W naszym przypadku komunikatem tym jest
funkcja operatorowa, a odbiorc tego komunikatu jest uamek. Wyraenie
postaci:
u1 u2
using namespace s t d ;
c l a s s ulamek
{ i n t l i , mia ;
public :
ulamek ( int , i n t ) ;
// k o n s t r u k t o r
friend i n t i l o c z y n _ L i ( ulamek &, ulamek &) ;
friend i n t iloczy n_Mi ( ulamek &, ulamek &) ;
void pokaz ( ) ;
};
10
12
14
16
18
20
ulamek : : ulamek ( i n t a , i n t b )
{ i f ( b == 0 )
{ c e r r << " mianownik = 0 " << e n d l ;
e x i t (EXIT_FAILURE) ;
}
li = a;
mia = b ;
}
22
24
26
28
30
32
34
36
38
155
156
7. Przecianie operatorw
mianownik = iloczy n_Mi ( u1 , u2 ) ;
ulamek u3 ( l i c z n i k , mianownik ) ;
c o u t << " i l o c z y n ulamkow= " ;
u3 . pokaz ( ) ;
getche () ;
return 0 ;
40
42
44
46
11
13
15
17
19
21
23
25
27
29
31
33
35
37
157
158
7. Przecianie operatorw
u1 . pokaz ( ) ;
c o u t << " ulamek 2 = " ;
u2 . pokaz ( ) ;
u3 = u1 u2 ;
c o u t << " i l o c z y n ulamkow= " ;
u3 . pokaz ( ) ;
getche () ;
return 0 ;
39
41
43
45
159
160
7. Przecianie operatorw
Implementacja moe mie posta:
ulamek operator ( ulamek &x , ulamek &y )
{
return ulamek ( x . l i y . l i , x . mia y . mia ) ;
}
to widzimy widoczne zalety gdy argumenty przekazywane s przez referencj. Przede wszystkim nie tworzymy kopii argumentw, nie tworzymy take
tymczasowego obiektu, ktry musi by nastpnie zniszczony. Zagadnienie
wydajnoci w naszym przykadzie nie odgrywa wikszej roli, gdy mnoymy
dwa uamki, staj si jednak istotne, gdy zechcemy dokona mnoe tysicy
uamkw.
Listing 7.5. mnoenie uamkw funkcja operatorowa zaprzyjaniona
2
// f u n k c j a operatorowa , z a p r z y j a z n i o n a , r e f e r e n c j e
#include <i o s t r e a m >
#include <c o n i o >
using namespace s t d ;
6
10
12
14
c l a s s ulamek
{ i n t l i , mia ;
public :
ulamek ( ) ;
// k o n s t r u k t o r
ulamek ( int , i n t ) ;
// k o n s t r u k t o r
friend ulamek operator ( ulamek &x , ulamek &y ) ;
void pokaz ( ) ;
};
22
ulamek : : ulamek ( i n t a , i n t b )
{ i f ( b == 0 )
{ c e r r << " mianownik = 0 " << e n d l ;
e x i t (EXIT_FAILURE) ;
}
li = a;
mia = b ;
}
24
16
18
20
28
30
32
34
36
38
40
42
44
Mimo zalet zwizanych ze stosowaniem operatorowych funkcji zaprzyjanionych naley pamita o szeregu ogranicze. Podczas przeciania operatorw inkrementacji i dekrementacji konieczne jest stosowanie parametrw
referencyjnych. Kolejnym ograniczenie jest fakt, e za pomoc funkcji zaprzyjanionych nie mona przecia nastpujcych operatorw:
operator przypisani ( = )
operator odwoania do elementu tablicy ( [ ] )
operator odniesienia do skadowej klasy ( > )
operator ()
161
162
7. Przecianie operatorw
public :
wek tor3d ( double w1 =0.0 , double w2 =0.0 ,
double w3 = 0 . 0 )
{
x = w1 ;
y = w2 ;
z = w3 ;
}
i n t operator == ( wek tor3d ) ;
i n t operator != ( wek tor3d ) ;
};
// d e f i n i c j a z f u n k c j a m i skladowymi
#include <i o s t r e a m . h>
#include <c o n i o . h>
10
12
14
16
18
20
22
24
26
28
30
32
34
36
c l a s s wek tor3d
double x , y , z ;
{
public :
wek tor3d ( double w1 =0.0 , double w2 =0.0 ,
double w3 = 0 . 0 )
{
x = w1 ;
y = w2 ;
z = w3 ;
}
i n t operator == ( wek tor3d ) ;
i n t operator != ( wek tor3d ) ;
};
i n t wek tor3d : : operator == ( wek tor3d v )
{
i f ( ( v . x == x ) && ( v . y == y ) && ( v . z == z ) )
return 1 ;
e l s e return 0 ;
}
i n t wek tor3d : : operator != ( wek tor3d v )
{
return ! ( ( t h i s ) == v ) ;
}
i n t main ( )
{
wek tor3d v1 ( 1 0 , 1 0 , 2 0 ) , v2 ( 2 0 , 3 0 , 4 0 ) , v3 ( 1 0 , 1 0 , 2 0 ) ;
// 3 o b i e k t y
i f ( v1 == v2 )
c o u t << " \n wek tory v1 i v2 s a i d e n t y c z n e " << e n d l ;
else
c o u t << " \n wek tory v1 i v2 n i e s a rowne " << e n d l ;
i f ( v1 == v3 )
c o u t << " \n wek tory v1 i v3 s a i d e n t y c z n e " << e n d l ;
else
c o u t << " \n wek tory v1 i v3 n i e s a rowne " << e n d l ;
40
42
i f ( v1 != v2 )
c o u t << " \n wek tory v1 i v2 n i e s a rowne " << e n d l ;
getch () ;
return 0 ;
}
Kada z nich ma jeden argument typu wektor3d i jeden argument domniemany this. Implementacji funkcji operator== ma posta:
i n t wek tor3d : : operator == ( wek tor3d v )
{
i f ( ( v . x == x ) && ( v . y == y ) && ( v . z == z ) )
return 1 ;
e l s e return 0 ;
}
// d e f i n i c j a z f u n k c j a m i z a p r z y j a z n i o n y m i
#include <i o s t r e a m . h>
#include <c o n i o . h>
c l a s s wek tor3d
{ double x , y , z ;
163
164
7. Przecianie operatorw
public :
wek tor3d ( double w1 =0.0 , double w2 =0.0 ,
double w3 = 0 . 0 )
{
x = w1 ; y = w2 ; z = w3 ;
}
friend i n t operator == ( wektor3d , wek tor3d ) ;
friend i n t operator != ( wektor3d , wek tor3d ) ;
11
13
};
15
17
19
21
23
25
i n t main ( )
{
wek tor3d v1 ( 1 0 , 1 0 , 2 0 ) , v2 ( 1 0 , 1 0 , 2 0 ) , v3 ( 1 0 , 2 0 , 2 0 ) ;
27
29
31
33
35
cout
<<
cout
<<
cout
<<
cout
<<
37
39
==
==
!=
!=
v2
v3
v2
v3
:
:
:
:
prawda
falsz
falsz
prawda
Widzimy, e operator przeciony moe by zdeniowany jako funkcja skadowa i jako funkcja globalna (lub zaprzyjaniona). Wobec tego musimy odpowiedzie na pytanie, jakie s kryteria wyboru implementacji przeciania
operatora. Prawd mwic nie ma generalnej zasady, wszystko zaley od
zastosowania przecionego operatora.
Jeeli operator modykuje operandy to powinien by zdeniowany jako funkcja skadowa klasy. Przykadem s tu takie operatory jak: =, +=, -=, *=, ++, itp. Jeeli operator nie modykuje
swoich operandw to naley go deniowa jako funkcj globaln
lub zaprzyjanion. Przykadem s tu takie operatory jak: +, -,
==, &, itp.
165
166
7. Przecianie operatorw
co oznacz, e odpowiednie pola obiektu d2 s przypisane polom obiektu d1.
Ten typ przypisania nosi nazw przypisania danych skadowych (memberwise assignment).
Listing 7.8. przypisanie bez przeciania operatorw
1
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
i n t main ( )
{ data d1 ( 1 3 , 3 , 2 0 0 3 ) , d2 ( 1 5 , 5 , 2 0 0 4 ) ;
c o u t << " \n p i e r w s z a data , d1 : " ;
d1 . pokaz ( ) ;
c o u t << " \n druga data , d2 : " ;
d2 . pokaz ( ) ;
d1 = d2 ;
c o u t << " \ npo p o d s t a w i e n i u d1 : " ;
d1 . pokaz ( ) ;
c o u t << e n d l ;
getche () ;
return 0 ;
}
W denicji operatora zastosowano referencj. W tej denicji d jest zdeniowane jako referencja do klasy Data. W ciele funkcji operatorowej skadowa
dzien obiektu d jest przypisana skadowej dzien aktualnego obiektu:
dzien = d . dzien ;
167
168
7. Przecianie operatorw
a . operator =(b ) ;
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
41
Funkcja operatora przypisania zaprezentowana w tym przykadzie, aczkolwiek poprawna, nie obsuy poprawnie prby przypisania wielokrotnego postaci:
a = b = c;
169
170
7. Przecianie operatorw
Data ( i n t =1 , i n t = 1 , i n t = 2 0 0 0 ) ;
void pokaz ( ) ;
// k o n s t r u k t o r
W ten sposb, kada funkcja skadowa otrzymuje aktualnie dodatkowy argument, ktry jest adresem struktury danych. Aby si o tym przekona
moemy uywa tych wskanikw w sposb jawny. Zademonstrujemy uycie wskanika this w krtkim programie (oczywicie w praktyce nie korzysta
si z tej techniki).
Listing 7.10. jawne uycie wskanika this
2
10
12
16
18
}
i n t punkt : : ustaw_2 ( i n t a , i n t b )
{ this>x = this>x + a ;
this>y = this>y + b ;
return x ;
}
20
22
24
26
28
i n t main ( )
{ punkt p1 ;
c o u t << " ustaw_1 , x= " ;
c o u t << p1 . ustaw_1 ( 1 0 , 10 ) << e n d l ;
c o u t << " ustaw_2 , x= " ;
c o u t << p1 . ustaw_2 ( 2 0 , 20 ) << e n d l ;
getche () ;
return 0 ;
}
171
172
7. Przecianie operatorw
Naley pamita, e funkcja operatorowa przypisania musi by funkcj skadow klasy, nie moe by funkcja zaprzyjanion. W przypadku przypisania
takiego jak b = c , (rwnowana forma b.operator=(c) ), wywoana funkcja
zmienia dane skadowe obiektu b na dane obiektu c i zwraca now warto
obiektu b. Taka operacja umoliwia wielokrotne przypisanie typu a = b =
c. Implementacja przecionego operatora przypisania i jego zastosowanie
pokazano na kolejnym wydruku.
Listing 7.11. rozszerzona wersja operatora przypisania
2
10
#i n c l u d e <i o s t r e a m >
#include <iomanip>
#include <c o n i o >
using namespace s t d ;
c l a s s Data
{ i n t d z i e n , m i e s i a c , rok ;
public :
Data ( int , int , i n t ) ;
// k o n s t r u k t o r
void pokaz ( ) ;
Data operator =(const Data &) ;
};
12
14
16
Data : : Data ( i n t dd = 1 , i n t mm = 1 , i n t r r = 2 0 0 0 )
{ d z i e n = dd ;
m i e s i a c = mm;
rok = r r ;
}
18
20
22
24
26
28
30
32
34
36
38
40
42
44
46
cout jest egzemplarzem obiektu klasy ostream, klasa ta jest zawarta w bibliotece standardowej. Operator jest zdeniowany w klasie ostream, a co
wicej jest on przeciony w ten sposb, e moemy go wykorzystywa dla
wszystkich wbudowanych typw danych. Moliwe jest rwnie takie przedeniowanie tego operatora, aby mona byo wywietla dane typw zdeniowanych przez uytkownika. Ma to due znaczenie praktyczne. Rozwamy
przykadow klas punkt:
c l a s s punkt
{
private :
int x ;
int y ;
public :
173
174
7. Przecianie operatorw
punkt ( i n t a , i n t b ) { x = a ; y = b ; }
i n t pokazX ( ) { return x ; }
i n t pokazY ( ) { return y ; }
// k o n s t r u k t o r
};
W klasie mamy dwie dane prywatne x i y oraz dwie funkcje skadowe pokazX() i pokazY() dla uzyskania dostpu do zmiennych prywatnych. Jeeli
chcemy wyswietli wartoci danych x i y do musimy napisa dwie instrukcje
strumienia cout z odpowiednimi argumentami dla operatora wstawiania .
To zagadnienie ilustruje pokazany poniej przykad. Aby wywietli wartoci
danych x i y musimy napisa dwie instrukcje:
c o u t << " x = " << p1 . pokazX ( ) << e n d l ;
c o u t << " y = " << p1 . pokazY ( ) << e n d l ;
10
c l a s s punkt
{ int x , y ;
public :
punkt ( i n t a , i n t b ) { x = a ; y = b ; }
i n t pokazX ( ) { return x ; }
i n t pokazY ( ) { return y ; }
};
// k o n s t r u k t o r
12
i n t main ( )
14
{
punkt p1 ( 5 , 1 5 ) ;
c o u t << " x= " << p1 . pokazX ( ) << e n d l ;
c o u t << " y= " << p1 . pokazY ( ) << e n d l ;
getche () ;
return 0 ;
16
18
20
W kolejnym przykadzie pokaemy jak mona przeciy operator wstawiania, aby mona byo wywietli dane obiektu przy pomocy jednej instrukcji. Klasyczna posta denicji operatora wstawiania ( ) jest nastpujca:
ostream & operator<< ( ostream & os , nazwa_klasy & ob )
{
// i n s t r u k c j e
return o s ;
}
11
13
15
17
i n t main ( )
175
176
7. Przecianie operatorw
19
21
23
25
punkt p1 ( 5 , 1 5 ) ;
c o u t << p1 ;
c o u t << " wy wolanie jawne : \n" ;
operator <<(cout , p1 ) ;
getche () ;
return 0 ;
albo
operator<<(cout , p1 ) ;
Rozdzia 8
Funkcje statyczne i wirtualne
8.1.
8.2.
8.3.
8.4.
8.5.
Wstp . . . . . . . . . . .
Metody i dane statyczne
Polimorzm . . . . . . .
Funkcje wirtualne . . . .
Funkcje abstrakcyjne . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
178
178
183
187
194
178
8.1. Wstp
Jedn z fundamentalnych cech programowania obiektowego jest polimorzm (innymi wanymi cechami, jak pamitamy s abstrakcja danych,
ukrywanie danych i dziedziczenie). Polimorzm zapewnia tworzenie bardziej
zrozumiaego kodu a w powizaniu z wykorzystaniem funkcji wirtualnych
umoliwia zaprojektowanie i wdroenie programw, ktre s stosunkowo
bardziej rozszerzalne. Na etapie kompilacji polimorzm jest realizowany
przy pomocy przecianych funkcji i operatorw, na etapie wykonania jest
realizowany przy pomocy dziedziczenia i funkcji wirtualnych. Dla wielu pocztkujcych programistw pojcie funkcji wirtualnych moe by pocztkowo niejasne, poniewa nie posiadaj one swoich odpowiednikw w jzykach
proceduralnych. Twierdzi si, e funkcje wirtualne s fundamentem programowania obiektowego.
179
i n t ST : : y ;
i n t ST : : z = 1 ;
int l i c z n i k
:: n;
// d e f i n i c j a d a n e j s t a t y c z n e j
11
13
15
17
19
i n t main ( )
{
l i c z n i k obj1 ;
c o u t << " i l o s c objek tow : " << l i c z n i k
l i c z n i k obj2 ;
c o u t << " i l o s c objek tow : " << l i c z n i k
getche () ;
return 0 ;
}
: : n << e n d l ;
: : n << e n d l ;
Moemy take deklarowa metody klasy jako statyczne, pamitajc jednak o kilku ograniczeniach. Statyczne funkcje skadowe mog odwoywa
si bezporednio jedynie do danych statycznych klasy (i oczywicie do danych globalnych). Skadowe funkcje statyczne nie posiadaj wskanika this,
180
11
13
15
/ / statyczna funkcja
// l i c z b a p a c j e n t o w
17
int p acjen t : : n = 0 ;
// i n i c j a c j a d a n e j s t a t y c z n e j
19
i n t p a c j e n t : : l i c z b a ( ) { return n ; }
21
23
25
27
29
31
33
35
37
pacjent : : ~pacjent ()
{ c o u t <<" zbadany p a c j e n t "<< i m i e << " "
<< nazwisk o << e n d l ;
delete i m i e ;
delete nazwisk o ;
n ;
}
39
41
43
45
47
49
51
53
55
57
59
i n t main ( )
{
c o u t << " l i c z b a p a c j e n t o w : "
<< p a c j e n t : : l i c z b a ( ) << e n d l ;
p a c j e n t p1 = new p a c j e n t ( " Jan " , " F a s o l a " ) ;
p a c j e n t p2 = new p a c j e n t ( " Emil " , " Burak " ) ;
p a c j e n t p3 = new p a c j e n t ( " Borys " , " D e l f i n " ) ;
c o u t << " l i c z b a p a c j e n t o w : " << p1>l i c z b a ( )
<< e n d l ;
delete p1 ;
delete p2 ;
delete p3 ;
c o u t << " p o z o s t a l a l i c z b a p a c j e n t o w : "
<< p a c j e n t : : l i c z b a ( ) << e n d l ;
getche () ;
return 0 ;
}
Dana skadowa n przechowuje liczb obiektw klasy pacjent. Mamy take statyczn funkcje skadow liczba(), ktra dostarcza informacji o liczbie
utworzonych obiektw klasy pacjent:
i n t p a c j e n t : : l i c z b a ( ) { return n ; }
Konstruktor dynamicznie przydziela pami dla danych pacjenta oraz inkrementuje statyczn dan n :
p a c j e n t : : p a c j e n t ( char x imie , char x nazwisk o )
{ i m i e = new char [ s t r l e n ( x i m i e ) + 1 ] ;
s t r c p y ( imie , x i m i e ) ;
nazwisk o = new char [ s t r l e n ( x nazwisk o ) + 1 ] ;
s t r c p y ( nazwisk o , x nazwisk o ) ;
++n ;
// z w i e k s z a l i c z n i k p a c j e n t o w
c o u t << " p a c j e n t : " <<i m i e << " " << nazwisk o<< e n d l ;
}
181
182
wywietlamy liczb utworzonych obiektw. Nastpnie przy pomocy operatora delete zwalniamy pami. W naszym przypadku w tym momencie nie
istnieje ju aden utworzony obiekt klasy pacjent. W takim przypadku informacja o iloci obiektw dostpna jest jedynie za pomoc statycznej metody
liczba() :
c o u t << " p o z o s t a l a l i c z b a p a c j e n t o w : "
<< p a c j e n t : : l i c z b a ( ) << e n d l ;
Metoda liczba() suy do informowania o liczbie aktualnie istniejcych obiektw klasy pacjent. Naley zwrci uwag, e podczas zwalniania pamici
(operator delete ) wywoywany jest destruktor obiektu i wtedy nastpuje
dekrementacja statycznej danej skadowej n.
Po uruchomieniu programu mamy nastpujcy wynik:
l i c z b a pacjentow : 0
p a c j e n t : Jan F a s o l a
p a c j e n t : Emil Burak
pacjent
: Borys D e l f i n
l i c z b a pacjentow : 3
zbadany p a c j e n t Jan F a s o l a
zbadany p a c j e n t Emil Burak
zbadany p a c j e n t Borys D e l f i n
p oz ost ala l i c z b a pacjentow : 0
11
13
8.3. Polimorzm
15
17
19
21
23
25
27
i n t main ( )
{ wek tor w1 , w2 ;
w1 . s e t ( 1 , 1 , 1 ) ;
w2 . s e t ( 2 , 2 , 2 ) ;
c o u t <<" i l o c z y n = " << wek tor : : i l o c z y n (w1 , w2 ) << e n d l ;
w1 . s e t ( 5 , 5 , 5 ) ;
c o u t <<" i l o c z y n = " << w1 . i l o c z y n ( w1 , w2 ) << e n d l ;
getche () ;
return 0 ;
}
W pierwszym wywoaniu funkcja iloczyn() moe by wywoana samodzielnie, niezalenie od obiektu. Musimy w wyraeniu uy nazwy klasy i operatora zakresu. W drugim przypadku funkcja iloczyn() jest wywoana na
rzecz obiektu w1, co jest standardowym wywoaniem metody.
Naley zauway, e dostp do zmiennych i wywoania funkcji statycznych danej klasy nie wymagaj istnienia obiektw tej klasy.
8.3. Polimorzm
Wana cech programowania obiektowego w C++ jest polimorzm. Dziki polimorzmowi istnieje moliwo otrzymania rnego dziaania metod
w odpowiedzi na ten sam komunikat . Taka technika jest wykorzystywana w
183
184
const double PI = 2 . 0 a s i n ( 1 . 0 ) ;
class kolo
{ protected :
double r ;
public :
k o l o ( double r r = 1 ) ;
double o b l i c z ( ) ;
};
10
12
14
16
k o l o : : k o l o ( double r r ) { r = r r ; }
double k o l o : : o b l i c z ( ) { return PI r r ; }
18
20
22
24
26
28
30
32
34
c l a s s w a l e c : public k o l o
{ protected :
double h ;
public :
w a l e c ( double r r = 1 . 0 , double hh = 1 . 0 ) : k o l o ( r r ) ,
h ( hh ) { }
double o b l i c z ( ) ;
};
double w a l e c : : o b l i c z ( )
{ return ( h k o l o : : o b l i c z ( ) ) ; }
i n t main ( )
{
k o l o k1 , k2 ( 2 ) ;
walec walec1 ( 3 ,4) ;
c o u t << " p o l e k o l a k1 = " << k1 . o b l i c z ( ) << e n d l ;
8.3. Polimorzm
c o u t << " p o l e k o l a k2 = " << k2 . o b l i c z ( ) << e n d l ;
c o u t << " o b j e t o s c walca = " << w a l e c 1 . o b l i c z ( ) << e n d l ;
getche () ;
return 0 ;
36
38
Jest to sprytny sposb wymuszenia na kompilatorze zwrcenia wartoci liczby Pi z maksymaln precyzj jak oferuje nasz komputer.
W pokazanych klasach programu mamy funkcje o takiej samej nazwie
oblicz(). Metoda w klasie bazowej oblicza pole powierzchni koa, w klasie pochodne oblicza objto walca. Nadpisanie bazowej funkcji skadowej
przez przecion pochodn funkcj skadow, tak jak to pokazano w programie jest przykadem polimorzmu. Polimorzm pozwala na rne dziaania funkcji skadowej o takiej samej nazwie w zalenoci na rzecz jakiego
obiektu zostaa wywoana. Te rne wywoania wida w instrukcjach:
c o u t << " p o l e k o l a k2 = " << k2 . o b l i c z ( ) << e n d l ;
c o u t << " o b j e t o s c walca = " << w a l e c 1 . o b l i c z ( ) << e n d l ;
const double PI = 2 . 0 a s i n ( 1 . 0 ) ;
class kolo
{ protected :
double r ;
public :
10
185
186
14
16
18
20
22
24
26
k o l o ( double r r = 1 ) ;
double o b l i c z ( ) ;
};
k o l o : : k o l o ( double r r ) { r = r r ; }
double k o l o : : o b l i c z ( ) { return PI r r ; }
c l a s s w a l e c : public k o l o
{ protected :
double h ;
public :
w a l e c ( double r r = 1 . 0 , double hh = 1 . 0 ) : k o l o ( r r ) ,
h ( hh ) { }
double o b l i c z ( ) ;
};
double w a l e c : : o b l i c z ( )
{ return ( h k o l o : : o b l i c z ( ) ) ; }
28
30
32
34
36
38
40
i n t main ( )
{
k o l o k1 ;
walec walec1 ( 3 ,4) ;
k o l o wsk ;
wsk = & k1 ;
c o u t << "wsk p o l e k o l a k1 = " << wsk>o b l i c z ( ) << e n d l ;
wsk = & w a l e c 1 ;
c o u t << "wsk o b j e t o s c walca = " << wsk>o b l i c z ( )
<< e n d l ;
getche () ;
return 0 ;
}
We fragmencie kodu:
k o l o k1 ;
walec walec1 ( 3 ,4) ;
k o l o wsk ;
wsk = & k1 ;
c o u t << "wsk p o l e k o l a k1 = " << wsk>o b l i c z ( ) << e n d l ;
wsk = & w a l e c 1 ;
c o u t << "wsk o b j e t o s c walca = " << wsk>o b l i c z ( )
<< e n d l ;
zdeniowalimy egzemplarze obu klas oraz wskanik typu klasy bazowej wsk.
Wywoanie:
187
188
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
41
i n t main ( )
{ bazowa wsk ;
// w s k a z n i k k l a s y b a z o w e j
bazowa x ;
// o b i e k t t y p u bazowa
pochodna y ;
// o b i e k t t y p u pochodna
c o u t << " wy wolanie f u n k c j i w i r t u a l n e j
przy pomocy wsk aznik a : "<< e n d l ;
wsk = &x ;
// p r z y p i s a n i e a d r e s u o b i e k t u
// bazowego x
wsk > w i r f u n ( ) ;
// metod k l a s y bazowa
wsk = &y ;
// p r z y p i s a n i e a d r e s u o b i e k t u
// pochodnego y
wsk > w i r f u n ( ) ;
// metod k l a s y pochodna
c o u t << " wy wolanie f u n k c j i w i r t u a l n e j
przy pomocy r e f e r e n c j i : "<< e n d l ;
reffun (x) ;
// p r z e k a z a n i e o b i e k t u bazowego
// do r e f f u n ( )
reffun (y) ;
// p r z e k a z a n i e o b i e k t u pochodnego
// do r e f f u n ( )
getche () ;
return 0 ;
}
funkcji
klasa
klasa
funkcji
W naszym programie mamy klas bazow i pochodn, w kadej z nich zdeniowana jest metoda wirfun(), zgodnie z oczekiwaniami klasy. Funkcja main() testuje nasze klasy. W programie mamy zadeklarowane cztery zmienne:
wsk (wskanik klasy bazowej), x (obiekt klasy bazowej), y (obiekt klasy
pochodnej) i z ( referencja klasy bazowej).
W instrukcjach
wsk = &x ;
// p r z y p i s a n i e a d r e s u o b i e k t u bazowego x
wsk > w i r f u n ( ) ; // metod k l a s y bazowa
zmiennej wsk przypisywany jest adres y ( obiekt klasy pochodnej) a metoda wirfun() jest ponownie wywoywana. W takim przypadku wywoywana
jest metoda wirfun() zdeniowana w klasie pochodnej. Najwaniejsze jest
to, e wybr wersji funkcji wirfun() dokonywany jest na podstawie typu
obiektu wskazywanego przez wskanik wsk. Mwimy, e realizacja wersji
metody wykonywana jest w trakcie dziaania programu, co oznacza, e realizowany jest polimorzm na etapie wykonania. Naley zwrci uwag, e
klasyczne przecianie funkcji a nadpisywanie funkcji przy pomocy metod
wirtualnych jest cakiem innym mechanizmem. Konieczne jest, aby prototyp
funkcji nadpisywany w klasie pochodnej by identyczny jak w klasie bazowej.
Pamitamy, e podczas klasycznego nadpisywania, sygnatury funkcji musz
si rni. Niesie to pewne niebezpieczestwo. Jeeli w trakcie stosowania
metod wirtualnych zmienimy przypadkowo jaki element prototypu, funkcji
nadany zostanie status funkcji przecionej i moe to spowodowa bdne
wykonanie programu. W instrukcji:
// r e f e r e n c j a f u n k c j i b a z o w e j j a k o parametr :
void r e f f u n ( bazowa &z ) { z . w i r f u n ( ) ; }
189
190
Majc moliwo uycia metod wirtualnych moemy poprawi le dziaajcy program w ktrym ilustrowalimy poprzednio nadpisywanie funkcji.
Poprawny kod pokazany jest na kolejnym listingu.
Listing 8.7. funkcje wirtualne; poprawne uycie wskanika
2
const double PI = 2 . 0 a s i n ( 1 . 0 ) ;
class kolo
{ protected :
double r ;
public :
k o l o ( double r r = 1 ) ;
v i r t u a l double o b l i c z ( ) ; // w i r t u a l n a f u n k c j a s k l a d o w a
};
10
12
14
16
k o l o : : k o l o ( double r r ) { r = r r ; }
double k o l o : : o b l i c z ( ) { return PI r r ; }
18
20
22
24
26
28
c l a s s w a l e c : public k o l o
{ protected :
double h ;
public :
w a l e c ( double r r = 1 . 0 , double hh = 1 . 0 ) : k o l o ( r r ) ,
h ( hh ) { }
double o b l i c z ( ) ;
};
double w a l e c : : o b l i c z ( )
{ return ( h k o l o : : o b l i c z ( ) ) ; }
30
32
34
i n t main ( )
{
k o l o k1 ;
walec walec1 ( 3 ,4) ;
36
38
40
42
44
k o l o wsk ;
wsk = & k1 ;
c o u t << " wsk aznik , p o l e k o l a k1 = " << wsk>o b l i c z ( )
<< e n d l ;
wsk = & w a l e c 1 ;
c o u t << " wsk aznik , o b j e t o s c walca = " << wsk>o b l i c z ( )
<< e n d l ;
getche () ;
return 0 ;
}
Teraz uycie wskanika do obsugi wywoania metody oblicz() dziaa prawidowo! Jeeli w klasie pochodnej nadpisywana jest funkcja standardowa,
zdeniowana w klasie bazowej (nie wirtualna) mamy do czynienia z procesem nazywanym wizaniem funkcji (ang. function binding). W typowym
wywoaniu funkcji jest wykonane tzw. statyczne wizanie (ang. static binding). W statycznym wizaniu decyzja, jak wersj funkcji naley zrealizowa jest wykonana na etapie czasu kompilacji (ang. compile time). W wielu
przypadkach, chcemy aby zamiast decyzji o wizaniu statycznym, decyzja
o wersji metody bya podejmowana pniej, w czasie wykonania (ang. run
time), na podstawie typu obiektu, ktry wykona wywoanie funkcji. Oczywicie taki mechanizm zapewnia polimorzm, konkretnie funkcje wirtualne.
Ten typ wizania funkcji nosi nazw wizania dynamicznego (ang. dynamic
binding). Specykacja funkcji wirtualnej informuje kompilator jzyka C++
aby utworzy wskanik do funkcji lecz nie nadawa wartoci wskanikowi
dopki funkcja nie bdzie wywoana. W tej sytuacji w czasie wykonywania programu, na podstawie typu obiektu wykonujcego wywoanie metody, odpowiedni adres jest wykorzystany i odpowiednia wersja funkcji jest
zastosowana.
Jak ju pisalimy, wykorzystanie funkcji wirtualnych nieznacznie spowalnia wykonanie programu w porwnaniu z wykorzystaniem funkcji klasycznych. Jak ju opisalimy, dla klas niepolimorcznych metoda jest wybierana
w czasie kompilacji, mechanizm nosi nazw wczesnego wizania (ang. ear-
191
192
11
13
15
17
193
19
21
i n t main ( )
{ bazowa wsk = new pochodna ;
c o u t << " p r z y d z i e l o n y a d r e s = " << wsk << e n d l ;
delete wsk ;
23
getche () ;
return 0 ;
25
10
12
14
16
18
c l a s s bazowa
{ public :
bazowa ( ) { c o u t << " k o n s t r u k t o r k l a s y bazowej " << e n d l ; }
v i r t u a l ~bazowa ( ) { c o u t << " d e s t r u k t o r k l a s y bazowej "
<< e n d l ; }
};
c l a s s pochodna : public
{ public :
pochodna ( ) { c o u t <<
<<
~pochodna ( ) { c o u t <<
<<
};
bazowa
" k o n s t r u k t o r k l a s y p o c h o d n e j"
endl ; }
" d e s t r u k t o r k l a s y p o c h o d n e j"
endl ; }
194
20
22
24
26
i n t main ( )
{ bazowa wsk = new pochodna ;
c o u t << " p r z y d z i e l o n y a d r e s = " << wsk << e n d l ;
delete wsk ;
getche () ;
return 0 ;
}
11
13
15
17
19
21
23
25
27
29
31
33
195
196
10
12
class zwierz
// a b s t r a k c y j n a k l a s a bazowa
{ public :
zwierz () { };
zwierz ( s t r i n g iimie ) { imie = iimie ; }
v i r t u a l void typ ( ) = 0 ;
v i r t u a l void g l o s ( ) = 0 ;
v i r t u a l void o p i s ( ) = 0 ;
s t r i n g imie ;
};
14
16
18
20
22
24
26
28
30
32
34
c l a s s p i e s : public z w i e r z
{ public :
pies ( string iimie ) : zwierz ( iimie ) { };
private :
void typ ( ) { c o u t << " p i e s " ; }
void g l o s ( ) { c o u t << " s z c z e k a " ; }
void o p i s ( ) { c o u t << " n i e l u b i k ota " ; }
};
c l a s s k ot : public z w i e r z
{ public :
k ot ( s t r i n g i i m i e ) : z w i e r z ( i i m i e ) { } ;
private :
void typ ( ) { c o u t << " k ot " ; }
void g l o s ( ) { c o u t << " miauczy " ; }
void o p i s ( ) { c o u t << " n i e l u b i myszy " ; }
};
197
38
40
42
44
46
void i n f o ( z w i e r z tab [ ] , i n t i l e )
{ f o r ( i n t i = 0 ; i < i l e ; i ++)
{ tab [ i ]>typ ( ) ;
c o u t << tab [ i ]> i m i e ;
tab [ i ]> g l o s ( ) ;
tab [ i ]> o p i s ( ) ;
c o u t << e n d l ;
}
};
i n t main ( )
{ z w i e r z tab [ ] =
48
50
i n f o ( tab , 3 ) ;
cin . get () ;
return 0 ;
52
54
198
const double PI = 2 . 0 a s i n ( 1 . 0 ) ;
7
11
13
15
17
class o b l i c z
{ protected :
double x ;
// w a r t o s c argumentu
double y ;
// o b l i c z o n a w a r t o s c f u n k c j i
public :
o b l i c z ( double z ) { x = z ; }
double getwy nik ( ) { return y ; }
double g e t x ( ) { return x ; }
v i r t u a l void o b l i c z f u n ( ) = 0 ;
// c z y s t a f u n k c j a w i r t u a l n a
};
19
21
23
c l a s s f s i n : public o b l i c z
{ public :
f s i n ( double xx ) : o b l i c z ( xx ) { }
void o b l i c z f u n ( ) { y = s i n ( x ) ; }
};
25
27
29
c l a s s f c o s : public o b l i c z
{ public :
f c o s ( double xx ) : o b l i c z ( xx ) { }
void o b l i c z f u n ( ) { y = c o s ( x ) ; }
};
31
33
35
37
i n t main ( )
{ o b l i c z wsk ;
// w s k a z n i k k l a s y b a z o w e j
fsin
f s ( PI / 4 . 0 ) ;
// argument f u n k c j i s i n
wsk = &f s ;
c o u t << " d l a x= " << wsk>g e t x ( ) ;
wsk>o b l i c z f u n ( ) ;
c o u t << " s i n ( x )= " << wsk>getwy nik ( ) <<e n d l ;
39
fcos
f c ( PI / 4 . 0 ) ;
// argument f u n k c j i c o s
wsk = &f c ;
c o u t << " d l a x= " << wsk>g e t x ( ) ;
wsk>o b l i c z f u n ( ) ;
c o u t << " c o s ( x )= " << wsk>getwy nik ( ) <<e n d l ;
cin . get () ;
return 0 ;
41
43
45
47
199
s i n ( x )= 0 . 7 0 7 1 0 7
c o s ( x )= 0 . 7 0 7 1 0 7
W kolejnym programie zilustrujemy uycie funkcji wirtualnych w praktycznym przykadzie. Opracujemy program obliczajcy pola paskich gur:
koo, trjkt i prostokt. Zbudujemy odpowiedni hierarchi klas. W klasie bazowej umiecimy funkcj wirtualn realizujc obliczanie powierzchni
gur. Odpowiednie funkcje wirtualne, realizujce konkretne obliczenia zdeniowane bd w klasach pochodnych. Klasa bazowa ma posta:
class f i g u r a
protected :
{
double a , b ;
public :
void ustaw ( double aa = 0 . 0 , double bb = 0 . 0 )
{ a = aa ;
b = bb ;
}
v i r t u a l void p o l e ( ) { }
};
200
pojawi si komunikat:
[ C++ E r r o r ] Unit1 . cpp ( 5 0 ) : E2193 Too few p a r a m e t e r s
i n c a l l t o f i g u r a : : ustaw ( double , d o u b l e )
i program nie skompiluje si. Inna moliwa poprawna denicja dla metody
z drugim domylnym parametrem ma posta:
void ustaw ( double aa , double bb = 0 . 0 )
{ a = aa ;
b = bb ;
}
#i n c l u d e <i o s t r e a m >
using namespace s t d ;
11
13
15
17
class f i g u r a
{ protected :
double a , b ;
public :
void ustaw ( double aa = 0 . 0 , double bb = 0 . 0 )
{ a = aa ;
b = bb ;
}
v i r t u a l void p o l e ( ) { }
};
c l a s s t r o j k a t : public f i g u r a
{ public :
void p o l e ( )
{ c o u t << " p o l e t r o j k a t a = " << 0 . 5 a b << e n d l ;
}
};
19
21
23
25
27
29
31
c l a s s p r o s t a k a t : public f i g u r a
{ public :
void p o l e ( )
{ c o u t << " p o l e p r o s t o k a t a = " << a b << e n d l ;
}
};
c l a s s k o l o : public f i g u r a
{ public :
void p o l e ( )
{ c o u t << " p o l e k o l a = " << 3 . 1 4 1 5 9 2 6 a a << e n d l ;
}
};
33
35
37
i n t main ( )
{
f i g u r a wsk ;
trojkat t ;
prostakat p ;
kolo k ;
39
41
wsk = &t ;
wsk> ustaw ( 1 . 0 , 2 . 0 ) ;
wsk> p o l e ( ) ;
43
45
wsk = &p ;
wsk> ustaw ( 1 . 0 , 2 . 0 ) ;
wsk> p o l e ( ) ;
47
49
51
wsk = &k ;
wsk> ustaw ( 1 . 0 ) ;
wsk> p o l e ( ) ;
cin . get () ;
return 0 ;
}
201
202
Rozdzia 9
Klasy wirtualne i zagniedone
204
9.1. Wstp
Wan cech jzyka C++ jest moliwo stosowania wirtualnych klas
bazowych. Konieczno stosowania tego mechanizmu wynika z moliwoci
pojawienia si niejednoznacznoci gdy korzystamy w skomplikowany sposb
z wielu powizanych hierarchicznie klas bazowych. Inn cech jzyka jest
moliwo deniowania klas zagniedonych, to znaczy klas deniowanych
jedna w drugiej. W praktyce technika klas zagniedonych jest stosunkowo
rzadko stosowana.
public bazowa_2
10
12
14
16
18
20
22
c l a s s motor
{ public :
v i r t u a l void pokaz_m ( )
{ c o u t << " motor madwa c y l i n d r y " << e n d l ;
}
};
c l a s s motorower : public rower , public motor
{ public :
v i r t u a l void pokaz_mr ( )
{ c o u t << "mamy motorower " << e n d l ;
}
};
24
26
28
30
32
i n t main ( )
{ motorower mr ;
mr . pokaz_r ( ) ;
mr . pokaz_m ( ) ;
mr . pokaz_mr ( ) ;
cin . get () ;
return 0 ;
}
205
206
11
13
15
17
19
21
23
207
27
29
31
33
35
}
};
i n t main ( )
{ motorower mr ;
mr . pokaz_r ( ) ;
mr . pokaz_m ( ) ;
mr . pokaz_mr ( ) ;
mr . waga ( ) ;
cin . get () ;
return 0 ;
}
// Blad n i e j e d n o z n a c z n a nazwa !
208
Rysunek 9.2. Diagram klas dla wielokrotnego dziedziczenie z klasy bazowej ios
potrzebny do utworzenia klasy pochodnej iostream (tzw. dziedziczenie rombowe
lub diamentowe)
W typ przykadzie klasy pochodne pole podstawy oraz pole boczne musz
zadeklarowa klas bazow walec jako wirtualn.
Listing 9.3. Dziedziczenie wielokrotne; klasa wirtualna
1
using namespace s t d ;
const double PI = 2 . 0 a s i n ( 1 . 0 ) ;
11
class walec
{ protected :
double r , h ; // promien podstawy i w y s o k o s c
public :
w a l e c ( double r r =1 , double hh=1)
{ r = rr ;
h = hh ; }
void pokaz ( ) { c o u t << " r= " << r <<" h= " << h << e n d l ; }
};
13
15
17
19
21
c l a s s pole_podstawy : v i r t u a l public w a l e c
{ protected :
double pole_p ; // p o l e podstawy w a l c a
public :
pole_podstawy ( double r r , double hh ) : w a l e c ( r r , hh ) { }
void l i c z _ p o l e _ p ( )
{ pole_p = 2 . 0 PI r r ; // o b l i c z o n e p o l e 2 podstaw
}
};
23
25
27
29
31
c l a s s pole_boczne : v i r t u a l public w a l e c
{ protected :
double pole_b ; // p o l e podstawy w a l c a
public :
pole_boczne ( double r r , double hh ) : w a l e c ( r r , hh ) { }
void l i c z _ p o l e _ b ( )
{ pole_b = 2 . 0 PI r h ; // o b l i c z o n e p o l e s c i a n y b o c z n e j
}
};
33
35
37
39
41
43
45
47
49
51
53
209
210
211
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
i n t main ( )
{ motorower mr ;
mr . pokaz_r ( ) ;
mr . pokaz_m ( ) ;
mr . pokaz_mr ( ) ;
mr . motor : : waga ( ) ;
cin . get () ;
return 0 ;
}
//usuwamy n i e j e d n o z n a c z n o s c !
212
//usuwamy n i e j e d n o z n a c z n o s c !
pozwala na uniknicie wygenerowania bdu zwizanego z niejednoznacznoci nazw. W sposb jawny zostaa wybrana metoda z klasy pochodnej
motor. Podobnie moemy wybra metod z klasy rower. Naley jednak zdawa sobie spraw z faktu, e ta metoda nie jest zbyt dobra, poniewa mamy
w klasie motorower wicej ni jedn kopi metody waga(). Rekomenduje si
w zasadzie stosowanie metod wirtualnych.
11
13
15
17
19
i n t main ( )
{ motorower ( ) ;
213
21
Poniewa klasa cc zostaa zadeklarowana wewntrz funkcji, jest widoczna wycznie wewntrz funkcji motorower(). Podczas tworzenia klasy lokalnej mamy szereg ogranicze. Wszystkie metody klasy musz by zdeniowane wewntrz deklaracji klasy. Klasy lokalne nie mog take zawiera zmiennych statycznych. Klasy lokalne nie mog korzysta ze zmiennych lokalnych
funkcji. Jzyk C++ dopuszcza moliwo deniowania klasy wewntrz innej klasy. Tego typu klasa nosi nazw klasy zagniedonej (inna nazwa
klasy wewntrznej, o klasie zewntrznej mwimy, e jest klas otaczajc.
W kolejnym przykadzie demonstrujemy wykorzystanie klasy zagniedonej, tworzc program do obsugi pacjentw, danymi wejciowymi s imi i
nazwisko pacjenta oraz koszt badania i koszt lekarstw. Na kocu drukowany
jest raport kocowy podajcy cakowity koszt wizyty pacjenta. W naszym
przykadzie, omawianym poniej, klasa Koszt jest klas zagniedon, a
klas Pacjent jest klas otaczajc. Klasy zagniedone s dostpne tylko
wewntrz klasy otaczajcej.
Listing 9.6. Klasa zagniedona
1
11
13
15
17
19
21
214
23
25
27
29
31
33
35
37
39
41
43
i n t P a c j e n t : : nr = 0 ;
P a c j e n t : : Koszt P a c j e n t : : d a j _ k o s z t ( )
{ return new Koszt ( id , b a d a n i e + aptek a ) ;
}
void P a c j e n t : : Koszt : : pokaz ( )
{ c o u t << " i d : " << i d << " k o s z t : " << i l e << e n d l ;
}
i n t main ( )
{ P a c j e n t p1 ( " Kowalsk i " ) ;
p1 . wplata_b ( 1 0 0 ) . wplata_a ( 5 0 ) ;
c o u t << " p a c j e n t " << p1 . naz ( ) << " " << p1 . im ( ) <<e n d l ;
P a c j e n t : : Koszt w1 = p1 . d a j _ k o s z t ( ) ;
w1>pokaz ( ) ;
45
47
49
51
cin . get () ;
return 0 ;
53
Kowalsk i
koszt :150
Kwiatek Ewa
k o s z t : 350
Klasy zagniedone stosowane s rzadko, najczciej praktycznie klasy zagniedone stosowane s do klas wyjtkw (obsuga bdw).
W przypadku klas zagniedonych obowizuj normalne zasady kontroli
dostpu. Na zewntrz dostp moliwy jest tylko, gdy klasa zagniedona
jest publiczna. Gdy klas zagniedon zadeklarujemy jako prywatn lub
chronion, dostpna bdzie tylko w klasie zewntrznej.
215
Rozdzia 10
Wskaniki do klas
218
10.1. Wstp
Zagadnienie stosowania wskanikw w odniesieniu do obiektw klasy jest
do wane aczkolwiek nieco skomplikowane (jak to zazwyczaj jest z niebanalnym wykorzystaniem wskanikw w jzyku C++). Obecnie koncentrowa
bdziemy si na zagadnieniach dostpu do danych klasy i funkcji skadowych
klasy przy pomocy wskanikw. Oczywicie wskanikami do obiektw posugujemy si podobnie jak wskanikami do zmiennych innych typw.
#include<i o s t r e a m >
using namespace s t d ;
11
13
15
17
c l a s s punkt
{ int x ;
public :
: x(x)
punkt ( i n t x=0 )
i n t g e t ( ) { return x ; }
};
i n t main ( )
{ punkt p ( 1 0 ) , wp ;
wp = &p ;
c o u t <<" sk ladowa = " << wp>g e t ( ) << e n d l ;
cin . get () ;
return 0 ;
}
W instrukcjach:
punkt p ( 1 0 ) , wp ;
wp = &p ;
tworzymy obiekt p, inicjalizujemy skadow obiektu p wartoci 10, tworzymy zmienn wskanikow wp oraz przypisujemy jej adres obiektu p. W
kolejnej instrukcji:
219
#include<i o s t r e a m >
using namespace s t d ;
11
13
15
17
19
c l a s s punkt
{ int x ;
public :
punkt ( i n t x=0) : x ( x ) {
i n t g e t ( ) { return x ; }
};
i n t main ( )
{ punkt p [ 2 ] = { 1 , 1 3 } ;
punkt wp ;
wp = p ;
c o u t <<" sk ladowa 1 = " << wp>g e t ( ) << e n d l ;
wp++;
c o u t <<" sk ladowa 2 = " << wp>g e t ( ) << e n d l ;
cin . get () ;
return 0 ;
}
W instrukcjach:
punkt p [ 2 ] = { 1 , 1 3 } ;
punkt wp ;
wp = p ;
220
inkrementacja wskanika wp powoduj, e uzyskujemy adres kolejnego obiektu i mona wydrukowa warto zmiennej skadowej drugiego obiektu. W
pokazanym przykadzie klasa miaa tylko jedn zmienn skadow. Wobec
tego inicjalizacja tablicy obiektw bya prosta:
punkt p [ 2 ] = { 1 , 1 3 } ;
W bardziej zoonych przypadkach do inicjalizacji elementw tablicy mona zastosowa konstruktor. W pokazanym programie trzeba go wywoa
oddzielnie dla kadego elementu tablicy.
Listing 10.3. Wskaniki; dostp do elementw tablicy obiektw
1
#include<i o s t r e a m >
using namespace s t d ;
c l a s s punkt
{ int x , y ;
public :
punkt ( i n t x =0 , i n t y=0) : x ( x ) , y ( y )
i n t get_x ( ) { return x ; }
i n t get_y ( ) { return y ; }
};
11
13
15
17
19
21
23
i n t main ( )
{ punkt p [ 2 ] = { punkt ( 1 , 1 1 ) , punkt ( 3 , 3 3 ) } ;
// i n i c j a l i z a c j a t a b l i c y
punkt wp ;
wp = p ;
c o u t <<" o b i e k t 1 , x= " << wp>get_x ( ) << e n d l ;
c o u t <<" o b i e k t 1 , y= " << wp>get_y ( ) << e n d l ;
wp++;
c o u t <<" o b i e k t 2 , x= " << wp>get_x ( ) << e n d l ;
c o u t <<" o b i e k t 2 , y= " << wp>get_y ( ) << e n d l ;
cin . get () ;
return 0 ;
}
1,
1,
2,
2,
x
y
x
y
=
=
=
=
1
11
3
33
10
12
14
16
18
20
y = 3
y = 4
y = 5
221
222
11
13
15
17
19
21
23
#include<i o s t r e a m >
using namespace s t d ;
c l a s s punkt_x
{ int x ;
public :
punkt_x ( i n t x=0) : x ( x ) {
set_x ( i n t xx ) {x = xx ; }
i n t get_x ( ) { return x ; }
};
25
wsk = punkt_x ( 1 0 0 ) ; // u s t a w i e n i a p o l a k o n s t r u k t o r e m
c o u t <<" o b i e k t k l a s y bazowej , x= " << wsk>get_x ( )
<< e n d l ;
27
29
cin . get () ;
return 0 ;
31
x =10
x =100
223
nie powiedzie si. Nie mona uzyska dostpu do danych klasy pochodnej wykorzystujc bezporednio wskanik klasy bazowej. Rozwizaniem jest
wykonanie odpowiedniego rzutowania wskanika klasy. Ilustruje to nastpny
program.
Listing 10.6. Wskaniki klasowe; rzutowanie wskanika
1
11
13
15
17
#include<i o s t r e a m >
using namespace s t d ;
c l a s s punkt_x
{ int x ;
public :
void set_x ( i n t xx ) {x = xx ; }
i n t get_x ( ) { return x ; }
};
c l a s s punkt_y : public punkt_x
{ int y ;
public :
void set_y ( i n t yy ) {y = yy ; }
i n t get_y ( ) { return y ; }
};
i n t main ( )
{ punkt_x wsk ;
// w s k a z n i k k l a s y b a z o w e j
punkt_y py ;
// o b i e k t k l a s y p o c h o d n e j
wsk = &py ;
// u s t a w i e n i e w s k a z n i k a
19
wsk>set_x ( 1 0 0 ) ;
c o u t <<" o b i e k t k l a s y bazowej , x= "
<< wsk>get_x ( ) << e n d l ;
( ( punkt_y ) wsk )>set_y ( 2 0 0 ) ; // metoda k l a s y p o c h o d n e j !
c o u t <<" o b i e k t k l a s y pochodnej , y= "
<< ( ( punkt_y ) wsk )>get_y ( ) << e n d l ;
cin . get () ;
return 0 ;
21
23
25
27
x =100
y =200
Mimo oglnej reguy, mona obej ograniczenie i uzyska dostp do wszystkich skadowych klasy pochodnej. W tym celu, jak to pokazalimy, naley
224
11
c l a s s punkt
{ public :
{ x = xx ; y = yy ; }
punkt ( i n t xx , i n t yy )
i n t kwadrat ( ) { return x x + yy ; }
int x , y ;
};
i n t punkt : : wskd ;
i n t ( punkt : : wsk f ) ( ) ;
// w s k a z n i k d a n e j
// w s k a z n i k metody
13
15
17
19
21
i n t main ( )
{ punkt p1 ( 3 , 4 ) ;
// t w o r z e n i e o b i e k t u
wskd = &punkt : : x ;
// p o b r a n i e o f s e t u x
c o u t << "x= " << p1 . wskd << e n d l ;
wskd = &punkt : : y ;
// p o b r a n i e o f s e t u y
c o u t << "y= " << p1 . wskd<< e n d l ;
wsk f = &punkt : : kwadrat ;
// p o b r a n i e o f s e t u metody
c o u t << " kwadrat= " << ( p1 . wsk f ) ( ) << e n d l ;
225
cin . get () ;
return 0 ;
23
25
// w s k a z n i k d a n e j
// w s k a z n i k metody
gdzie typ zm oznacza typ skadowej w klasie nazwa klasy. Wartoci zmiennej nazwa wskaznika bdzie przesunicie (ofset) publicznej skadowej w obiekcie klasy nazwa klasy. W deklaracji obowizkowo wystpuje specykator
zakresu :: (w pokazanej denicji mamy zapis nazwa klasy :: ).
W pokazanym programie mamy w klasie punkt dwie zmienne skadowe: int x i int y. Denicja wskanika i przypisania (inicjowanie wskanikw
adresami elementw skadowych) mog mie posta:
i n t punkt : : wskd ;
wskd = &punkt : : x ;
wskd = &punkt : : y ;
// w s k a z n i k d a n e j
// p o b r a n i e o f s e t u x
// p o b r a n i e o f s e t u y
W tych przypisaniach symbol & nie oznacza tak jak w typowym wskaniku operatora pobranie bezwzgldnego adresu. W przypadku wskanikw
skadowych klas pobierane jest wzgldne przesuniecie skadowej x lub y
wzgldem pocztkowego obiektu klasy punkt. Nie mona wskanikw skadowych klas inkrementowa ani wykonywa innych operacji arytmetycznych
( operacja wskd++ jest nieprawomocna).
Zadeklarowany wskanik nie ma wikszego znaczenie jeeli nie zostanie
utworzony konkretny obiekt klasy. W naszym przykadzie tworzymy obiekt o
nazwie p1, przy pomocy konstruktora polom x i y przypisujemy odpowiednio
wartoci 3 i 4.
226
// t w o r z e n i e o b i e k t u
Troch bardziej skomplikowana jest deklaracja wskanikw skadowych klasy dla metod (funkcji skadowych klasy). Przypominamy, e metody nie s
zawarte zycznie w obiektach istnieje tylko jedna wersja metody. Podczas
wywoywania metody, jest do niej przekazywany wskanik do wywoujcego
obiektu (tzn. do obiektu, ktry wywouje konkretn funkcj), jest to wskanik this. Dla funkcji skadowej klasy mamy nastpujc posta deklaracji
wskanika:
typ_m
gdzie typ m jest typem zwracanym przez metod klasy nazwa klasy, nazwa wskaznika jest wskanikiem do funkcji skadowej klasy, argumenty oznaczaj list parametrw przekazywanych do funkcji. W deklaracji obowizkowo wystpuje specykator zakresu :: , nawiasy s konieczne do poprawnego
przyporzdkowanie operatora ::* . W pokazanym programie mamy w klasie
punkt metod o nazwie kwadrat(). Denicja wskanika i przypisania (inicjowanie wskanika metody) mog mie posta:
i n t ( punkt : : wsk f ) ( ) ;
punkt p1 ( 3 , 4 ) ;
wsk f = &punkt : : kwadrat ;
// w s k a z n i k metody
// t w o r z e n i e o b i e k t u
// p o b r a n i e o f s e t u metody
c l a s s punkt
{ public :
227
11
13
15
17
19
};
i n t main ( )
{ i n t punkt : : wskx ;
i n t punkt : : wsky ;
i n t ( punkt : : wsk f ) ( )
wskx = &punkt : : x ;
wsky = &punkt : : y ;
wsk f = &punkt : : kwadrat ;
punkt p1 ( 3 , 4 ) ;
punkt w1 = &p1 ;
//
//
; //
//
//
//
//
//
wskaznik danej x
wskaznik danej y
w s k a z n i k metody
pobranie of setu x
pobranie of setu y
pobranie o f e s t u kwadrat ()
tworzenie obiektu , i n i c j a c j a
w s k a z n i k do o b i e k t u , i n i c j a c j a
21
23
25
cin . get () ;
return 0 ;
27
Uytecznoci wskanikw do skadowych klasy nie jest zbyt dua. Argumentuje si, e czasami warto tej techniki uy do konstruowania rnego
rodzaju opcji wyboru (np. menu). W kolejnym przykadzie demonstrujemy
program w ktrym wykorzystano tablice wskanikw skadowych klasy.
228
11
13
15
17
19
21
23
25
27
29
31
33
= 1
dx = 1
=1
dy = 1
stare = 5
+ dx = 2
+ dy = 3
nowe = 13
Pokazany wynik dziaania programu jest poprawny i wida, e wykorzystanie wskanikw do skadowych klas, nie powinno sprawia kopotw.
Rozdzia 11
Techniki obsugi bdw
11.1.
11.2.
11.3.
11.4.
11.5.
Wstp . . . . . . . . . . . . . . . . .
Funkcje walidacyjne . . . . . . . . .
Graniczne wartoci plik limits.h .
Narzdzie assert() i funkcja abort()
Przechwytywanie wyjtkw . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
230
230
234
237
239
230
11.1. Wstp
W zasadzie jest bardzo trudno unikn bdw w kodzie programu.
Nie istniej ugruntowane zasady jak pisa bezbdne programy, mamy w
podrcznikach lepsze lub gorsze zalecenia jak unika zoliwych bdw. Z
drugiej strony dymy do pisania bezbdnych kodw jzyk C++ i jego
biblioteki wspieraj obsug bdw. Wszystkie bdy moemy podzieli na
dwie kategorie:
bdy czasu kompilacji
bdy czasu wykonania
Przyjmuje si, e mniej grone s bdy powstajce w czasie kompilacji
wikszo kompilatorw wyposaona jest w doskonae (i bardzo rozbudowane) techniki diagnostyczne, tak, e przy pierwszym wykryciu bdu
kompilator przerwie proces kompilacji i wywietli odpowiedni komunikat o
bdzie. Oczywicie w wielu przypadkach komunikat taki bdzie mao czytelny, czasem bez sensu i nie zawsze wskazany wiersz kodu zawiera bd. Pocieszajcym jest fakt, e bd kompilacji zawsze si ujawni. Po stosunkowo
krtkim czasie, programista nabywa dowiadczenia i usuwanie bdw czasu kompilacji nie jest zbyt skomplikowane. Istniej popularne techniki (np.
wyczanie linijki kodu w ktrym kompilator sygnalizuje bd przy pomocy
znakw komentarza, czy te umieszczanie w programie testowych wydrukw czciowych wynikw) pomagajcy usun takie bdy. Wiele platform
programistycznych wyposaonych jest zestaw narzdzi diagnostycznych s
to tzw. debugery.
Bdy czasu wykonania s w wielu przypadkach trudne do zidentykowania i usunicia. Przyczyn tego jest fakt, e takie bdy nie generuj
komunikatw bdu oraz e mog pojawia si bardzo rzadko. Klasyczne
przyczyny wystpienia bdw to prba otworzenia nieistniejcego pliku,
danie wikszej iloci pamici ni moemy otrzyma, prba dzielenia przez
zero, napotkanie zej wartoci danych wejciowych, itp. W programach moemy stosowa rne techniki zapobiegajcy wystpieniom moliwych bdw, klasyczn technik jest stosowanie funkcji walidacyjnych, sprawdzanie poprawnoci danych wejciowych czy wykorzystanie wyspecjalizowanych
funkcji takich jak assert(). Odrbn technik obsugi bdw w jzyku C++
(podobnie jak w jzyku Java czy Pyton) jest przechwytywanie wyjtkw
(ang. exception).
11
13
15
17
19
231
232
10
12
14
16
18
20
22
24
i n t main ( )
{ using namespace s t d ;
float x ;
c o u t << " Podaj x : " ;
c i n >> x ;
i f ( w a l i d ( x ) ) c o u t << " p i e r w i a s t e k z "
<< x << " = " << s q r t ( x )<< e n d l ;
else
c o u t << " argument ujemny " << e n d l ;
getche () ;
return 0 ;
}
i n t w a l i d ( f l o a t xx )
{ i f ( xx >0) return 1 ;
else
return 0 ;
}
10
12
14
16
18
20
22
24
26
233
234
28
30
(11.1)
Przyjmuje si, e 0! =1 oraz 1! = 1. Silnia jest bardzo szybko rosnc funkcj, ju dla niewielkich wartoci n moe dawa bardzo du warto. W tej
sytuacji obliczenia silni bdziemy przeprowadza na typie unsigned long.
Specykacja jzyka C++ wymaga, aby zmienna typu unsigned long bya
przechowywana przynajmniej na 32 bitach (4 bajty), co daje zakres od 0 do
4294967295. Przekroczenie zakresu nie jest sygnalizowane w aden sposb,
programista musi sam zadba aby nie wyj poza zakres w czasie oblicze.
Tworzymy w programie funkcj:
ktra przyjmie warto true gdy n jest w zakresie oraz false gdy n jest zbyt
du liczb. Staa ULONG MAX (plik nagwkowy limits.h) jest rwna
maksymalnej liczbie cakowitej typu unsigned long jaki moe by uyty w
danym kompilatorze i systemie operacyjnym. Dostpne stae to:
Nazwa staej
UCHAR MAX
USHRT MAX
UINT MAX
ULONG MAX
Typ zmiennej
unsigned char
unsigned short
unsigned integer
unsigned long
using namespace s t d ;
6
10
12
14
16
18
20
bool w _ z a k r e s i e ( i n t ) ;
unsigned long s i l n i a ( i n t ) ;
i n t main ( )
{ int i ;
c o u t << " Podaj l i c z b e : " ;
c i n >> i ;
i f ( w_zakresie ( i ) )
c o u t << " S i l n i a z l i c z b y " << i << " = "
<< s i l n i a ( i ) << e n d l ;
else
c o u t << " Poza zak resem " << e n d l ;
getche () ;
return 0 ;
}
22
24
unsigned long s i l n i a ( i n t n )
{ i f ( n <= 1 )
235
236
26
28
30
bool w _ z a k r e s i e ( i n t nn )
{ unsigned long max = ULONG_MAX;
f o r ( i n t i = nn ; i >=1; i )
max /= i ;
i f (max < 1 )
return f a l s e ;
else
return true ;
}
32
34
36
38
36288800
Podaj l i c z b e : 12
S i l n i a z l i c z b y 12 =
479001600
Podaj l i c z b e : 13
Poza zak resem
i n t main ( )
{
using namespace s t d ;
int x ;
c o u t << " Podaj d o d a t n i a l i c z b e c a l k o w i t a : " ;
while ( ! ( c i n >> x ) | | x < 0 )
{
237
13
15
17
19
cin . clear () ;
c i n . i g n o r e ( 1 0 0 , \n ) ;
c o u t << " Podaj d o d a t n i a l i c z b e c a l k o w i t a : " ;
}
c i n . i g n o r e ( 1 0 0 , \n ) ;
c o u t << " P i e r w i a s t e k z " << x << " = " << s q r t ( x ) ;
cin . get () ;
return 0 ;
}
calkowita
calkowita
calkowita
calkowita
:
:
:
:
4
k
$
9
Dopki nie zostanie wprowadzona poprawnie dodatnia liczba cakowita program nie przystpi do obliczania pierwiastka kwadratowego (wprowadzenie
znaku powoduje bd poniewa cin oczekuje liczby cakowitej typu int, jeli
cin ma warto false, oznacza to, e wystpi bd)
while ( ! ( c i n >> x ) | | x < 0 )
Aby przywrci cin do stanu pocztkowego, tak aby ten obiekt mg ponownie przyj dane wejciowe moemy uy funkcji clear(), ktra wymazuje
znacznik bdu (ale nie czyci bufora).
cin . clear () ;
W powyszym przykadzie bdzie odrzuconych 100 kolejno wczytanych znakw, chyba, e wczeniej bdzie napotkany znak nowego wiersza \n.
238
#include
#include
#include
#include
<i o s t r e a m >
<math>
<c o n i o . h>
<a s s e r t . h>
11
13
15
17
19
i n t main ( )
{
using namespace s t d ;
int x , y ;
f l o a t m;
c o u t << " Podaj x : " ;
c i n >> x ;
c o u t << " Podaj y : " ;
c i n >> y ;
m = xx yy ;
a s s e r t (m >= 0 ) ;
c o u t << " P i e r w i a s t e k z " << m << " = " << s q r t (m) ;
getche () ;
return 0 ;
}
Jeeli w programie umiecimy asercje to program z tymi danym nie uruchomi si, nastpi przerwanie programu i powrt do rodowiska programistycznego.
Obsug bdu bardzo podobn do wywoania makra assert() jest wywoanie funkcji abort(). Zmodykujemy poprzedni program, rezygnujc z
assert() na rzecz zastosowania funkcji abort().
10
12
14
16
18
20
22
Zaleca si stosowanie makra assert(), poniewa w wyniku jego dziaania otrzymujemy wicej informacji na temat bdu (np. numer wiersza, w
ktrym bd wystpi).
239
240
10
12
14
16
18
20
22
Naley pamita, e demonstrowane tu programy z przykadami obsugi wyjtkw s bardzo proste i normalnie nikt nie zastosowa by takich
mechanizmw obsugi bdw. Przy spenionych warunkach bezpieczestwa
dziaanie programu moe mie posta:
Liczba kamizelek 5
Liczba kajakarzy 5
Zaczynamy sply w kajakowy
// b l o k t r y
c o u t << " L i c z b a k a m i z e l e k : " ;
c i n >> kam ;
c o u t << " L i c z b a k a j a k a r z y : " ;
c i n >> k a j ;
i f ( k aj >kam) throw ( k a j kam) ; // i n s t r u k c j a throw
c o u t << "Zaczynamy sply w kajakowy " ;
Parametr t typu int w sowie kluczowym catch nosi nazw parametru bloku
catch. Parametr bloku catch ma okrelony typ, dziki czemu wiemy, jaki
zosta wyrzucony wyjtek (mona wyrzuca rne wyjtki). Po drugie, ma
on nazw, dziki czemu w bloku catch moemy na tej wartoci wykona
jakie operacje, co moe na przykad prowadzi do usunicia bdu.
241
242
10
12
14
16
18
20
22
24
26
10
12
14
16
18
20
22
24
Blok try moe wyrzuca wicej ni jeden wyjtkw, wobec tego mamy
moliwo umieszczenia wielu blokw catch, naley jednak pamita, e kady taki blok catch musi przechwytywa wyjtek innego typu.
Instrukcje catch sprawdzane s w takiej kolejnoci w jakiej umieszczone
s w programie. Gdy wykonywany jest blok try moe by wyrzucony tylko
jeden wyjtek, ale tych wyjtkw moe by wiele (za kadym razem moe to
by wyjtek innego typu). Blok catch wychwytuje tylko jeden wyjtek okrelonego typu. Bloki catch umieszczone s jeden pod drugim, tak aby bya
moliwo dopasowania danego typu. Jeeli wyrzucony zostanie wyjtek,
ktrego typ nie jest obsugiwany przez aden blok catch mamy problem.
Program jest zatrzymywany awaryjnie. Naley unika takich sytuacji.
Gdy w programie przechwytujemy wiele wyjtkw, jest istotna kolejno
w jakiej umieszczamy bloki catch. Sprawdzanie s kolejne bloki catch, gdy
program natra na odpowiadajcy typ, ktry pasuje do typu wyrzuconego wyjtku, wykonywany jest ten blok. Widzimy, e dziaanie programu
jest zalene od kolejnoci wyrzucanych wyjtkw i kolejnoci poszczeglnych blokw catch. W naszym przykadzie instrukcje catch przechwytuj
243
244
11
13
15
17
19
21
23
25
using namespace s t d ;
i n t main ( )
{ int i ;
char zn ;
c o u t << " Podaj l i c z b e nieujemna : " ;
c i n >> i ;
c o u t << " Podaj znak , q k onczy program : " ;
c i n >> zn ;
try
{ i f ( i < 0 ) throw ( i ) ;
else
c o u t << "Wprowadzona l i c z b a : " << i << e n d l ;
i f ( ( zn == q ) | | ( zn == Q ) ) throw " Koniec " ;
else
c o u t << "Wprowadzony znak : " << zn << e n d l ;
}
catch ( i n t t )
{ c o u t << " l i c z b a " << t <<" j e s t ujemna " ;
}
catch ( char n a p i s )
{ c o u t << n a p i s << " programu " ;
}
getche () ;
return 0 ;
27
29
Analizujc wyniki widzimy, e w zalenoci od wprowadzonych danych mamy rne reakcje programu (otrzymujemy inne wyniki). W jzyku C++
mamy bardzo wygodn konstrukcj do przechwytywania wyjtkw. Jest to
blok catch, ktry przechwytuje wszystkie wyjtki. Dzieje si tak dlatego, e
nie ma podanego typu wyjtkw. Tego typu blok catch ma posta:
catch ( . . . ) {
// i n s t r u k c j e
}
Nawias, ktry posiada wewntrz trzy kropki, oznacza w takim bloku dowolny typ wyjtku. W nastpnym programie demonstrujemy dziaanie uniwersalnego bloku catch.
Listing 11.12. Proste obsugiwanie wyjtkw; uniwersalny blok catch
1
11
13
15
17
19
21
245
246
Analizujc ten program, widzimy, e pojedynczy blok catch obsuguje wszystkie wyjtki. Gdy to moliwe zaleca si stosowanie tego uniwersalnego bloku
catch. Ma to jeszcze inn istotn zalet dziki przechwyceniu wszystkich
wyjtkw nie mamy awaryjnego zatrzymania programu, gdy wystpi nieobsugiwany bd. Jak ju pokazalimy, funkcja moe wyrzuca wyjtki. W
wielu przypadkach naley ograniczy typy wyjtkw jakie moe wyrzuca
funkcja, mona nawet zakaza funkcji wyrzucania jakichkolwiek wyjtkw.
Aby wprowadzi ten mechanizm musimy zmodykowa prototyp (oraz denicj ) naszej funkcji:
zwracany typ nazwa funkcji (lista argumentw) throw (lista typw)
W takiej sytuacji funkcja moe wyrzuci tylko takie wyjtki, ktrych typy
znajduj si na licie typw. Jeeli na licie typw nie ma adnego typu (lista
jest pusta) to funkcja nie moe zwrci adnego wyjtku. Naley pamita,
e gdy funkcja bdzie prbowaa wyrzuci wyjtek nieobsugiwanego typu
natychmiast zostanie wywoana funkcja biblioteczne unexpected(), ktra
wywoa funkcje abort(), ktra koczy wykonywanie programu.
Listing 11.13. Ograniczenia typw wyjtkw zgaszanych przez funkcj
2
247
10
12
14
16
18
20
22
24
26
28
(11.2)
248
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
41
Metoda info() klasy zy wynik wywoana na rzecz obiektu er dostarczy moe detalicznych informacji o wystpujcym wyjtku.Pokazany poniej program jest inn modykacj programu wyliczajcego redni harmoniczn.
Listing 11.15. Wykorzystanie klasy wyjtkw
1
249
250
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
Kolejna modykacja pokazuje wykorzystanie klasy wyjtkw, ktre wyrzucane s przez funkcj usugow.
Listing 11.16. Klasy wyjtkw; funkcja ze specykacj wyjtkw
1
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
41
zly_wynik ( i n t xx=0 , i n t yy = 0 )
: x ( xx ) , y ( yy ) {}
void i n f o ( ) ;
};
void zly_wynik : : i n f o ( )
{ c o u t << " x= " << x << " " << " y = "
<< y << e n d l ;
c o u t << " Z l e dane poniewaz x=y "
<< e n d l ;
}
double sr_harmon ( double , double ) throw ( zly_wynik ) ;
i n t main ( )
{ double a , b , sh ;
c o u t << " Podaj dwie l i c z b y : " ;
while ( c i n >> a >>b )
{ try
{
sh = sr_harmon ( a , b ) ;
c o u t << " S r e d n i a = " << sh << e n d l ;
c o u t << " Podaj k o l e j n a p a r e l i c z b ,
w aby s k o n c z y c : " ;
}
catch ( zly_wynik & e r )
{ er . i n f o () ;
c o u t << " Podaj k o l e j n e l i c z b y . \ n" ;
}
}
getche () ;
return 0 ;
}
double sr_harmon ( double x , double y ) throw ( zly_wynik )
{ i f ( x == y )
throw zly_wynik ( x , y ) ;
return 2 . 0 x y / ( x+y ) ;
}
251
252
W tym bloku wywoana jest funkcja info(), ktra jest metod klasy wyjtkw zy wynik. Poniewa preferowanym sposobem reprezentacji klasy wyjtkw jest uycie klasy wyjtkw omwimy kolejn prost aplikacj do
wykrycia bdu dzielenia przez zero.
Listing 11.17. Klasy wyjtkw; prosty przykad
2
253
10
12
14
16
c l a s s MianownikZero
{ int x , y ;
public :
MianownikZero ( ) : x ( 0 ) , y ( 0 ) { }
MianownikZero ( i n t a , i n t b ) : x ( a ) , y ( b ) { }
void komunikat ( )
{ c o u t << " proba d z i e l e n i a p r z e z z e r o " << e n d l ;
}
void pokaz ( )
{ c o u t << " d z i e l n a = " << x << " d z i e l n i k = "
<< y << e n d l ;
}
};
18
20
22
24
26
28
30
32
34
36
38
40
42
getche () ;
return 0 ;
}
: 3 6
q k onczy : 1 3
q k onczy : 1 0
q k onczy : q
254
Funkcja najpierw sprawdza, czy mianownik jest rwny zero. Gdy jest to
prawda wyrzuca wyjtek typu MianownikZero, gdy mianownik jest rny
od zera wykonuje dzielenie i wynik jest zwracany do programu. W funkcji
main() mamy sekcj try catch:
try
{ wynik = d z i e l e n i e ( l i c z n i k , mianownik ) ;
c o u t << " wynik = " << wynik << e n d l ;
}
catch ( MianownikZero &e r )
{ e r . komunikat ( ) ;
}
11
13
15
17
19
21
23
25
27
29
31
i n t main ( )
{
i n t l i c z n i k , mianownik ;
double wynik ;
c o u t << " podaj dwie l i c z b y c a l k o w i t e : " ;
while ( c i n >> l i c z n i k >> mianownik )
{ try
{ wynik = d z i e l e n i e ( l i c z n i k , mianownik ) ;
c o u t << " wynik = " << wynik << e n d l ;
}
catch ( MianownikZero e r )
{ c o u t << e r . kom ( ) << e n d l ;
}
c o u t << " podaj dwie l i c z b y c a l k o w i t e , q k onczy : " ;
}
33
getche () ;
return 0 ;
35
255
256
257
//LONG_MAX
11
13
15
17
class Info
{
s t a t i c char t e k s t [ ] ;
public :
s t a t i c char kom( i n t n )
{ return t e k s t [ n ] ;
}
} ;
char I n f o : : t e k s t [ ] = { " mianownik rowny z e r o \n " ,
" mianownik ujemny " ,
" \n podaj l i c z n i k i nieujemny mianownik : " ,
" \n ( podaj q aby z a k o n c z y c ) " ,
" w a r t o s c ulamka : "
} ;
19
21
23
25
c l a s s MianownikZero
{ protected :
char kom ;
public :
MianownikZero ( char wiad ) : kom( wiad )
void pokaz ( ) { c o u t << kom ; }
};
27
29
31
33
35
37
39
41
43
45
47
49
258
53
55
57
59
61
63
65
67
69
i n t main ( )
{ while ( true )
{ long l i c z n i k , mianownik ;
double u l ;
c o u t << I n f o : : kom ( 3 ) << I n f o : : kom ( 2 ) ;
i f ( ( c i n >> l i c z n i k >> mianownik ) == 0 )
try
{ ulamek ( l i c z n i k , mianownik , u l ) ;
c o u t << I n f o : : kom ( 4 ) << u l << e n d l ;
}
catch ( MianownikUjemny & neg )
{ neg . pokaz ( ) ;
}
catch ( MianownikZero & z e r )
{ z e r . pokaz ( ) ;
}
}
getche () ;
return 0 ;
}
break ;
259
260
Gdy podczas wykonywania funkcji ulamek() wystpi bd, wyjtek przechwytywany jest przez kolejne bloki catch.
Obecnie, wyjtki s w zasadzie czci jzyka. Plik nagwkowy exception deniuje klas exception, ktra jest klas bazow dla innych klas wyjtkw. Klasa ta posiada funkcj skadow what(), ktra wykonuje operacje na
obiektach tej klasy. Wirtualna funkcja skadowa what() zwraca cig znakw,
zaleny od implementacji, ten cig znakw opisuje wyjtek. Ponisza tabela
261
exception <
bad alloc
bad cast
bad typeid
logic error
ios base::failure
runtaim error
bad exception
Klas pochodna logic error jest klas nadrzdn dla kolejnych klas pochodnych. Kolejna tabela pokazuje standardowe klasy wyjtkw dziedziczcych z tej klasy.
domain error
invalid argument
length error
out of range
Podobnie klasa runtime error jest klas nadrzdn dla innych klas pochodnych, hierarchia pokazana jest w kolejnej tabeli.
range error
overow error
underow error
Klasy wyjtkw zdeniowane s w rnych plikach nagwkowych. Informacje o najczciej uywanych klasach pokazuje kolejna tabela.
262
klasa
bad alloc
plik
<new>
bad cast
<typeinfo>
bad typeid
<typeinfo>
bad exception
<exception>
ios::failure
<ios>
Opis
Wyjtek zgaszany przez operator new gdy nie uda si
przydzia pamici
Wyjtek zgaszany przez operator dynamic cast gdy nie
udaa si konwersja
Wyjtek zgaszany przez operator typeid, gdy wskanik bdcy jego argumentem jest
pusty
Wyjtek zgaszany gdy pojawi si nieznany wyjtek
Wyjtek zgaszany, gdy zmieni si stan strumienia w niepodany sposb
Metoda what() zwraca komunikat, zaleny od wyjtku. W naszym przypadku jest to komunikat:
bad a l l o c e x c e p t i o n thrown
using namespace s t d ;
i n t main ( )
{double w [ 1 0 0 ] ;
long l i c z n i k = 0 ;
try
{ f o r ( i n t i =0; i <100; i ++)
{w [ i ] = new double [ 5 0 0 0 0 0 0 ] ;
++l i c z n i k ;
}
}
catch ( b a d _ a l l o c w y j a t e k )
{ c o u t << " o b s l u g a wyjatku : " << w y j a t e k . what ( ) << e n d l ;
}
11
13
15
17
19
21
23
25
27
a l l o c e x c e p t i o n thrown
48 blokow
bajtow
1920000000 bajtow
Wynik wykonania pokazanego programu oczywicie bdzie rny na rnych systemach. Zalee bdzie od iloci bajtw uytych do kodowania typu
double i iloci dostpnej pamici (zycznej pamici i przestrzeni dyskowej
dostpnej dla pamici wirtualnej).
263
Rozdzia 12
Szablony w C++
12.1.
12.2.
12.3.
12.4.
Wstp . . . . . . . .
Przecianie funkcji
Szablony funkcji . .
Szablony klas . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
266
266
276
292
266
12.1. Wstp
W jzyku C++ istnieje cisa kontrola zgodnoci typw. Czasami taka
cisa kontrola typw (ktra generalnie jest zalet) powoduje znaczne komplikacje przy tworzeniu wydajnego oprogramowania. Gdy na przykad opracujemy funkcj pobierajc argumenty typu int, to taka funkcja nie bdzie
poprawnie obsugiwaa argumentw typu double. Dla obsugi argumentw
innego typu musimy opracowa inn funkcj. Jzyk C++ oby omin to
ograniczenie cile zwizane z kontrol typw wprowadza szablony, czasami
zwane w literaturze wzorcami (ang. template). Przy pomocy kodu jednej
funkcji lub jednej klasy moemy obsugiwa dane rnych typw. Moemy
opracowa pojedynczy szablon (wzorzec) funkcji sortujcej tablice z danymi
typu int, double, char czy nawet napisw. Podobnie moemy pisa uniwersalne szablony klas.
Mechanizmy wzorcw zostay szczegowo opisane i omwione w pracy Bjarnea Stroustrupa w 1998 roku w pracy pod tytuem Parametrized
Types for C++.
Szablony, prawdopodobnie ze wzgldu na szerokie rozpowszechnienie si
zastosowa biblioteki szablonw STL (ang. Standard Template Library)
uwaane s za jedn z najwaniejszych waciwoci jzyka C++. Dziki
szablonom mamy moliwoci programowania oglnego (ang. generic programming). Tworzc szablon funkcji (podobnie jak szablon klasy) tworzymy
kod dziaajcy na nieopisanych jeszcze typach funkcja dziaa na typach
oglnych (ktre nie istniej w jzyku C++), w momencie wywoania funkcji pod te typy oglne podstawiamy konkretne typy, jak np. int czy char.
Przekazujemy szablonowi typy jako parametry, kompilator generuje funkcje
konkretnego typu.
pasuje do prototypu int x oraz do prototypu &x. W takiej sytuacji kompilator raczej nie jest w stanie zadecydowa jakiej wersji funkcji kwadrat() ma
uy. Kompilator traktuje referencje do danego typu i sam typ jako rwnowane. Podobnie, z punktu widzenia kompilatora nastpujce deklaracje s
rwnowane:
i n t suma ( i n t x ) ;
i n t suma ( i n t x [ ] ) ;
poniewa maj takie same sygnatury. Przeciy moemy nastpujcy zestaw funkcji:
i n t suma ( i n t a , double b )
double suma ( double a , double b )
Zagadnienie przeciania funkcji zilustrujemy klasycznym przykadem dydaktycznym jakim jest funkcja obliczajca kwadrat liczby.
Listing 12.1. Przykad przecienia funkcji
2
i n t kwadrat ( i n t ) ;
double kwadrat ( double ) ;
i n t kwadrat ( int , i n t ) ;
using namespace s t d ;
10
12
i n t main ( )
{ c o u t << kwadrat ( 3 )
<< e n d l ;
c o u t << kwadrat ( 3 . 3 ) << e n d l ;
c o u t << kwadrat ( 3 , 4 ) << e n d l ;
267
268
16
18
20
22
24
26
getche () ;
return 0 ;
}
i n t kwadrat ( i n t x )
{ c o u t << x <<" do kwadratu= " ;
return xx ;
}
double kwadrat ( double x )
{ c o u t << x <<" do kwadratu= " ;
return xx ;
}
i n t kwadrat ( i n t a , i n t b )
{ c o u t << " a a +bb= " ;
return a a + bb ;
}
Trzy funkcje o nazwie kwadrat() s rozrnialne bez kopotu przez kompilator ze wzgldu na typ parametrw (kwadrat(int) oraz kwadrat (double) )
oraz ze wzgldu na liczb argumentw (kwadrat (int) i kwadrat (int, int)).
W kolejnym przykadzie pokazano rozbudowany zestaw uytecznych funkcji
przecionych do wyznaczania najmniejszej liczby ze zbioru. Kompilator bez
trudu radzi sobie z identykacj funkcji.
Listing 12.2. Przykad przecienia funkcji
1
11
13
15
17
19
23
25
27
29
31
33
return 0 ;
}
i n t min ( i n t x , i n t y )
{ return x<y ? x : y ; }
i n t min ( i n t x , i n t y , i n t z )
{ i f ( x<y ) return x<z ? x : z ;
e l s e return y<z ? y : z ; }
i n t min ( i n t x , i n t y )
{ return x<y ? x : y ; }
long min ( long x , long y )
{ return x<y ? x : y ; }
double min ( double x , double y )
{ return x<y ? x : y ; }
=
=
=
=
=
=
7
3
11
8
14
22
musimy dokona konwersji jawnej, gdy kompilator nie jest w stanie w stanie
rozrni typw. Gdyby wywoanie miao posta:
c o u t << "min= " << min ( a , h ) << e n d l ;
W denicjach:
i n t min ( int , i n t ) ;
i n t min ( i n t , i n t ) ;
269
270
10
12
14
i n t kwadrat ( i n t ) ;
i n t main ( )
{ i n t ( w1 ) ( i n t ) ; // d e k l a r a c j a w s k a z n i k a
// do f u n k c j i t y p u i n t
w1 = &kwadrat ;
// p r z y p i s a n i e w s k a z n i k o w i a d r e s u f u n k c j i
c o u t << ( w1 ) ( 5 ) ; // w y w o l a n i e f u n k c j i z argumentem 5
getche () ;
// i w y s w i e t l e n i e kwadratu argumentu
return 0 ;
}
i n t kwadrat ( i n t x )
{ return xx ; }
11
13
w1 ( 1 0 , ) ;
w2 ( 1 0 , | , . ) ;
w1 ( 1 0 , \xCD ) ;
getche () ;
return 0 ;
15
17
19
21
271
25
27
29
c o u t << e n d l ;
}
void ramka ( i n t n , char z1 , char z2 )
{ c o u t << z1 ;
f o r ( i n t i =1; i <n ; i ++) c o u t << z2 ;
c o u t << z1 << e n d l ; ;
}
// w s k a z n i k do f u n k c j i
// w s k a z n i k do f u n k c j i
a w instrukcjach:
w1 ( 1 0 , ) ;
w2 ( 1 0 , | , . ) ;
w1 ( 1 0 , \xCD ) ;
// dopasowani e ( i n t , c h a r )
// dopasowani e ( i n t , char , c h a r )
Zadeklarowane metody moemy take przecia, pod znanym warunkiem, e inna jest liczba argumentw lub inne s ich typy. Na przykad w
klasie Osoba mamy dwie przecione metody o nazwie set().
Listing 12.5. Przykad przeciania metod
2
c l a s s Osoba
{
s t r i n g nazwisk o ;
i n t rok ;
public :
272
10
12
14
16
18
20
22
24
void d r u k u j ( ) ;
void s e t ( s t r i n g nn ) { nazwisk o = nn ; } // p r z e c i a z o n a
// metoda
void s e t ( i n t r r ) { rok = r r ; }
// p r z e c i a z o n a metoda
};
void Osoba : : d r u k u j ( )
{ c o u t << nazwisk o << " " << rok ;
}
i n t main ( )
{ Osoba o s ;
o s . s e t ( " Kowalsk i " ) ;
os . s e t (1999) ;
os . drukuj ( ) ;
getche () ;
return 0 ;
}
11
13
15
17
19
21
c l a s s Data
i n t m i e s i a c , d z i e n , rok ;
{
public :
Data ( i n t = 8 , i n t = 7 , i n t = 2 0 0 6 ) ; // k o n s t r u k t o r
Data ( long ) ;
// k o n s t r u k t o r
void drukuj_Data ( ) ;
};
Data : : Data ( i n t mm, i n t dd , i n t r r )
{ m i e s i a c = mm;
d z i e n = dd ;
rok = r r ;
}
Data : : Data ( long k_data )
{ rok = i n t ( k_data / 1 0 0 0 0 . 0 ) ;
m i e s i a c = i n t ( ( k_data rok 1 0 0 0 0 . 0 ) / 1 0 0 . 0 ) ;
d z i e n = i n t ( k_data rok 1 0 0 0 0 . 0 m i e s i a c 1 0 0 . 0 ) ;
}
25
27
29
31
33
35
37
39
// k o n s t r u k t o r
Reprezentowanie daty w tej postaci posiada kilka zalet: po pierwsze umoliwia reprezentowanie daty w postaci liczby cakowitej, dane tego typu maj
wzrastajcy porzdek, po drugie sortowanie dat staje si ekstremalnie atwe
i szybkie. Konstruktor konwertujcy ma posta:
Data : : Data ( long k_data )
{ rok = i n t ( k_data / 1 0 0 0 0 . 0 ) ;
m i e s i a c = i n t ( ( k_data rok 1 0 0 0 0 . 0 ) / 1 0 0 . 0 ) ;
d z i e n = i n t ( k_data rok 1 0 0 0 0 . 0 m i e s i a c 1 0 0 . 0 ) ;
}
273
274
11
13
15
17
19
21
23
25
27
29
31
33
c l a s s Data
{
i n t m i e s i a c , d z i e n , rok ;
public :
Data ( i n t = 8 , i n t = 7 , i n t = 2 0 0 6 ) ; // k o n s t r u k t o r
operator long ( ) ;
// f u n k c j a o p e r a t o r o w a
void drukuj_Data ( ) ;
};
Data : : Data ( i n t mm, i n t dd , i n t r r )
{ m i e s i a c = mm;
d z i e n = dd ;
rok = r r ;
}
Data : : operator long ( )
{ long rmd ;
rmd = rok 1 0 0 0 0 . 0 + m i e s i a c 1 0 0 . 0 + d z i e n ;
return rmd ;
}
void Data : : drukuj_Data ( )
{ c o u t << s e t f i l l ( 0 )
<< setw ( 2 ) << m i e s i a c << /
<< setw ( 2 ) << d z i e n << /
<< setw ( 2 ) << rok %100 << e n d l ;
}
i n t main ( )
{ Data a ( 1 , 5 , 1 9 4 7 ) ; // d e k l a r a c j a o b i e k t u t y p u Data
long b = a ;
// d e k l a r a c j a o b i e k t u t y p u l o n g
c o u t << " data a : " ;
a . drukuj_Data ( ) ;
c o u t << " t a data j a k o l o n g : " << b ;
35
getche () ;
return 0 ;
37
275
// f u n k c j a o p e r a t o r o w a
W kolejnym przykadzie pokaemy uyteczne przeciania konstruktora suce take do obsugi daty. Bardzo czsto zdarza si, e data podawana jest
w dwch formatach jako dane typu int albo dane s typu napisowego.
Przecienie konstruktora obsuy te dwa sposoby pisania dat. W programie
wykorzystamy specyczn funkcje wejcia jak jest funkcja sscanf().
Funkcja sscanf() ( wymagany jest plik <cstdio> ), ktrej prototyp ma
posta:
i n t s s c a n f ( ) ( const char buf , const char format ,
...) ;
jeeli wpiszemy dane wejciowe w postaci 21/10, wtedy 21 zostanie przypisane zmiennej a, / zostanie zignorowane, a 10 bdzie przypisane zmiennej
b.
Listing 12.8. Przecianie konstruktorw (obsuga typu int i napis)
1
11
276
15
17
19
21
23
25
27
29
31
Widzimy, e chocia obiekty typu Data byy inicjowane przy uyciu trzech
wartoci typu int :
Data d1 ( 1 1 , 2 1 , 2 0 0 8 ) ;
(obiekt d2), to wywietlanie daty zostao wykonane poprawnie. Dziki przecianiu konstruktorw, uytkownik moe decydowa w jaki sposb bdzie
wprowadza daty do programu. Takie podejcie zmniejsza ryzyko popenienia bdu w danych wejciowych.
11
13
15
i n t main ( )
{ c o u t << " wynik i n t : " << kwadrat ( 2 )
<< e n d l ;
c o u t << " wynik f l o a t : " << kwadrat ( 2 . 2 ) << e n d l ;
getche () ;
return 0 ;
}
277
278
Obowizkowo musimy stosowa nawiasy ostre. Po deklaracji tworzenia szablonu nastpuje zwyka denicja funkcji:
T kwadrat (T x )
return xx ;
{
}
Widzimy zwracany typ (w naszym przypadku jest to typ oglny T), nazw
funkcji (w naszym przypadku kwadrat) oraz list argumentw. W nawiasach
klamrowych umieszczone jest ciao funkcji.
Bardziej rozbudowany przykad uycia szablonu funkcji pokazany jest
w kolejnym przykadzie. W programie uyjemy wzorca funkcji max(), ktra pobiera trzy argumenty i zwraca warto najwikszego argumentu oraz
funkcji foo() ktra wczytuje dane z klawiatury i wywietla wyniki. Dziaanie
szablonw funkcji testujemy na trzech typach danych : int, double i char.
Listing 12.10. Uycie wzorcw funkcji
1
11
13
15
17
19
21
23
25
27
Program dziaa zgodnie z oczekiwaniem. Gdy do szablonu funkcji przekazujemy np. argumenty typu int, kompilator tworzy kompletna funkcje do
obliczenia najwikszej wartoci spord trzech wprowadzonych danych typu
int. Gdy wprowadzimy dane typu char tworzona jest funkcja do obsugi argumentw typu char. W programie zdeniowalimy dwa szablony, szablon
funkcji max():
template <c l a s s T>
T max(T w1 , T w2 , T w3 )
279
280
oraz foo():
template <c l a s s T>
void f o o (T x1 , T x2 , T x3 )
{ c o u t << " \ nargument 1 : " ;
c i n >> x1 ;
c i n >> x2 ;
c o u t << " \ nargument 2 : " ;
c o u t << " \ nargument 3 : " ;
c i n >> x3 ;
c o u t << " \ n n a j w i e k s z a w a r t o s c t o : " << max ( x1 , x2 , x3 ) ;
}
Realizacja szablonu funkcji max() dla obsugi argumentw typu int ma posta:
i n t max( i n t w1 , i n t w2 , i n t w3 )
{ i n t max = w1 ;
i f ( w2 > max ) max = w2 ;
i f ( w3 > max ) max = w3 ;
return max ;
}
x2 + y 2
(12.1)
Aby okreli, ktry z wektorw jest duszy wystarczy obliczy sum kwadratw skadowych wektorw. Wektor zdeniowany jest nastpujc struktur:
struct wek tor
{ int x , y ;
wek tor ( i n t xx =0 , i n t yy = 0 ) : x ( xx ) , y ( yy ) { }
bool operator < ( wek tor & w) // p r z e c i a z o n y o p e r a t o r
{ return ( xx + yy ) < (w . xw . x + w . yw . y ) ;
}
};
11
13
15
17
19
21
i n t main ( )
{ wek tor w1 ( 1 , 1 ) , w2 ( 2 , 2 ) , ww;
ww = max (w1 , w2 ) ;
c o u t << " d l u z s z y wek tor ma sk ladowe : "<< e n d l ;
c o u t << ww. x << " " << ww. y << e n d l ;
23
getche () ;
return 0 ;
25
281
282
10
12
14
16
Aby wprowadzi dan ilo argumentw uoglnionych w nagwku szablonu funkcji musimy to zadeklarowa przy pomocy sowa kluczowego class:
class T1, class T2, class T3. . . . . . W naszym przykadzie dalimy dwch
typw uoglnionych, wobec tego szablon funkcji mia posta:
template <c l a s s T, c l a s s R>
void pokaz ( T x1 , R x2 )
{ c o u t << " argument 1 : " << x1 ;
c o u t << " , argument 2 : "<< x2 << e n d l ;
}
using namespace s t d ;
11
13
15
17
19
21
23
25
stwierdzi, e uyto dwch argumentw typu int wobec czego wywoa pasujcy szablon:
template <c l a s s T>
T max (T & x1 , T & x2 )
{ i f ( x1 > x2 ) return x1 ;
else
return x2 ;
}
argumentem funkcji jest tablica typu double i cakowita liczba 10, wobec
czego wywoany zostanie szablon:
283
284
10
12
14
16
18
20
22
24
11
13
15
17
19
21
285
286
11
13
15
17
i n t main ( )
{ c o u t << max ( "Ewa" , "Anna" ) << e n d l ;
287
19
21
getche () ;
return 0 ;
23
funkcja
funkcja
funkcja
funkcja
:
:
:
:
Anna
Anna
Anna
Anna
10
12
14
16
18
20
22
288
24
26
28
30
32
{ i n t m1 [ 5 ] = { 1 1 , 3 3 , 2 3 , 1 5 , 7 7 } ;
char m2 [ 5 ] = { d ,
r ,
a ,
e ,
g };
bubble (m1 , 5 ) ;
bubble (m2 , 5 ) ;
c o u t << " posortowane l i c z b y c a l k o w i t e : "<< e n d l ;
pokaz (m1 , 5 ) ;
c o u t << " posortowane z n a k i : "<< e n d l ;
pokaz (m2 , 5 ) ;
getche () ;
return 0 ;
}
liczby calkowite :
33 77
znaki :
g
r
T>
x , T t a b l i c a , int n)
k)
k) / 2;
tablica [ s ])
return s ;
else i f (x < t ab lic a [ s ] )
k = s 1;
else
p = s +1;
}
return 1;
}
289
W pokazanym poniej programie najpierw deniujemy tablic (z ustalonym typem, np. int) a potem wywoujemy funkcj szablonow BinSearch().
Listing 12.18. Uycie wzorcw funkcji przeszukiwanie binarne
2
10
12
template <c l a s s
i n t BinSearch (T
{ int p = 0 ;
i n t k = n 1;
int s ;
while ( p <=
{s = (p +
i f ( x ==
T>
x , T t a b l i c a , int n)
k)
k) / 2;
tablica [ s ])
return s ;
else i f (x < t ab lic a [ s ] )
k = s 1;
else
p = s +1;
14
16
}
return 1;
18
20
22
24
26
28
30
32
34
i n t main ( )
{ i n t tab1 [ ] = { 1 , 1 1 , 2 1 , 3 1 , 4 1 , 5 1 , 6 1 } ;
int t1 = 31;
drukTab ( tab1 , 7 ) ;
c o u t << " e l e m e n t " << t 1 << " ma i n d e k s "
<<BinSearch ( t1 , tab1 , 7 ) << e n d l ;
char tab2 [ ] = { a , e , g , h , i , k , x , y } ;
290
36
38
40
42
51
x
61
y
Skonkretyzowana dla typu int funkcja szablonowa wymaga trzech argumentw : poszukiwanego elementu, tablicy i rozmiaru tablicy. Podobnie postpujemy dla poszukiwania elementu w tablicy znakowej. Na licie parametrw
szablonu funkcji moemy umieszcza typy klas. W kolejnym przykadzie
zademonstrujemy skomplikowan obsug zarwno zmiennych typw wbudowanych jak i obiektw klasy. Kompilatory zachowuj si bardzo inteligentnie. W naszym przykadowym programie chcemy porwna dugoci
wektorw i wyznaczy mniejszy wektor, oczywicie chcemy mie moliwo
wyznaczenia mniejszej z dwch liczb typu int.
Klasa wek do obsugi wektora 2D ma posta:
c l a s s wek
int x , y ;
{
public :
wek ( i n t xx , i n t yy ) { x = xx ;
i n t getX ( ) { return x ; }
i n t getY ( ) { return y ; }
y = yy ; }
291
// p r z e c i a z o n y o p e r a t o r
} ;
Funkcja min() moe by wykorzystana do porwnania dowolnych zmiennych. W naszym przypadku bdzie skonkretyzowana dla typu wek, bdzie
wyznaczaa mniejszy z dwch obiektw typu wek. Do wykonania poprawnie
operacji porwnania w klasie wek umieszczony zosta przeciony operator <.
Dla testw utworzylimy dwa obiekty klasy wek : w1 i w2.
Listing 12.19. Uycie wzorcw funkcji typ klasy argumentem szblonu
2
10
12
14
16
18
20
22
24
26
i n t main ( )
{ wek w1 ( 1 , 1 ) ;
wek w2 ( 2 , 2 ) ;
wek w = min (w1 , w2 ) ;
c o u t << " m n i e j s z y wek tor : " << w . getX ( )
<< " " << w . getY ( ) << e n d l ;
i n t x1 = 3 ;
i n t x2 = 4 ;
c o u t << " m n i e j s z a l i c z b a : " << min ( x1 , x2 ) << e n d l ;
28
getche () ;
292
return 0 ;
}
32
34
36
nastpuje konkretyzacja funkcji szablonu min() dla typu wek. Skonkretyzowana funkcja wyznacza mniejszy obiekt za pomoc przecionego operatora
mniejszoci:
i n t wek : : operator < ( wek & ww)
{ i f ( xx + y y < ww. x ww. x + ww. y ww. y )
return 1 ;
return 0 ;
}
Sowo kluczowe template informuje kompilator, e bdzie deniowany szablon. T typ oznacza nazw typu, ktry bdzie okrelony podczas tworzenia egzemplarza klasy. Zazwyczaj stosujemy jeden typ oglny, ale moemy
stosowa wicej takich typw. W takim przypadku w nawiasach ostrych
umieszczamy list typw oglnych oddzielonych przecinkami.. Egzemplarz
klasy tworzymy przy pomocy konstrukcji:
nazwa_klasy < typ> nazwa_obiektu ;
293
294
11
13
15
17
19
i n t main ( )
// o b s l u g a t y p u c h a r
{ war <char> zn ;
c o u t << " podaj znak : " ;
zn . set_war ( ) ;
c o u t << " wprowadzony znak : " ;
zn . pokaz ( ) ;
// o b s l u g a t y p u d o u b l e
war <double> x ;
c o u t << " podaj l i c z b e r z e c z y w i s t a : " ;
x . set_war ( ) ;
c o u t << " wprowadzona l i c z b a : " ;
x . pokaz ( ) ;
getche () ;
return 0 ;
21
23
25
27
Specykacja szablonu klasy (nasza klasa nosi nazw war) zawiera sowo
kluczowe template a po nim mamy nawiasy ostre <>. Wewntrz nawiasw umieszczamy identykator, ktry reprezentuje typ sparametryzowany,
w naszym przykadzie jest to identykator o nazwie T. Nazwa identykatora moe by dowolna. Wewntrz nawiasw mona umieci wicej ni jeden
identykator, np. :
< c l a s s T, c l a s s W >
Parametry musz by oddzielone przecinkami. Na licie parametrw szablonu mog by dowolne elementy, moemy mie parametry oglne i konkretne:
< c l a s s T1 , i n t n , c l a s s T2 >
Jako parametry szablonu mog by uywane typy wbudowane, typy zdeniowane przez uytkownika oraz wyraenia stae. Klasa war posiada jedn
dan skadow prywatn typu oglnego:
T n;
295
296
// k o n s t r u k t o r
// o b s l u g a t y p u d o u b l e
// t y p o g o l n y T
// k o n s t r u k t o r
Bardzo czsto stosujemy szablony klas do stworzenia tzw. klasy kontenerowej (ang. container class). Klasa kontenera to taka klasa, ktra deniuje
kolekcj obiektw. Do obsugi tablic, list powizanych czy stosw wykorzystujemy klasy kontenerowe, poniewa moemy napisa uniwersaln aplikacje
obsugujca rne typy danych. W kolejnym przykadzie pokaemy klas
kontenera do obsugi tablic.
Listing 12.21. Uycie wzorcw klas klasa kontenera (tablica)
1
11
13
15
17
19
21
23
25
27
297
298
29
31
33
35
37
39
41
i n t main ( )
{ t a b l i c a <int> t 1 ( 4 ) ;
c o u t << " podaj l i c z b y c a l k o w i t e : " << e n d l ;
t1 . init_tab ( ) ;
c o u t << " e l e m e n t y t a b l i c y : " ;
t 1 . pokaz_tab ( ) ;
t a b l i c a <char> t 2 ( 5 ) ;
c o u t << " podaj z n a k i : " << e n d l ;
t2 . init_tab ( ) ;
c o u t << " e l e m e n t y t a b l i c y : " ;
t 2 . pokaz_tab ( ) ;
43
45
47
getche () ;
return 0 ;
49
51
jest ona metod klasy tablica (wchodzi w skad szablonu klasy), zdeniowana jest na zewntrz klasy (klasa ma tylko jeden parametr typu), nie zwraca
adnej wartoci. Funkcja ta znajduje si w zasigu egzemplarza klasy tablica,
zdeniowana jest z typem oglnym T.
Funkcja pokaz tab() suy do wywietlania elementw tablicy:
299
300
oraz
t a b l i c a <char> t 2 ( 5 ) ;
Obsuga danych typu int i typu char jest poprawna. Gdy uytkownik sprbuje obsuy napis (tablic znakow) w postaci:
t a b l i c a <char> t 3 ( 5 ) ;
pojawi si problem, program nie uruchomi si gdy napisy wprowadza bdziemy z klawiatury. Musimy na nowo opracowa klas obsugujc tablic
znakow, w praktyce oznacza to dopisanie klasy specjalnej, korzystajc z
mechanizmu specjalizacji jawnej (ang. explicit specialization). W programie
musi by zarwno szablon klasy oglny oraz jego specjalizacja. Nie mona
301
// Klasa s z a b l o n u
302
// p u s t a l i s t a s z a b l o n u
// t y p s p e c j a l i z a c i n a p i s y
// o b i e k t t a b l i c y s p e c j a l i z o w a n y
Na kolejnym listingu pokazano program zawierajcy szablon klasy tablica oraz jego specjalizacj dla danych typu tablicy znakowej.
Listing 12.22. Specjalizacja klasy szblonu klasa kontenera (tablica)
1
11
// Klasa s z a b l o n u
// t a b l i c a danych na s t e r c i e
// k o n s t r u k t o r p r z y d z i e l a
// pamiec na s t e r c i e
{ rozmiar = r ;
tab = new T [ r ] ;
}
void i n i t _ t a b ( ) ;
13
15
// metoda do wprowadzani a
// elementow t a b l i c y
void pokaz_tab ( ) ;
// metoda do w y s w i e t l e n i a
// elementow t a b l i c y
~ t a b l i c a ( ) { delete [ ] tab ; }
17
19
21
};
23
25
27
29
31
33
35
39
41
43
45
47
49
template <>
// p u s t a l i s t a s z a b l o n u
c l a s s t a b l i c a <char >
// t y p s p e c j a l i z a c j i n a p i s y
int rozmiar ;
// r o z m i a r t a b l i c y
{
char tab ;
// t a b l i c a napi sow na s t e r c i e
public :
t a b l i c a ( int r = 1)
{ rozmiar = r ;
tab = new ( char [ r ] ) ;
}
void i n i t _ t a b ( ) ;
void i n i t _ t a b 1 ( ) ;
void pokaz_tab ( ) ;
~ t a b l i c a ( ) { delete [ ] tab ; }
};
51
53
55
57
void t a b l i c a <char> : : i n i t _ t a b ( )
{ c o u t << " dane : " << e n d l ;
f o r ( i n t i = 0 ; i < r o z m i a r ; i ++)
{ tab [ i ]= new ( char [ 2 0 ] ) ;
c i n >> tab [ i ] ;
}
}
59
61
63
65
67
69
71
73
75
77
79
81
83
85
87
i n t main ( )
{ int i l e = 1 ;
c o u t << " i l e elementow t a b l i c y : " ;
c i n >> i l e ;
t a b l i c a <int> t 1 ( i l e ) ; // o b i e k t t a b l i c y i n t
c o u t << " l i c z b y c a l k o w i t e , i l o s c = "<< i l e << e n d l ;
t1 . init_tab ( ) ;
c o u t << " e l e m e n t y t a b l i c y : " ;
t 1 . pokaz_tab ( ) ;
c o u t << " i l e elementow t a b l i c y : " ;
c i n >> i l e ;
t a b l i c a <char> t 2 ( i l e ) ; // o b i e k t t a b l i c y s p e c j a l i z o w a n y
c o u t << " n a p i s y , i l o s c = " << i l e << e n d l ;
t2 . init_tab ( ) ;
c o u t << " e l e m e n t y t a b l i c y : " << e n d l ;
t 2 . pokaz_tab ( ) ;
c o u t << " i l e elementow t a b l i c y : " ;
c i n >> i l e ;
t a b l i c a <char> t 3 ( i l e ) ; // o b i e k t t a b l i c y c h a r
c o u t << " z n a k i , i l o s c = " << i l e << e n d l ;
t3 . init_tab ( ) ;
c o u t << " e l e m e n t y t a b l i c y : " ;
303
304
getche () ;
return 0 ;
91
W naszym programie specjalizacja szablonu klasy zostaa specjalnie opracowana aby poprawnie obsugiwa tablic znakow. W szablonie klasy oglnej
deklaracja tablicy danych oraz konstruktor maj posta :
template <c l a s s T>
// Klasa s z a b l o n u
class t a b l i c a
{
int rozmiar ;
T tab ;
// t a b l i c a danych na s t e r c i e
public :
t a b l i c a ( i n t r = 1 ) // k o n s t r u k t o r p r z y d z i e l a
// pamiec na s t e r c i e
{ rozmiar = r ;
tab = new T [ r ] ;
}
.....................
//
//
//
//
pusta l i s t a szablonu
typ s p e c j a l i z a c j i napisy
rozmiar t a b l i c y
t a b l i c a napi sow na s t e r c i e
Mamy take rnie zdeniowane metody init tab(). W klasie oglnej mamy:
template <c l a s s T>
void t a b l i c a < T > : : i n i t _ t a b ( )
{ c o u t << " dane : " << e n d l ;
f o r ( i n t i = 0 ; i < r o z m i a r ; i ++)
c i n >> tab [ i ] ;
}
305
Sownik angielsko-polski
access specycation
accessor function
address
allocate
application
argument
assertion
A
abstrakcyjny typ danych, zazwyczaj
tym terminem okrelamy typ danych zdeniowany przez programist, typowym przykadem jest klasa jest to formalnie typ danych,
proces opisywania funkcji klasy niezalenie od jej implementacji, nazywamy abstrakcj danych
specykacja dostpu, etykiety private, public oraz protected reguluj
dostp do skadowych klasy (danych
i metod)
funkcje dostpu, s to funkcje (z wyjtkiem konstruktorw), ktre maja
dostp do prywatnych (private) danych klasy
adres, jest to liczba wskazujca
miejsce w pamici,
alokacja, przydzia (pamici)
aplikacja, program komputerowy,
traktowany przez uytkownika jako
jednostka
argument, warto przekazywana do
funkcji
asercja, instrukcja mwica, e element umieszczony w danym miejscu
w programie musi by prawdziwy
308
Sownik angielsko-polski
base class
binding
bit eld
boolean
bug
call
call by reference
call by value
calling constructors
calling function
cast
cast operator
B
klasa bazowa, jest to klasy na podstawie ktrej, tworzona jest klasa
pochodna, wykorzystujca mechanizm dziedziczenia
wizanie, interpretacja wywoania
funkcji w kodzie rdowym w celu
wykonania kodu waciwej funkcji
pole bitowe
wbudowany typ logiczny, wartoci
true lub false
bd w kodzie, nieoczekiwana sytuacja, termin wprowadzony przez dr
Grace Hopper, ktra bya kontradmiraem w U.S.Navy
C
wywoa, wywoanie
proces deklarowania parametru
funkcji jako zmiennej wskanikowej,
w konsekwencji przekazanie adresu
jako argument
proces deklarowania parametru
funkcji jako zmiennej prostej (nie
wskanikowej), w konsekwencji
przekazanie wartoci zmiennej jako
argument
wywoywanie konstruktora, konstruktor jest wywoywany zawsze,
gdy obiekt jest kreowany, jest wiele
sposobw wywoywania konstruktora
wywoanie funkcji, przekazanie sterowania do funkcji, ktra wykona
dane operacje
dosowna, wymuszona, konwersja
typw, np. (double)i
operator konwersji, suy do wymuszenia konwersji, np. zapis int (x*y)
powoduje, e warto wyraenia x*y
jest konwertowana do wartoci typu
int.
309
Sownik angielsko-polski
child class
cin
class
access specier
access specier
declaration
destructor
implementation
inheritance
instance variables
310
Sownik angielsko-polski
library
members
methods
scope
collating sequence
command line
compiler error
compile-time errors
concatenate
console application
constructor
base class
311
Sownik angielsko-polski
copy
conversion
default
inline
overloaded
containers
conversion automatic
cout
debugging
declaration
konstruktor kopiujcy wykorzystywany jest do kopiowania istniejcego obiektu do nowo utworzonego obiektu, gdy klasa nie zawiera wskanikowych danych, zazwyczaj nie ma problemw z konstruktorem kopiujcym ( nie s generowane bdne wyniki), gdy w klasie wystpuj zmienne wskanikowe, naley opracowa wasny konstruktor
kopiujcy
konstruktor przeksztacajcy, jest
to konstruktor jednoargumentowy,
ktry umoliwia przeksztacenie
obiektu jednego typu (wcznie z
typami wbudowanymi) na obiekt
danej klasy
konstruktor domylny, jeeli programista nie dostarczy wasnego konstruktora klasy, kompilator dostarczy konstruktor domylny
konstruktor typu inline
konstruktor przeciony
kontenery, zasobniki (podstawowe
elementy STL), kontener jest to
obiekt, ktry moe przechowywa
inne obiekty
proces ujednolicania typw w wyraeniach mieszanych
obiekt cout jest wykorzystywany
do wyprowadzania danych, denicja
obiektu cout jest zawarta w pliku iostream, plik iostream opisuje strumie wyjciowy, operator wykorzystywany jest do wstawiania znakw do strumienia wyjciowego
D
debugowanie, proces usuwania bdw w programach komputerowych
deklaracja, specykowanie typw
312
Sownik angielsko-polski
denition
dynamic allocation
delete operator
derived class
destructor
dynamic binding
eciency
encapsulation
equality expression
errors
compile-time
logic
programming
313
Sownik angielsko-polski
run-time
syntax
typos
evaluate
exit
executable program
exponential notation
expression
extern
eld
le
FIFO
ag
ow of control
ush
formal parameter
314
Sownik angielsko-polski
forward declaration
friend function
function
function
argument list
arguments structure
array arguments
call by reference
call by value
formal parameter
friend
header
inline
pointer parameter
type specier
virtual
function binding
315
Sownik angielsko-polski
function template
header le
heap
hexadecimal
high level languages
high-order bit
IDE
implementation
indirect addressing
indirection operator
inheritance
initialization
array
pointer
string
structure
316
Sownik angielsko-polski
variable
inline declaration
inline function
input/output
input/output stream
insertion operator
instance variables
integer overow
interface
invariant
item
iteration
languages
high-level
low-level
middle-level
inicjalizacja zmiennej
deklaracja funkcji wbudowanej (inline)
funkcja wbudowana, funkcja rozwijalna
wejcie/wyjcie, dane wejciowe s
wprowadzane do programu, dany
wyjciowe s wytwarzanie w procesie przetwarzania
strumienie wejcia/wyjcia, strumie to urzdzenie logiczne produkujce lub pobierajce informacje
operator wstawiania (symbol ) potrzebny jest do wywietlania danych
typw wbudowanych, sam operator
wstawia znaki do strumienia
dane klasy (data members), czonek
klasy
przepenienie, w czasie wykonania
program moe wystpi przekroczenie zakresu np. dla liczb cakowitych, mona otrzyma niepoprawny
wynik
interfejs, zbir deklaracji, potrzebnych do wywoywania funkcji
niezmiennik, element, ktry w danym punkcie programu musi by
prawdziwy
pozycja, skadnik, egzemplarz
iteracja, wielokrotne wykonywanie
tych samych instrukcji
L
jzyki (programowania)
jzyki programowania wysokiego
poziomu
jzyki programowania niskiego poziomu
jzyki programowania redniego poziomu
317
Sownik angielsko-polski
object-oriented
procedure-oriented
library
LIFO
linker
logical expression
low-order bit
machine accuracy
machine language
macro
manipulator
mask
mathematical library
member
318
Sownik angielsko-polski
member function
memory dynamic allocation
methods
multi-dimensional arrays
multiple inheritance
namespaces
new operator
null character
null pointer value
object
object program
one-dimensional array
operator
319
Sownik angielsko-polski
addition
address
arithmetic
assignment
associativity
bitwise
bitwise exclusive or
cast (type)
comma
conditional
decrement
delete
dereferencing
division
equality
equals
greater than
greater than or equal to
increment
left shift
less than
less than or equal to
logical
logical and
logical negation
logical or
modulus
multiplication
not equals
ones complement
precedence
relational
right shift
sizeof
operator dodawania
aperator adresowy
operator arytmetyczny
operator przypisania
czenie operatora ( z lewej do prawej lub z prawej do lewej)
operator bitowy
operator alternatywy bitowej (symbol |)
operator rzutowania jawnego
operator przecinkowy
operator warunkowy (symbol (?:)
operator dekrementacji
operator zwalniania pamieci
operator dereferencji (wyuskania)
operator dzielenia
operator relacyjny (porwnywania)
operator rwnoci
operator relacyjny wikszy ni
operator relacyjny wikszy ni lub
rwny
operator inkrementacji
operator przesunicia bitowego
operator relacyjny mniejszy ni
operator relacyjny mniejszy ni lub
rwny
operator logiczny
operator logiczny koniunkcji (symbol &&)
operator logiczny zaprzeczenia
operator logiczny alternatywy (symbol ||)
operator modulo (symbol %)
operator mnoenia
operator relacyjny nierwny
operator bitowy dopenienia
priorytet operatora (okrela kolejno wykonywania operacji)
operator relacyjny
operator bitowy przesunicia w prawo
operator rozmiaru
320
Sownik angielsko-polski
structure member
structure pointer
subtraction
unary minus
ostream
output
overow
overloaded function
overloaded operators
parameter
parent class
parametrized manipulator
pass
pass by reference
pass by value
passing addresses
passing arrays
passing le names
passing structures
pointer
321
Sownik angielsko-polski
argument
arithmetic
array
assignment
class members
constant
declaration
indirection
initialization
parameter
structure memebers
this
polymorphism
program ow
promote
322
Sownik angielsko-polski
protected access rights
random le access
323
Sownik angielsko-polski
STL
scope
sequential access
shift
side eect
signicant gures
simple inheritance
stack
stack operation
standard library
standard template library
statement
static
static Winding
storage allocation
storage class
auto
extern
register
static
static external
string
array of character
constant
end-of-string character
initialization
324
Sownik angielsko-polski
library function
standard function
struct
subclass
superclass
syntax
syntax error
template prex
this pointer
top down
truncate
truth table
two-dimensional array
type mismatch
type specier
typedef
unary operator
union
variable
virtual functions
void
Skorowidz
#dene, 65
#include, 6
abort(), 237
abstrakcyjne funkcje, 194
abstrakcyjne klasy, 195
akcesory, 50
argumenty domylne funkcji, 11
asercja, 238
assert(), 237
auto, 323
bdy, 230
bool, 23
break, 256
bufor, 231
buforowane wejcie, 237
buforowane wyjcie, 231
catch, 239
cerr, 150
chronione dane, 96
chronione metody, 44
cin, 5
class, 42
clear(), 237
const, 67
cout, 5
dane chronione, 44, 96
dane prywatne, 44, 96
dane publiczne, 44
default, 311
denicja funkcji, 53
deklaracja dostpu, 44
deklaracja funkcji, 33
deklaracja struktury, 31
deklaracja zapowiadajca, 135
delete, 75
destruktor, 58
dynamiczna alokacja pamici, 318
dyrektywy preprocesora, 39
dziedziczenie, 84
egzemplarz, 58
endl, 44
exit, 231
aga, 313
friend, 123
funkcje, 29
funkcje abstrakcyjne, 194
funkcje matematyczne, 317
funkcje operatorowe, 146
funkcje prywatne, 44
funkcje przecione, 13
funkcje publiczne, 44
funkcje skadowe, 47
funkcje statyczne, 179
funkcje wirtualne, 178
funkcje zaprzyjanione, 122
getche, 124
ignore, 236, 237
indeks, 72
inicjalizacja tablic, 72
inline, 48
klasa, 32, 33
abstrakcyjna, 195
bazowa, 84
oglna, 293
pochodna, 84
wirtualna, 208
zagniedona, 215
zaprzyjaniona, 123
klasy zaprzyjanione, 141
326
Skorowidz
kod, 16
kompilacja, 40
koniec acucha, 80
konstruktor, 58
konwersja, 59, 269
kwalikator zakresu, 212
manipulatory, 167
namespace, 24
new, 72
obiekt, 42
operator alokacji dynamicznej, 75
operator przeciony, 144
pne wizanie, 191
pami, 20
polimorzm, 178
private, 44
protected, 44
prywatne dane, 44
prywatne funkcje, 44
prywatne skadowe, 44
prywatne zmienne, 44
przecianie konstruktorw, 272
przecizanie funkcji, 13
przecizanie operatorw, 144
przekazywanie argumentw przez referencj, 7, 129
przestrze nazw, 24
public, 44
referencje, 7
rekurencja, 322
rzutowanie, 207, 223
silnia, 235
sizeof, 146
specykator dostpu, 34
static, 60, 178
statyczne funkcje, 178
statyczne zmienne, 178
std, 24
stderr, 238
STL, 266
struct, 31
struktury, 31
strumienie, 4, 175
tablice, 71, 75
template, 266
this, 67
throw, 239
try, 239
typedef, 324
typename, 278
using, 24
wejcie, 5
what, 260
wizanie pne, 191
wizanie wczesne, 191
wskanik, 7, 45
wskanik do funkcji, 191, 270
wskanik do tablic, 299
wyjtek, 239
zaprzyjanione funkcje, 122
zaprzyjanione klasy, 141
zmienne globalne, 178
zmienne referencyjne, 127
zmienne statyczne, 60
zmienne wskanikowe, 9
znak zerowy, 80
zwalnianie pamici, 181