You are on page 1of 542

Az operátorok precedenciája ACésaC+ +

és asszociativitása kulcsszavai
A táblázatban elóbb szere p lő operátorok magasabb precedenciával rendelkeznek, mint a ké- Kulcsszavak
sőbb felbukkanók. A C+ + szabályai szerint egy kifejezés leg belső zárójelén belül található
műve l eteket a program az operátorok precedenciájának sorrendjében vég zi el úgy, hogy and' (&&) false' s izeo f
a legmagasabb rangú operátorral e lőírt műve l etet hajtja végre előszö r, majd csö k kenő sor- and_e,,' (&,, ) float size, t
rend szerint folytatja. tv. egyoperandusú plusz (+) és minusz (-) a precedencia-lista maso- .,m '0" static
dik szintjén szerepelnek. vagyis - teljesen logikusan - mege l őzik az aritmetikai megfele l ői­ a u to friend' statiC, cast'
ket, amelyek az ölödik szinten kaptak helyet. A második szinten található & operátor a "cí- bitand' (&) goto struct
me" operátor, míg a g, szinten látható ugyanilyen jel a bitenkénti AND művelet operátora,
bitor' ( I) switch
A 2, szinten látható " a mutató által cfmzett tartalom kiolvasását végzi, míg a 4. szinten lát·
ható ugyanilyen jel a szorzás, Amennyiben a műve letek végrehajtásának sorrendjére nem bool' "in l ine' template '
utalnak zárójelek, úgy az azonos precedenciaszinthez tartozó műveleteket a program jobbról break 'ne this '
balra. vagy balról jobbra haladva hajtja végre a táblázatban megadottnak megfelelöen, case long throw'
Szint Operátorok Kiértékelési sorrend catch' mut a ble' true '
char names pace try'
1 (magas) ( ) :: balról jobbra
2 balról jobbra class' new' typedef
· [ l -> ++ --
type i d keyword, typecast campl' (-) not ' (l) type id'
3 * & ! - + ... -- ... - jobbról balra const not,eq' ( I,,) typename '
aizeof new delete const, cast ' operator' union
4 · * ->* balról jobbra continue or' (I I ) unsigned
5 • /% balról jobbra default or, eq' (I ,, ) us!n g '
6 ,- balról jobbra delete ' private' virtual'
) « » balról jobbra do protected' void
8 < <= > >= balról jobbra
double public' volatUe'
9 ='" I ", balról jobbra
dynarnic, caet' re gister wch ar, t
10
11
•• balról jobbra
balról jobbra else reinter - while
12 balról jobbra on= pret, cast' xor ' (A)

13
14
•• balról jobbra
balról jobbra
explidt'
export'
return
short
xor, e q' (A;; )

15 "
?, jobbról balra
jobbról balra
extern eigned
16 = * = /= ... = -= %=
«=»;&=A=I= , Csak a C++ nyelvnek része,
17 throw balról jobbra Azok a kulcsszavak, amelyek után zárójel szerepel,
18 (alacsony) , (vessző operator) balról iobbra a zárójelben található operátor szinonimái.

Adattípusok a C+ + nyelvben
TIpus 16 bit 32 bit Tartománv
uneigned short int 2 bájtos 2 bájtos O-tól 65 535-ig
short int 2 bájtos 2 bájtos -32 768-t6l32 767-ig
uneiqned long int 4 bájtos 4 bájtos O-tól 4294967 295-ig
long int 4 bájtos 4 bájtos -2147483 648-t612147 483647-ig
int 2 bájtoe 4 bájtos (16) :-32768 - tóI32767 - ig;
(32) :-2147483 6 4 8 - tól 2147483 647 - ig
unsigned int 2 bájtoe 4 bájtos (16) : O- tó165 535 - ig; (32) : Q-tól 4 294 967295-ig
s i ze_ t 2 bájtoe 4 bájtos (16) : 0 - tó165 535 - ig; (32) : Q- tól 4 29 4 9672 9 5 - ig
char 1 bájtoe l bájtos 256 karak ter
wchar_t 2 bájtoe 2 bájtos 65535 karakter
bool l bájtos 1 bá j tos Tr u e va gy False
float 4 báj t os 4 b ájtoe 1, 2*1 0·>1 tó13,4"10 " ig
double 8 bájtos 8 b ájtoe 2, 2 *10 -"'- tól 1, 8 * 10'''- i g
l ong d ouble 10 bájt o s 10 bájtos 3 , 4 *10 ·"" -t6l l, l *10"" -ig
TARTALOMJEGYZÉK

I. rész Bevezetés a C+ + programozási nyelvbe


l. óra Kezdő lépések
E!őkészülctck a programozáshoz ....... . · 3
A C++ nyelvjárásai (C++, ANSI C++, ISO C++ és a Windows) .4
A fordító telepítése és beállítása .5
Telepítés a CD-r61 . , ................ . . ..... 6
A Borland C++BuilderX fordító beállítása ...... . · .. 6
Fordít.'Ís a Borland C++BuilderX-szel ..... 8
A fordítóprog ram és a szövegszerkesztő ..... 9
Fordítás és linkelés ......... 10
Fordítás az integrált fejlesztői környezetben .10
linkelés (ősszeszerkeszl(:s) .......... . .......... 11
A fejlesztési ciklus. , .. . ..... II
HELLO.CPP - Az els6 C++ programunk · 13
Fordítási hibák ...... . 15
Kérdések és válaszok 16
Gyakorlatok 16
Kvíz 17
Feladalok 17
Válaszok a kvízkérdésekre . 17

2. óra Egy C++ program részei


Mitő!lehet jó választás a C++ nye lv. · . 19
Procedurális, strukturált és objektum-központú programfejlesztés . · 21
A C++ rwelv és az objekmm-központú programozás . 22
Beágyazás (encapsulation) . . . . . . . . . . . . . . . . . . . .. . ......... .. .... 23
Öröklődés és újrahasznosítás .................... . . .... 2.~
Többalakúság (polimorfizmus) .... . .. . . ..• . . . .. · . 24
Egy egyszeru program részei .......... . · . 24
Az #indudc utasítás viz.<;gálala .... . .. . .. ...•.. . 25
Elemzés soronként. . ... 26
Megjegyzések ................................ 28
A megjegyzések tipusai ................... .. ... ....•. .. . . ... 28
Megjegyzések használata egy egyszerű programban . 29
I
iv Tanuljuk meg a C++ programozási nyelvet 24 óra alatt

Függ"ények . . . . . . . . . . . . . . . . . . . . .....•.....•..... 30
Függvénybívások . . . • ...•..... . .. . . . •..•..... 31
Függvények használata ....... . ........ ............ . 32
Függvényparaméterek használata . . • . . . • . . . . . . . . . • . . • . • . .. . 33
Kérdések és válaszok ....... . .. 34
Gyakorlatok ........ ................. . .. 3S
Kviz .. ... 35
Gyakorlatok ... 35
Válaszok a kvrzkérdésekre ... 35

3. óra Változók és állandók


Mi az a változó? . . . ... .•. .. ..•.. ... . 37
Mem6riafoglalás .. . ... , . . . . .. .... . . , . . 38
A'L. egt:s'L. számok (Integer) mérete ..•.... . . •. . . . . •. . .... 39
signed és unsigned ........ ... ..................... 40
Alapvető változ6lípusok .......... . ........ ....... 41
Egy v{lltozó meghatározása . . . . . . ... .•.. . .... 42
A kis- és nagyl>culk eltér6 jelemésér61 ......... ........ . . , 42
Kulcsszavak. . . . . . . . . . . . . . . . . . . . . .. ... .•... .. .. . .... 43
Egynél több változó megadása egyszerre .....•............ .. . . .... 44
ÉrtC:kck hozzá rendelése a változókhoz . . . . . . . . . . . . . ....... 44
typedef . .. .. ... ... ... .. ... ... ... ... .. ... ..•.. ... ... . . .... 45
Mikor használjunk short és mikor long típust ... . . . . . • . . . . . ...... ,47
El6jel nélküli egészek túlcsordulása ...............•.....•............ 47
Az el6jeles egészek körbefordulása .......... , . . . ........ • .. .•.. ...... 48
Állandók . . . . . . . . . . . . . . . . . . . . . . . . . ...•............ •.. , ........ 49
Uterális állandók ........................ ... . ... •. ... • . . .•..•.. ... 49
Nevesített (symboJic) állandók .............•. . ..............•..... 50
ÁIland6 bevezetése a #define ku1cssz6val ..... .. , . . . • . . . • . . . . . . 50
Állandó bevezetése a const ku1csszÓval. ...... . .....•......•.....•..... 51
Felsorolt (enumemted) állandók ............ . . , . . . . , , ..•.. ...... 51
Kérdések és válaszok ................... . ................. 52
Gyakorlatok .. ... . ... . .......... . 53
Kvíz . ... ... 53
Feladatok . ...... . .. . .. . . .. . 53
Válaszok a kví'L.kérdésekre .. .... ...... 54

4. óra Kifejezések és utasftások


Utasítások .55
Üres helyek .. .. 56
Összetett utasítások .. 56
Kifejezések .. 57
Tanuljuk meg a C++ programozási nyelvet 24 óra alatt v

Operátorok ..................... . . .. 58
Értékadó operátor ............... . . .... 58
Matematikai operátorok ................................. 59
Matematikai és értékadó operátorok kombinálása . . . . . . . . .. 60
Növelés és csökkentés. . . 60
Előtag (prefix) és utótag (pOStfL'0 operátorok .............. . .... 61
Precedencia, avagy a műveletek végrehajtási sorrendje ........ 63
Egymásba ágyazott zárójelek összetett kifejezésekben. . .... 64
Relációs operátorok
Az if utasítás . . .
M_~

Összetettebb if konstrukciók .......


........ 65

Kapcsos zár6jelek használata egymásba ágyazott if utasítások esetén


. . 66
.. ~

. . 68
.. 70
A logikai operátorokr61 bővebben . . . . . . . . . . . . . ................. 73
Logikai ÉS .. . . 73
Logikai VAGY. .......... . ........ 74
Logikai NEM. . . . . . . .. .. . . . . . . . 74
Relációk precedenciiija .... 74
B ővebb en az igazság természetéren ... .... .•..•... . . . 75
Kérdések 6s v<Ílaszok ....... . . . . . . . . . . . . . . . . . . . . . . .. 76
. Gyakorlatok . . . . . . . . . . . . . .. . ... 76
Kvíz .. . ......... 76
_~~ ........ . . . ......... n
Válaszok a kvízkérdésekre . . . . . . . . 77

5. óra Függvények
Mi is az a függvény? ................... . . .. . . . . . . 79
A függvények deklarálása és definiálása .81
Függvények deklará lása . .81
Függvények definiálása ... .. . . 83
Függvények megadása: összegzés .. ... . . . .. . ... ..84
Változók használaca függvényeken belül ........... . ........ 85
Helyi változók ............... . . .... . .. ...... 86
Globális változók ............... ... ...... . . . ..88
A függvények argumenrumai ....... . . . . ..89
Függvényhívás mim paraméter ....... . .... . .... . ..89
A paraméterek egyben helyi változók is ..... . .. . . . . . .. . . 90
Értékek visszaadása függvényből ... . ....... .. . . . ... 92
r-üggvények alapértelmezett paraméterei ................ . .. 95
Függvények túlterhelése .. . ...........• . . . . . . . . . 97
Inline függvények .. .. . ......... . . ......... 98
A függvények és a verem . . . . . • . . ....• . . . ... 101
Kérdések és válaszok .......... . .. . .... . . . .... . ........ 103
I
vi Tanuljuk meg a C+ + programozási nyelvet 24 óra alatt

Gyakorlatok ....... . . . 104


Kvíz ... · ...... 104
Feladatok . 105
Válaszok a kví:t.k~rdésekre ... .... . . 105

6. óra Vezérlési szerkezetek


Ciklusszervezés ..... . . .... ..... 107
A ciklusok 6se: :I goto utasítás .••...• ..... 107
Miért ne használjuk :I goto utasítást? ...... ... . . ... ........ 108
A while ciklus ....... . ............. .• · ....... 108
llonyolulcabb while UUlsítások ........ . · ........ 109
A <.:ontinue és a break ......... ....... .. . . .. 111
while(1) - végtelen óklusok .. 113
A do .. .while ciklus .......... . · . 114
A for ciklus .. . ......... . · . 117
lnidalizád6, vizsgálat, léptetés. · •...... 117
Összeteuebb for ciklllsok ............ . · ....... 119
Halmozott inidaliz:ídó és léptetés .. . · ....... 119
Üres UUlsítások a for ciklusfejben .. . . ..... •...... 120
Üres for ciklus .... ...... ......... . . . . . . .. .. ..... 122
Egymásba ágyazott ciklusok · . . ...... 123
A switch utasítás ....... . .•.. .. ... .. 124
Kérdések és válaszok .... . · . 126
Gyakorlatok ......... . . .... 127
Kvíz .. · ........ "127
Fehldalok ................. . ..• ..... .. 127
Válaszok a kvízkérdésckre ... . . ...... 128

II. rész Osztályok


7. óra Alapvető osztálvok
Mi a típus? . . . .......... . . · ...... 13 ]
Új típus létrehozás,l · .. ..... 132
Osztályok és tagok ..... . . . ... . . . ... ... 132
Osztályok deklarálása ............... 133
Osztályok nevezékIana .. · ...... 134
Objekrum definiálása . .... . ..... ..134
Osztályok és objektumok ...... 13;
Osztályok Ulgjainak elérése: ......... 135
, Privát és publikus hozzáférb .... ..... ....... 136
Osztálymetódusok létreho~ .. ... 138
Konstruktorok és clestru!-..'l( :; ~ ~.lIZ _- .... létrehozása és türlése .. 140
Tanuljuk meg a C+ + programozási nyelvet 24 6ra alatt vii I
Alapértelmezett konstruktorok ............... . ... . · .... 141
A fordító áJtal biztosított konstruktor .... ... ... .. . ... •. .. . · .... 141
Kérdések és válaszok . ... 144
Gyakorlatok. · . 144
Kvíz . . ....... 144
Feladatok · .... 145
Válaszok a kvízkérdésekre ............. . · . 145

8. óra Az osztályokról - mélyebben


Konst.1ns tagfüggvénye k ........... . . ......................... .. 147
Felület és megval6sítás ............ . ...................• •. .. . .. J48
Hová tegyük az osztály-deklarációt és a metódus-def'iníciókat? ......•...... 149
Helyben kifejtett (jnline) megval6sítás ... .... . ............ • .... , .. 150
Osztályok beillesztése más osztályadattagjaiként ...... .. ..... 152
Kérdések és válaszok ......... ......... . .. .. ... • ....... "1 57
Gyakorlatok. . . . ........... ............... . . 158
Kvíz ...•..•. . .. ..• ... .. . • . • .. ..... 158
Feladato k . . . . . . . . .. . .................. 158
Válaszok a kvizkérdésekre ...... . .. ....... . ... •.• •.. •... .. .... 159

III. rész Memóriakezelés


9. óra Mutatók
A mutatók és használatuk megértése ........ .... . •. . . •.•. . . •. · .... 163
A memóriadm tárolása a mutató kban ..167
Mutatónevek .......................................... . · ... . 1<>9
A közvetett elérés operátor, avagy mutatók közvetett használata ... · .. 1<>9
Mutatók , címek és változók. . . . . . . . . . . . . . . .... .. . · .. 170
Adatok manipulálása a mutatók használatával ............ . .170
A mutatókban tárolt címek vizsgálata .. .. . .• . .. •. .172
Miért jó mutatókat használni? .................. . . . . · .. 174
A verem (stack) és a dinamikus memória (heap) .......... . .. . . . .. 175
A new kulcsszó használata · .. 176
A delete kulcsszó használata .. .177
Amemóriaszivárgás elkerOlése . . . . . . •. •.. .•. . . . . . . . .. . . . . .. 179
Kérdések és válaszok ............•. . . .. .......•. ... . • .. .... 180
Gyakorlatok. . ............ ... •. •. ..•.• . .. .. . .. . . ..• . ..... 181
Kvíz · . 181
Feladatok .. •... •.. ...• . . . .. .... •... . •• ..... 181
Válaszok a kvízkérdésekre · .... 182
I
vi Tanufjuk meg a C++ progral'TJOlási nyeMrt 24 óra alatt

10. óra A mutatók kifinomult használata


Objekrumok létrehozása a memóriában .......... ........... ... IM
Objektumok törlése . ... . . .... . .... .... .... . .....
o o ••••••• 184
Adattagok elérése mutatókon keresztül ••••• o o • o o o o • o o • o •• o • o o • o •• o 185
Adauagok a dinamikus memóriában ...... ................. 186
A this mutató . o • o o o • o • o •••••• •• o o o o o. o o ••• o o. 0 ••• 188
Gazdátlan vagy ~16g6 ~ mutatók ........ . . ...... .. ... .. ...... 190
Konswns mutatók •••••••• o o •••• o. o 0'0 o. o. o,. o o ••• o 191
Konstans mut:ltók és konstans tagfüggvények. o • o ., o o , •• , o o , • •• o o o o o 191
Konstans this mutatók ......... o • • o • o o • o o o o • o ,93
Kérdések és válaszok ........ o •• o • • • • • •• • o • 193
Gyakorlatok. . ................ • o •• o • o o • 194
Kvíz o o • o o o o •• o • o •• • o • • • • • • o • • • • • 194
P<:!ladalok o o o • • o • o o • o • • • o o • 194
Válaszok ti kvízkérdésekre o o • • o • • • •• o • o • • •••• 194

11. óra Hivatkozások


Mi :IZ a hiv:ll.koWs (referencia)? ... o •• o • o •••• o •• o •• ••• ••• ••• •••••• 195
Hivatkozások létrehozása .......... o • o o • o •••• o •• o •• o • • • • • • ••• 196
A wdme~ operátor alkalma7.ása hivatkozásokra o. o • o •• o o •••• o o • • • • o • o ••• 197
A swap() javítása mutatók segítségével o •• o o • o •• o • o •• o o ••• o o • • • 203
A SW:lp() ójabb megvalósílása hivatkozásokkal o •••• • •• o •••• o • • • • • • • •• • 204
Kvíz ...... o • o •• o • o o • o o • o •• o • o ••• 210
Feladatok . . . .. o • o ••• o • o •• o • o o •••• o • o o • • ••• • • o • o • o 210
Válaszok a kvízkérdésekre o •• o • o •• o ••••• o • o • o o •• o • o •• o • o o • o o • o • o 211

12. óra A hivatkozásokról és mutatókról - mélyebben


Hmékonyabb a cím szeri nti paraméterátadás .. 213 o • o o • o. o o • o o • o o o o •

Mutassunk konstansr.i ........ 217 o • o o • o • • o • o o •••

Hivatkozások, mint a mutatók kényelmes alternatívái. . ...... 219


Mikor használjunk hivalkozásokat, és mikor mutatókat? .. 221
Ne adjunk vissza mcgszCínt objektutnr3 mulatÓ hivatkozást! ............... 222
A dinamikus mcm6rialerületen létrehozott objekUlmra
mutató hivatkozás visszaadása 223 ••••••••••••• o • o o • o •• o • o o • o o

Mutató, mutató, kié a mulató? . ............... 226 o o • o •••••••••••••••

Kérdések és válaszok .. o •• o 227


• o o o o

Gyakorlatok .. o • 0227
Kvíz o o o o o o o o o o o o o o o o o0227
o o

Fcladatok o o 0228 o o

Válaszok a kvízkérdésekre 0228 o o


Tanuljuk meg a C++ programozási nyelvet 24 óra alatt ix

IV. rész Haladó eszközök


13. óra Afüggvények kifinomultabb használata
Túlterhelt tagfüggvények. · .. . 231
Alapértelmezett értékek használata · ... 234
Hogya n válasszunk a túlterhelés és az alapértelmezett
értékek h:lsználata között? .... 236
Konstruktorok túlterhelése ..... 236
Objektumok inicializálása .. 237
A másoló konstruktor .. . . .. . . •.... . .. 237
Kérdések és válaszok ..... . ..... 242
Gyakorlatok. · .. 243
Kvíz · 243
FeJadalok .243
Válaszok a kvízkérdbckre .. ... .. .. .. . . .. . . . • ..... 243

14. óra Operátorok túlterhelése


Hogyan terhelhetünk túl operulorokat . · . 245
Írjuk meg az inkremenuíl6 függvényt ...... ... . 247
A postfix (Ul6tag) inkremenu:i ló operihor tú llerhelése 248
A prefix és a postl1x közöui különbség . ........ . .... 248
operator+ ...... . . 251
Az operator+ túherhelése . . .. 252
Az operátortú lterhelés korlátai · ... 254
Mit célszerti túlterhelni? .254
operator" .254
Konverziós operátorok .258
Az intO opcdtor . . .260
Kérdések és válaszok · . 261
Gyakorlatok . · .. . 262
Kvíz ... . .... . 262
Feladalok . ....... .. 262
Válaszok a kvízkérdésekre . . . .. 263

15. óra Tömbök


Mi az a tömb? . ... . .... .. .. •. .. . ....... 265
A tömbök elemei . . . . ... 266
Tömbök túlírása . .. 268
Kerítésoszlop hibák . 268
Tömbök előkészítése. 269
ObjekUllntömbök ........... . . . ... .. • ..... .... 270
Többdimenziós tömbök ...... . . . . . .. ... . ...... 272
xITanuljuk meg a C+ + programozási nyelvet 24 óra alatt

Többdimenziós tömbök inidalizálásll .273


Néhány szó II memórial1asználalr61 ........... . .. ... 275
Mutatótömbök . . .......... .. ... . ........ 276
Dinamikus memóriában tárolt tömbök deklarálása .... 278
Tömb mutatója és mutatók tömbje ...... . .. .... 278
Mutatók és tömbnevek .............. . .. .. .. .... 279
Dinamikus memóriában létrehozon tömbök törlése .. 281
Karaktertömbök ..... . •.•• •.• •.• • ..... 282
Az strcpyO és az strncpyO függvények .......... . . 284
Karakterlánc-osztályok ... . .. .. . .. ..... 286
Kérdések és válaszok . .. . .. ..... 287
Gyakorlatok. .. 287
Kvíz ....... .... . . ...... 287
Feladatok .. • .. . •. ..288
Válaszok a kvizkérdésekrc ........... . ............. .. ..... 21J8

V. rész Öröklődés és többalakúság


16. óra Az öröklődés
Mi az az öröklődés? ... . . . .................... • ..... . ......... 291
Öröklődés és származtatás ....... . .........•. . •.. . . . . ...... 292
Modellezzük az öröklődést az Állatok Országával. . . . . . . • . . . . . • . . . ... 293
A .';zármaztatás szintaxisa ..................... . 294
Privát vagy védett? ......... . ... . ......... 296
Konstnlktorok és destruktorok ...... .. .. . •.. ...• . .. . ...... 298
Paraméterek átadása a báziskonstruktornak .......... . ..... 300
Függvények felülbírálata . . . . . . . . . . . . . . . . .. . ..• .. ... •... •...... 305
Túlterhelés vagy felülírás? ........ .............. . .... 307
A bázisosztály metódllsaimlk elfedése . . . •. . . •. . . . . . . . •. . . • . . . 307
A bázismetódlIs meghívása .... ... .•... ..•... .. . . 309
Kérdések és válaszok ...•... •.... .. .. . ..... 31 1
Gyakorlatok. . . . . . . . . . . . . . . . . ... ..•... .. . 311
Kvíz... ... .. .... ... .. .... .. .. . ... .. 31 1
Feladatok ...... . ...... 311
Válaszok a kvízkérdésekre .............................. 312

17. óra A többalakúság és a származtatott osztályok


A vinuális függvényekkel megval6síton többalakúság ...... . ......... 313
Hogyan működnek II virmális függvények ............ . • . . . . . • . . . 318
Nem juthaL,>z innen amoda. ....... . ....... 319
Szeletelés (slicing) . . ......... . .. . .. ........ 320
Virtuális destruktorok .. ... ..•. . 322
Tanuljuk meg a C+ + programozási nyelvet 24 6ra alatt xiI
Virtuális másoló konstruktorok ... . 322
A virtuális függvények kölL5égei ..... • . ... 326
Kérdések és válaszok ........ . . ....126
Gyakorlatok. ..327
Kvíz ..... 327
rdadatOk ..... 327
Válaszok a kvízkérdésekre . . .....~28

18. óra A többalakúság kifinomult használata


Problémák az egyszeres örökl6déssel ....... . .. . . .329
Elvont adattípusok. . . . ........... . .....•..... .. 334
Üres virtu,ílis függvények ....... ... ....... . .. . ...... . .. 338
Az üres viltuális függvények megva16sítástl .......... . .. . .. .. 342
Az elvonatkoztatás összetett hierarchiája .. 345
Mely típusok elvontak? ....... .349
Kérdések és válaszok 350
Gyakorlatok. · . 351
Kvíz .... . 351
FeJadatok . ........ 351
Válaszok ti kvízkérdésekre ........ . . . . .. . . .. . . .352

19. óra Láncolt listák


Láncolt listák és egyéb struktúrák ....... . . ... 353
Esetlanulmánya láncolt listák használatáról · . 355
A felel6sség átruházása .. .355
Az alkotórészek ........ 355
Mi a láncolt listák tanulsága? 364
Kérdések és válaszok · 365
Gyakorlatok. . ....... . 366
Kvíz 366
Feladatok .366
Válaszok a kvízkérdésekre ......... . ..... . .. ... . . .. . . . . . . . . 366

VI. rész Különlegességek


20. óra Különleges osztályok, függvények, mutatók
Statikus tagváltozók . ... 369
Statikus tagfüggvények . ...... 372
Egymást tartalmazó osztályok ............... . .... . .... 374
A tartalmazott osztály tagjainak elérése . . .. . . . . 380
A tartalmazott osztály elérésének korlátozása . . . . . 380
Xli , Tanuljuk meg a C++ programozási nyelvet 24 6ra alatt

A tartalmazás költsége ... .. . , . . . . . . . . . . 381


Érték szerinLi és hivatkozás slerinti másolás. . .. ... ... . . , . . . .. 381
Baráti viszonyban levő osztályok ....... . . ......... 381
Baráti viszony füg&'Vények közölt ............... . ..• • . . . . ..... 382
Függvénymutarók ....... , . . . . . . . . . . . . . .... . 382
Rövidített írásmód .. ... .. ... .. ..•.. . .. 386
Függvénymutatók tömbje ................ . . . • . . . .. . .. 386
Függvénymutatók átadása más függvényeknek .. . ..... 389
A type(k:r használata függvénynmtatókkal ........... . ............ 391
Tagfüg&>vényekre vonatkozó mutatók . . . . . . . . . . . . . . . . . . . . . .. 394
Tagfüggvényekre vonatkozó mutatókhól álló tömbök ....... . ..... 397
Kérdések és válaszok .. ............... . ........ 400
Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . .. . .. 400
Kvíz. . . . . . . . . . . ....... . ........•............ . ......... 400
Feladatok ....... ... ... ..•.. .•.. . . •... . ...... 401
Válaszok a kvízkérdésekre . . ..................... . ......... 401

21. óra Az előfeldolgozó


Az előreldolgozó és a fordító ......... •.. .......... . ................ 403
A köztes állapot mentése . . . . . . . . . . . . . . . . . . • . . . . . . . . . . . . . . 404
A #de nne direktíva használata. .......................... . .... 404
A . define használata álland6kkal kapcsolatban . . . . . . . . . . . • . . . . . . .. 405
A -*define és az '#ifdcf használata vizsgálatok végzésére. . ........ 405
Az #else direktíva .............................. ..... .... 406
Beszúrás és annak vezérlése .. . . . . . . . . . ........... • .......... 407
Szimbólumok definiálása a parancssorban .......... . .... . .......... 109
A meghat~irozotts{lg rnegszüntetésc .. . . . ................ 409
Feltételes fordítás ......................... . ... . .... . . . ........ 409
Makróként megvalósított fü~>vényck ........ . . . ........ 410
Mi ez a rengeteg zárójel? ..... .•.......... 41 1
Makrók, függvények, sablonok .. . .......... 413
Karakterláncok kezelése .......... . . ............... 414
A szöveggé ala kító (slringizing) operátor. . . . . . .• . ..• . . . . . . . . .414
Az összefúző operátor. . . . . . . . . . . . . . . . . . . . . . . . ... 414
Előre meghalározoa makrók . . . . .. . . . . . . . .. . . . . . .. . .. . .. 415
Az aS5ertO beépített makró . . . . . . .. .......•.... . ..... . .116
Nyomkövetés az assertO makr6 segítségével .418
A makrók mellékhatásai ....... . . ................ 418
Osztályinvariánsok ............ . . ... 420
Köztes értékek kiíratása ...... . . ... ... 425
Nyomkövetési szintek .. ... .... .. ... •. •... . . .. •. . . . . . ... 426
Kérdések és válaszok ...................... .432
Fe1adatok . .... .. ......... ... . . ..... . ...... . .... . . 4.l3
Tanuljuk meg a C+ + programozási nyelvet 24 6ra alatt xiii I
Kvíz , ...... . ........ 433
Gyakorlatok . , ...... . .. ... .... ..433
Válaszok a kvízkérdésekre .. . ............ ........... . ........ 434

22. óra Objektum-orientált elemzés és tervezés


A fejlesztési ciklus . . . . . . . . . . . ........ , . , 435
E~,')' riaszt6rendszer szimulációja ... ..... . ............ , . . . . , 436
Konce[Ki6lelV ..........• ... •... .. •..• . .. ........ 437
Elemzés és az igények felmérése ........ • ......•..... . ........... 437
Magas és alacsony szinnl teJVezés ....... ••.• .... .. ... •. . .•.. • .... 438
Egyéb objektumok . . . . . . . . . . . . . .•. .. ..•... . . .. 439
Milyen osztály.link lesznek? . . . . . . . . . ............... 4,,9
Hogyan jelezzük a ri:lsztásokat? ........•. , . 440
Eseményhurkok ............ . 441
PostMaster: egyesettanulmány . . . . . . . . . . . .... . . • ' , 443
Mérj kétszer, vágj eb'Yszer . ......... .. ... .. ... .•. .. , ... 444
Oszd meg és uralkodj. . ... ... .. . ..•..•.. , ... 444
Üzenetform;'itum ................. . ........ . , .. , ...... 445
Az osztályok kezdeti telVC ....... . .•...... .. . . , ...... 446
Egy vagy több kiindulópontú hier'dfchia .. . ...•..... . .......... 447
Interfészek tervezése .................. . . . ... ...• , .•. . . ....... 449
Prototípus létrehoz.1S:l ................. ... ... ..•.. . .. 450
A 8O/80-as szabály .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . . . . . . . 451
A PostMasrerMessagc os;t.t{ily tervezése ......... . . 452
Az alkahnazásfejle.~;t.tGi interfész. . ..... . ..... . , . .. , .... 452
Progmmozás nagy csoportokban ......... , .... , . • . . . . . . ... 454
A tervezési folyamauaJ kapcsolatos szempontok. . . . . . . • . . . . . . ....... 454
TelVezési döntések. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .... 455
Mikor hozzunk döntéseket? ..... . . . . . . . . . . . . . . . • . . . . . . . 455
Munka a vezé rl őprogramokkal . . . . . . . . . . . • . . . • . . • . . . . . • . . . . . . 456
Kérdések és válaszok .... . •......•..... • .. , . . . 463
Gyakorlatok. ........ . . . . . . . . . . . . . . •. . . . . ..... 463
Kvíz .. . .... , . . . . . . . . . . . . ...•.. , . . . .. ...... 463
Feladatok . . . .. ........ ... .. ..•.. ....... 464
Válaszok a kvízkérdésekre ..... ....• . .. .. . .. , . . . ....... 464

23. óra Sablonok


Mik azok a sablonok? ........ . . . .. . ... .. .. . .. 465
A sablon példányosítása ....... . ... .. . • ' . . .. . ..... , 466
A sablon definíciója . . . . . . . . . . . . .. . ...•... , . . ........ , 466
Sablontípus használata .......... . ..... .. . • ' . . . . . . . , 474
A szabványos sablonkönyvtár .......... ... ..•. , 480
xiv ITanuljuk meg a C++ programozási nyelvet 24 6ra alatt

Kérdések és vála.o;zok .... . .... .. ......... . 481


Gyakorlatok ..... . .. .. .. .. .. .. .. 481
Kvíz . . ...... ........ . .. .. 481
Feladatok ..... . . ... 482
Válaszok a kvízkérdésekre ..... " ..... ".482

24. óra Kivételek, hibakezelés és néhány tanács


Programhibák, tévesztések, k6dmegromlás . . . . . . . . . . . . . . . . . . . ... 483
Váratlan események kezelése ... ... .. ..•.... .. ... .. . . 485
Kivételek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . • . . . . . 485
Hogyan val6sitsuk meg a kivélelkezelést? . . . . . . • . . . . . 486
A try és catch blokkok használata ..... 491
A kivételek elkapása ................ . . . . . . . . . . . . . . . .. . .. 491
Több catch megadása. . . . . . . . . . . . . . 492
Hivatkozás elkapása - többrétúség a kivélelkezelésben ....•. . . •.... .. 492
Hogyan írjunk professzionális minőségű programo kat? . . . 496
Zár6jelek ......................... . . .• . . .•...... 497
Hosszú sorok . . ........ . . .. .. . . • .......... 498
A switch utasítások. . . . . . . . . . . . . •. . .. . . . . •.... . . 498
A program szövege ................. .. 498
Azonosít6nevek . . . . . . . . . . . . . . . . .. . ....• . .. .. . . . . •...... 499
A nevek betO"zése és a nagybetuk kérdése . . . . . . . . . . . . . . . . 499
Megjegyzések ... .. ... .... ..•...•. . 500
Hozzáférés ....... ................ . 501
Osztálydefiníd6k .......... .•.. ... ... ... . 501
Beemelend6 állomá nyok .. ..• .. . .. •.... . ... • .. . ... . . •. ...... 501
assenO . . . . . . . . . . . . . . .................... . . 502
const ... . .. . ... . ........ . . . . . . . . . .. . . . • . .. . . . 502
További források . . . . . . . . . . . ... ... ... .. ... .... .. .. . .. . . . 502
Hol kapunk segítséget és tanácsokat . . . . . . . • . . . . . • . . . . . . .. 502
Ajánlott olvasmányok ....... ... ... .•. ... . .... . 503
Kérdések és válaszok ....... . . . . . . 504
Gyakorlatok. . ........ 504
Kvíz. . . . ..•... . . . ..... 504
Fe1adatok ... ... ... ... ..•.. . . ....... 504
Válaszok a kvízkérdésekre . ... ... ... ...•. ....•. . . .. 504

VII. rész Függelékek


A függelék A bináris és a hexadecimális számrendszerek ..... . .. . ...... 507
B függe lék Gyakran használt kifejezések. . . 519

Tárgymutató . 531
A SZERZŐKRŐl
Jesse Liberty számos szoflverfejlesztéssel kapcsolatos könyvet írt már, amelyek között
akad néhány kiugróan népszerű is. Ez utóbbiak elsősorban a CH nyelvvel illetve
a .~TI technológiával kapcsolatosak. 6 a vezet6je a Ubeny Associates [nc. nevű cég-
nek, amely egyedi programok fejlesztésével , tanácsadással és oktalással foglalkozik.

David B Horvath eCCp) vezető tanácsadó Philadelphiában, Pennsylvaniában éL Több


mint 15 éve dolgozik az informatikában illetve részmunkaidőS egyetemi tanár néhány
helyi ilIerve távoktatással m(íköd6 egyetemen. Az áilala oktatott tá rgY'd.k el sősorban
a C++ nyelvvel, a Unix/Linux operációs rendszerrel, valamint adatbázisokklll kapcsola-
tosak. Diplomáját szervezeti dinamikából szerezte a University of Pennsylvanián 1998-
ban. Több nemzetközi szemináriumot és szakmai összejövetelt vezetett már SZ<1kmai
egyesületek és vállaltok sz!imára.

David a szerzője a Unix for the Mainframe (Prentice-Hall/PTR) címu könyvnek, Jesse
Libeny társzerzojeként pedig részt vett a Sams Teach Yourself C++ for Unux in 21 Days
címu kiadvány írásában. Szintén o írt egyes fejezete ket a következo muvekbol: Unix
Unleasbed (tőbb kiad~s), Red l-hit Linux Unleashcd (több kiadás), Learn Shell
Programm ing in 24 Hours, Linux Unleashed Fourth Edition, Linux Programming
Unleashed Second Edition. Emellett számos SZllkcikke jelent meg különbözo magni-
nokban.

David írói és oktatótevékenysége mellett aktív közösségi ember és önkéntes feladato-


kat is vállal különböző szakmai szervezetekben. Ha épp nem a billentyűzet el6u Ol és
könyvet ír, akkor kertészkedik, áZlatja magát egy kád forró vízben, vagy nyomdafesté-
ket nem túrő hobbikkal Oti el az icl6t. Idestova 17 éve há zas és van néhány kutyája
meg macskája (ezek száma folyamatosan változik). Aki társalogni szeretne vele,
az a cppin24@cobs.com címen éri el. Reklámleveleket kéretik me1l6zni!
I
xvi Tanuljuk meg a C+ + programozási nyelvet 24 óra alatt

Ajánlás
Ajimlom ezt a k6l1yuet Edythe-nek, akilo7 az C/etet kaptam; Staccy-nek, aki megosztja
azt ve/em; Robillllak és Nachelllek, akik értelmet adnak neki.

- Jesse liberry

Ajimlom ezt II k6/1yuettestvéremnek Andy-nek ésfeleségéllek Peg/lck második házassá-


gi évford/llójl/k alkalmából (2004. július 13.). Igazából az eskl1vojtiMI1 akll/1am át-
adlli Ilekik (>gy dedikált példányt, de ellltíz6doll a projekt.

- David B. HOIVa/h

Köszönetnyilvánftás
Minden könyv kivál6 alkalmal teremt arm, hogy köszönetet mondjunk benne azoknak,
akik nélkül garantáltan nem jÖll volna létre. Esetünkben a Staccy, Robin és Rachel
Liberty állnak az élen.

Ezen kívül szeretnék köszönetet mondani mindazoknak, akik a különböző kiadók-


Sams, Que, O'Reilly, \'('rox - munkatársaiként segítenek abban, hogy eddigi ffitlveim
megjelenhessenek. A Sa ms Kiadó szerkesztői valamennyien kiváló munkál végeztek,
ám külön szeretném közülük kiemelni Carol Ackermant, Chrisly Fr..mklint és Pau l
Striclandel. Szintén hálával ta!lozom Rieh Halpertnek. Végezetül szeretném megkö-
szönni Mrs. Ka1is munkáját, aki 1965-ben, hatodik oszt{llyos koromban megtanítolIa
nekem, hogyan kelJ keHes számrcndslerbcn összeadni és kivonni, miközben sem 6,
sem mi nem tudtuk, hogy ez pontosan mire is jó.

-Jesse liben)'

Mindazok mellett, akiknek Jesse az imént köszönetet mondoLL valójában még sokan le-
hetnek II Samsnél, akiket kifclejtcttlink. Tőlük cz úton is elnézést szeretnék kérni. Jó-
magam Songlin Qillval és Lorella Yatcs-Slel dolgoztmn ezen ;1 projekten.

Ez a könyv természetesen nem jöhetett volna létre a csa!{Ic.!orn, killönösképpen pedig


feleségem Mary támogatása és megértése nélkül. Sajnos a könyvír.'is már csak ilyen.
Az embernek leginkább esténként meg hétvégén van ideje és hangulata az alkotáshoz,
amikor a csa láddal kellene lennie. Ilyenkor jön aztán a ~bocs de most nem tudok vele-
lek menni, mert a könyvön kell dolgoznom" szöveg.

-/)cwid B. Horoath
Tanuljuk meg a C++ programozási nyelvet 24 óra alatt xviiI

Kfváncsiak vagyunk az Ön véleményére!


Mint a könyv olvasója, Ön, kedves olvasó a mi legfőbh kritikusunk. Éppen azért nagyr.!
értékeljük az Ön véleményét. Tudni .szeretnénk, ha valamit jól csináltunk, és persze a7.t
is, Ita nem. Tudni szeretnénk, milyen más teriilctekr61 olvasna szívesen könyvekct eb-
ben a sorozatban, és termésZdesen örömmel fogadunk minden egyéb megjegyzé.<;l is.

A Sams és a Kiskapu Kiadó örömmel meghallgatj:t mindenki véleményét. Kt:rjlik írja


meg nekünk, mi az, ami tCtsZett Önnek vagy ami nem tetszett ebben a könyvben , hi-
SZen ez na!.'Yban segítheti további munkánkat , és azt, hogy egyre jobb minőségű köny-
veket adjunk ki.

Ug)'clllakl...'orjCJ/lIOS lwk r,mjl/k megjegyezni, hogya könyv rmta!múva! kapcsolaros 11/11-


szaki /...0rdésekbclI álfalá!?clII /leli! rudullk segírelli. A SC/ms Kiad6nak IIg)'l/I/C/kko r U(1/1
egy Felhaszná/ói SzolgáItClfÓ részlegc, amcly (I ÁlÖII)'Vve/ kapcso/alos kérdéseker meg-
kapja, és esetleg II/eguálaszolja.

Ha ír nekünk, kérjük adja meg a könyv pontos dmét és szerz6jét, az ön nevét, e-maii
dmét és esetleg telcfonszámát. Amennyiben módunkban áll, me!,'Vizsgáljuk a felvetést
vagy problémáI , illetve megosztjuk aZI a szerzővel és a szerkeszt6vel.

A könyv eredeti angol ki:tdóját:l feedback@samspublishing.com címen érheti el.


Ha hagyományos levt:lben kívánja a Sams Kiad6t megkeresni, levelét a következő cí-
men várjuk:

Email: feedback@samspublishing.com
Maii: Michael Stephens
Associate Publisher
Sams Publishing
800 East 96th Street
Indianapolis, IN 46240 USA

Magyar kiadással kapcsolatos megjegyzéseit kérjük a kiadoElkiskapu . hu címre küldje.

A Sams egyéb kiadványaival kapcsolatban kérjük látogasson el


www.samspublishing.comcímre. Ha ezzel a konkrét könyvvel kapcsolatban keres
valamit a SAMS webhelyén , a keresőmezőbe kérjük az eredeti kiadás ISBN számát
(0672326817) gépelje be.

A magyar kiadás weboldalát és letölthelő anyagait a Kiskapu Kiadó webhelyén


(www.kiskapukiado.hu) találja meg.
BEVEZETÉS

Ennek a könyvnek az a célja, hogy 5egítségévcl <IZ olvasó megtanulhasson C++ nyel-
ven programozni. A kötet 24 tan6rának megfeldó menn y iségű anyagot tartalmaz, és
részletes áttekintést az o lyan alapvető területekről, mint a be- és kimenet kezelése,
a ciklusok szervezése, a tömbök használata , az objektum-központú programozás vagy
a sablonok (template). Mindez elengedhetetlen ahhoz, bo!:.')' valaki C++ nyelven múkö-
dó alkalma7..ásokat mdjon fejleszteni. A leckékben számos a tananyagot illusztráló kód-
részlet találhat6, :lmelyeknél mindig megadtuk azt is, hogya prog'.lrn futtatásának mi
a kimenel. A fontos szintaktikai elemeket külön is igyekeztünk kiemeh,i, hogya könyv
utólag, referendaként is használható legyen. Valamennyi óra anyaga néh{my kérdéssel
és gyakorlattal végz6dik, ezzel is segítve :L tanultak gyakorlatba v,116 átemelésél. A ko-
rábbi kiadásokb61 hi{myzott az ebben a kiadásban minden lecke végén felbukbn6
kvíz, amelyhez természetesen a helyes válaszokat is megadjuk.

Kinek érdemes elolvasni ezt a könyvet?


Ebből a könyvből az is megtanulhalja a C++ nyelv has:miilat{ll, akinek semmiféle ko-
rábbi programozási tapaszmbta nincs. Mi mindenl az clcj6r61 kezdünk elmagyaclzni,
vagyis nem csak magát nyelvet tanítjuk meg, hanem a C++ nye l vű programozással
kapcsolatos koncepcióbl, alapelveket is. A szintaxist mindig példákkal illusztrn ljuk,
a bemutatott k6dok míiködését pedig szükség esetén akár soronként elmagyarn z7.uk.
Éppen ezért gondoljuk úgy, hogy ez a könyv nagyszeni társ lehet egy a C++ nyelv bi-
rodalmában tell utazáshoz. Akár rendelkezik az olvasó korábbi programozási tapaszta-
latokkal, akár teljesen kezdő, bízunk benne, hogy e kötet világos szerkezete meg-
könnyíti majd számára a tanulást.

Nem kellene el6bb megtanulnom a C nyelvet?


Ez a kérdés b'Yakorlatilag elkerülhetetlenül merül fe l, hiszen a C++ bevallottan nem
más, mint a C nyelv továbbfejlesztése. Kell-e tehát ismernünk magát a C nyelvet a hhoz,
hob'Y megéruük a ~javított vá ltozatot"? Stroustroup, a C++ nyelv alkotója és számos más
szakértő is egyetért abban, hogy nem csak hogy nem kell ismernünk a C-l, hanem bi-
zonyos helyzetekben kifejezetlen előny is, ha nem tudunk róla semmit. MegfigyelbelŐ
ugyanis, hogya korábban C-ben fejlesztő progl'"dmoz6knál kialakulnak bizonyos "rossz
szokások", amdyeknek C++-ban már nem tudnak "hódolni", és amelyek kezdetben
erősen gátolják őket az átáJlásban. Ennek megfelelően a könyv megírása során mi sem
)o( ITanuljuk meg a C+ + programozási nyelvet 24 óra alatt
fcJtételeztük az olvasór61, hogy ismeri a C nyelvet Persze ha valaki mégis, akkor sincs
semmi veszve. Nekik azt ajánljuk, hogy fussák át az e1s6 néhány fejezetet, csak hogy
képbe kerüljenek, aztán kapaszkoc\janak meg valami szilá rd objektumban, mert hama-
rosan az általuk ismert nyelv olyan kiterjesztbeit fogják látni, ame\yekr61 eddig nem is
:ílmoc1tak.

Miért érdemes megtanulni a C+ + nyelvet?


Ez megint egy igen gyakori kérdése, 561. áll!lláb-.tn olyasvalanú szokott utána jönni hogy
H Vé&tiI is az X nyelv lJ laglÍjabb és legnalJyszen7bb, és hamarost/Il úgyis leváltja ti C++-I".
Erre a rövid vábszunk körülbelül ~lImyi, hogy .Nem addig van az!". Rengeteg olyan
nyelv vol! m:lr, amir61 egy id6ben mindenki tudta, hogy le fogja váltani a COBOl-t, még-
is ebben a pillanatban is töhh millió sornyi olyan COBOl kód van, anút "komoly" szerve-
zetek rendszereiben aktívan használnak. (Mi több, foly:mlatOsan újabb sorokat is irnakD
Körülbelül ugyanez a helyzet a C++-szal: kódsorok millióit írták ezen a nyelven, 86t, még
mindig írják 6ket. A másik nyomós indok, amiért mindenk6ppen érdemes megtanulni
l C++-t az, hogy bár vannak . újabb és javított~ nyelvek, az(:rt ezek bevallOlwn sokat me-
rítettek a C++-ból. Mind :1 Jav:1 mind a CI:t sz{unos .. C++-szal eb'Ycz6 nyelvi elemet tartal-
maz, viszont ezeknek a nyelveknek az els.1játítása több ponton jóval nehezebb.

Aki tehát ismeri az új trónkövetelők 6sét, az könnyebben boldogul majd a leSZ:.'Írmazot-


takkal is. De akár tovább is mehetünk: a C++ ismerete nem csak a Java vagy a C.. elsa-
játítása során válhat el6nyünkre, hanem az awk, a Perl na és persze a C tanulása köz-
ben is. A legfontosabb lényez6 ezen a téren az, hogya C++-sza l olyan hordozható kl:-
pességeket sajálíthatunk el, amelyeket aztán mindenféle platformon, és az informatik:li
piac legkülönböz6bb területein kamatoztalhatunk, legyen lZ PC, valamilyen nagy telje-
silmén}'Ű Unix rendszer, vagy egy egész adatcentrumot megtölt6 mainframc.

Lehel, hogy a kód, amit írtunk nem lesz hordozható, vagyis nem Tudjuk változtatás nél-
kül átvinni egyik rcndszcrr61 a má~ikra , de a rudás, amit állllla megszerezlünk, biztos:'ln
az lesz.

A könyvben használt jelölések


A könyvben a kövctkez6 speciCIlis jelöléseket fogjuk használni.

Az ilyen jelzésú szövegdobozok olyan információt tartalmaznak. amelyek birtokában


hatékonyabb kódot tudunk írni, vagy mi magunk hatékonyabban tudunk dolgozni.
I
Tanuljuk meg a C++ programozási nyelvet 24 6ra alatt xxi

Figyelem!
Az ilyen jelzésű keretes részekben olyan problémákra vagy mellékhatásokra hívjuk
fel a figyelmet, amelyek bizonyos helyzetekben gyakran felmerülnek.

TudIa hogy...?
Az. ilyen dobozokban tippeket, trükköket mutatunk be, illetve az aktuális témához
kapcsolódó kiegészítő információkat közlünk.

Helytele.
Az ilyen táblázatokban tömören Helytelen, ha figyelmen kívül hagy-
összefogla ljuk mindazokat a tanácso- juk ezeknek a felsorolásoknak a tar-
kat, aranyszabályokat, amelyek talmát.
az é ppen tárgyalt témához kapcso-
lódnak. Ilclyes tehát, ha odafigye-
Jü nk ezekre.

Ha ezt a szimbólumollátjuk, akkor a melletre levő szöveg vabmilyen éppen bemuta-


[ott program kimenetét tartalmazza.

Ez a jel mUlat ja az éppen tárgyalt példakód elemzésének kczdetC:t.

A könyvben ezen kívül különféle berutipusokat is használunk:

• Hogy könnyen meg lehessen különböztetni a folyószövegec a kódoktól, utóbbi-


akat speciális monospace betúkészlettel szedtük. Az olyan szavakat és karakte-
reket, amelyek a kód részét képezi, de hivatkozás végett a foly6szövegbcn is
szerepelnek ugyanennek a betútípusnak a dőlt változatával írtuk.
• Az új vagy fontos fogalmakat szintén egy speciális betűtípussal jelöljük.
• A könyvben találhat6 valamennyi kódban megszámoztuk a sorokat I-Ia tehát egy
sor elejéről mégis hiányzik a sorszám, az annak a jele, hogy átl6g oda az e l őz6
sor, vagyis amit látunk, az csupán egy sor folytatása. Ilyenkor a két sort begépe-
lésnél természetesen egyesíteni kell, vagyis egy sorba keU őket gépelnünk.
I
xxii Tanuljuk meg a C++ programozási nyelvet 24 óra alatt

A CD tartalma
A könyvhöz mellékelt CD-n a következőket laláljuk:

• V,llamennyi a könyvlX!n szereplő fomlskódol.


• Egy szabadon használható integrált fejlesz16 i környezetet ODE) és
fordítóprogramol,a Borland C++BuilderX-el. Bár a könyvben sLereplő vala-
mcnnyi kódot kipr6bálluk ezzel a fordft6programmal, fontos megjegyezni, hogy
maga a kötet nem e rről a termékről sz61. A bemutatott kódokat bármely stabil
C++ fordít6valle lehel fordítani. Szintén fomos hangsúlyozni, hogy nem minden
fordítóprogram kell6cn friss, vagy hű az érvényben [évei szabványokhoz, így
egyes k6dokkal helycnként és id6nként gondunk lehet, miközben a többi kifo-
g5swlanul maködik. Mivel a piacon számtal:m fordít6program és azoknak mcg-
számlálhatatlan változata 1(;!111CtÓ fel, természetesen nem v511alkozllatunk ezek
teljes áttekintésére.
• Az 6rák végén szerepl6 gyakorlatok közül számosnak a megoldásál.
I. RÉSZ

Bevezetés a C++
programozási nyelvbe
1. óra Kezdö lépések
2. óra Egy C++ program részei
3. óra Változók és állandók
4. óra Kifejezések és utasítások
5. óra Függvények
6. óra Vezérlési szerkezetek
1. ÓRA

Kezdő lépések
Ebben az 6rában a következő kről lesz sz6:
• Hogyan telepítsünk egy fordítóprogramot és hogyan haszn:í ljuk azt
• A C++ nyelvt1 programfejlesztés lépései
• I !ogyan gépeljük be, fordítsuk le, és linkeljük első működé CH programunkat

Elókészülelek a programozáshoz
A C++ nyelv filozófiája - akárcsak a löbbi objektum -orientált nyelvé (ilyen például a Ja-
va iS) - , megköveteli, hogy a munka megkezdése előtt tervezzünk. Az olyan egyszeru
fe!ada tok persze, amilyenekkel a könyv első néhány fejezetében találkozhatunk, nem
igényelnek túl nagy előkészületeket. Az olyan összetett feladaloknál azonban, ;'l!nc-
lyekke1 egy igazán profi programozó nap mim nap találkozik, a tervezés már létfontos-
ságú. Minél részletesebb a terv, annál zökkenőmentesebb lesz a feladat megvalósítása
a megadott határidőt és a kölL<;égkeretet is figyelembe véve. Egy jó terv ezen kívül segít
a hibalehetőségek rninimalizálásában és megkönnyíti a későbbi karbantartást is. Becs-
lések szerint a szoftverfejlesztés költségeinek 900/o-át a hibakeresés és a karbantartás
adja . A megfelelő tervezés nagy mértékben csökkenti ezeket a költségeket, amely az-
tán a teljes projekt költségvetésén is meg fog látszani.
41J. rész · Bevezetés a C+ + programozási nyelvbe
Egy program tervezésekor az első kérdés, amit fel kdl tennünk a következő; Mi
az a probléma, amil meg szeretnénk oldani? Minden probléma megoldásához meg-
felelően behatárolt célt kell kitűzni, mint ahogy ezt a könyv legapróbb példáinál is
megtesszük.

A második kérdés, amit minden valamire való progmmozó feltesz magának: Megoldha-
fÓ ez a/dadal eg)'edi program írá.sa nélkül is? Egy régi program újrah'lsznosítása, át-
dolgoz,ísa, vagy egy már meglév6 kereskedelmi szoftver sokszor jobb megoldást nyújt-
hat egy problémára, mim mindent teljesen elölről kezth:::ni. Aki ilyen alternatívákat kí-
nál ügyfelei sz:hnára, sose mamd munka nélkül. Egy köllSéghatC:konyabb megoldás ma
kulcs lehel egy később elénk !.áruló ajtóhoz, ajánlólevél t:gy jövőbeni mt:gbízáshoz.

Hl! mégis új szoftver írásába vágjuk a fejszénket, a költségek akkor is jelemősen lefa-
mgllat6ak, amennyiben felhaszn:'iljuk a már korábban megírt fi.lggvénykönyvtárakat és
oszt(tlyok:tt (objektumdefiníci6k könyvtÍlra). A fejlesztés sebessége ezekkel radik,íHsan
növclht:t(5, amely részben magyarázat cl C++ népszerO"ségére is.

Tudta hogy, ..? IBM - OeveloperWorl<s portál


Noha az IBM DeveloperWorks portál- http://www.ibm.com/developerworks/ - el-
sősorban a Java-hoz nyújt segítséget, mutat néhány remek példát a függvény-
könyvtárak megosztásával kapcsolatban is,

Feltételezem, hogy az olvasó megértette a megoldandó probitmái és az mindenképp


új szoftvert igényel. Vágjunk hát bele a tervezésbe.

A C++ nyelvjárásai (C++. ANSI C++. ISO C++ és a Windows)


C++ egy programozási nyelv. A Windows, a Unix (mint például AIX, Solaris, HP-UX,
stb.), a Linux , és II Mac OS X ezzel szemben operáci6s rendszerek. Bizonyára ;IZ olvasó
úgy szeretné megt:tnulni a C++-t, hogy késo"'bbi programjai architektúrát61 és operációs
rendszertől függetlenül működjenek.

A TlnLlljuk meg a C++ programozási nyelvet 24 óra alatt dmű könyvben ennek megfele-
lően nem tételezünk fel sernrnit a használt operáci6s rendszert illetően. A könyvben
az ANSI/ISO C++-t - vagy más néven a szabványos C++ nyelvet - tárgyaljuk. Ez egy
nemzetközileg elfogadon változat, meJy bármely architektúrán és bármely fejlesztői kör-
nye7.etben használható.

Aprop6 Mi az ANSI? Mi az ISO?


Al ANSI az Amerikai Nemzeti Szabványügyf Intézet (American National Standards
Institute) rövidítése, míg az ISO a Szabványosítás Nemzetközi Szervezetéé
(International Organization for Standardization). Ahogyanevükb ől sejth ető, a szab-
l, 6ra • Kezd61épósek 15
ványosításért felelősek az Amerikai Egyesült Államokban és az egész világon.
A szabványokon nyugvó nyelv használatának nagy előnye, hogy mindenki ugyanazt
a leírást és ugyanazokat a szabályokat követi.

_-\ könyv példaprogramjai az. ANSI/ISO C++ szabványt köve tik és csakne m bármelyik
fordíl6 programmal le kell tudnunk fordítani őket. Keveset hivatkozunk majd ablakok-
1"3, listadobozokra, grafikára és ehhez hasonló dolgokra, I::zck ugyanis nagy mértékben
ruggnek az operációs rendszert61 és a használt fejleszt6i környezeuől.

A végeredményt mindig az alapértelmezett kimeneten kapjuk. Hogy ez mOködjön, való-


szim11eg közölnünk kell a fordítóval, hogy kamkteres felületre kívánunk alkalmazást írni.
Ez a helyzet a Ha rland C++l3uildcrX-szd is. Néhány fordító - többnyire a grafikus felü le tre
írtak (\Vindows, Mac, stb.) - ezt gyors (quick window) vagy egyszeru (simple window)
ablaknak nt:vc:.::i, esetleg egys:.::eníen csak konzolos alkalm:lZ.áské nt hivatkozik tiL

A könyvben mi mindvégig l11:lgát a fordít6p rogr:l mot (compi ler) hasznCIljuk. E:.:: a z
a progmm, ami lefordítja az emberek számára olvashat6 forráskódot gépi kócinI. A vég-
eredmény egy tárgykóclú állomány (object file) lesz. A linker szir,t{:n I::gy program, mely
a tárgyk6dú ál!om:'inyokat összef(izve hozza létre magát a futtatható prognunot.

A könyvhöz tartozó CD-n találunk egy ilyen fordítóprogramot. A következ6 részben Jé·
pésr61-1épésre bemUIatjuk, hogyan kell telepíteni a Borland CH Builde rX-eL Ez egy
Windows alapO integrált fejlesztői környezet (Integr:lted Development Environment -
IDE), mely szolgá ltatásaiv<il megkönnyíti a fejlesztést, hiba keresést, slb. Termés:.::etesen
használhatunk más - akár Windowst61 független - fordítót is. Ez csak r'.ljlunk áll.

A fordftó telepítése és beáUftása


A könyvhöz mellékelt CH BuilderX integrált fejleszt6i kö rnyezet Microsoft Windows
operudós rendszer alatt futtatható.

Amennyiben ne m ilyen környezetben dolgozunk, nyugodtan ugorjuk át ezt a részt. Eb-


ben az csetben ke resnünk kell egy, az operációs rendszerünkhöz elC:rhet6 fordítót.
Az operációs rendszer dokumentációja vagy a szállító cég további részletekkel szolgá l-
hat a használható fordítóprogramo kkal kapcsolatban.

Ha Windowst használunk , telepítsük a Borland C++ fordítót és a hozzá tartozó fejlesz-


tői környezelet. A könyv ábráinak készítéséhez mi is ezt használtuk. Maga a gmfikus
felület sokaknak ismerős lehel, hiszen találkozhattunk vele Borland Compiler, Borland
CH BuilderX, CHBuHderX, vagy C++BuilderX IDE néven is, dc ez valójában mind
ugyanaz a te rmék.
s i l. rész· Bevezetés a C++ programozási nyelvbe

Telepítés a CD-rol
Helyezzük be a CD-t a meghajtóba és várjunk, amíg elindul az automatikus telepítés.
Ha el nem történne meg, indítsuk el a CD-n találhat6 install . exe programot.
Az install. html és a readme . txt további információkkal szolgál a program legfris-
sebb kiadásáról. Amint elindult a te1epít6program, kövessük az alábbi lépéseket:

l. Kattintsunk a Next gombra.


2. Fogadjuk el a Fclhaszn:íl :ísi feltételeket.
3. Kattintsunk a Next gombra (hagyjuk alapértelmezeuen).
4. Válasszuk a Full Install Sel pontot (Teljes telepítés), és kattintsunk a Next-re.
5. Válasszuk ki a telepítés cél könyvtárát (az alapértclmczctl val6színl1leg megfele-
16) és kattints~l/1k Next-re.
6. Válasszuk ki az ikonok helyét (aL alapértelmezett valószínű l eg itt is megfelel6
lesz) és kaltintsunk a Next-re.
7. Végül pedig kattintsunk az Install-ra. Várjuk meg amíg a telepítés be fejeződ i k.

Természetesen a progr-drnunkat még regisztrál ni is kell. A Borland-tól egy állományt


kapunk, melyet be kelJ másolnunk a program könyvtá rába (egyszen.íen kövessük az e-
mailben kapott egyszen.í lépéseket). Ezzellulajdonképpen máris használható a prog-
ram, de cé lszerű még elvégeznünk rajta néhány beállítást.

A Borland C++BuilderX fordftó b.állftása


Tndílsuk el a C++BuikJe rX-szct, .1 köveLkez6képpcn: kattintsunk a Start gombra, majd
a Minden program pontra. ILL keressük ki a Borland C++ 13uilderX pontot majd legvé-
gül kattintsuk a program ikonjfl r-.I. Ha mindent jól csináltunk, akkor elindul a fejlesztői
környezet. Most az alapbc{llIításokkal fogunk foglalkozni, amelyeket természetesen
bármikor módosíthatunk :1 menüsorban található Tools menüponlra kanimva.

Az l. l -es ábdra pillantva látható, hogy a menÜS{)f alatt két sorban helyezkednek cl
gombok. Ez al eszköztár. Az egénnutatót az egyes gombok fölé húzva rövid leírást ka-
punk az adott gomb f\Jnkciójll r61. Az fels6 sor els6 hét ikonj{lVal az alábbi mtiveleteket
érhetjük el balról jobbra haladva: új fájl létrehoz{)sa (iu készíthetünk új proje kteú, [5 jl
megnyitása (létező projekt megnyitása), fájl ism(:telt megnyitása (a lemezen található
állomány beolvasása), forrás bezárása, aktuális fájj mentése, az összes fájl ment{!sc, il-
letve a nyomtatás. Az 10101 feliratú fájl ikonra kaUintva fordítha tjuk le a forrást és ké-
szíthetiink futtatható állományt, a zöld színű nyíl pedig elindítja az elkészült progr.a-
mot. A gombok megFelel6it a menüsorban is megtalálhatjuk rövid keresés után.

Számos ikonnál szerepel a projekt szó. A projekt egy csomag, amely összefog több
egymáshoz taltozó ~!lományt. A C++BuilderX .cbx ki te~es7.téssel menti a projektfájlo-
kat. A legtöbb a könyvben szerepl ő projekt csupán egy állományból áll, ami kissé ér-
I
1. 6ra • KezdI lépések J

Lelmctlcnnck tt1nhet. Ugyanakkor több tucat vagy akár több száz forráskódállománnyal
rendelke:d:i projekt esetén igen hasznos ez a szolgáltatás, tehát érde mes mcgtanulni
a használatát.

-•
...
; ~··~Il~"K [lDo<:uo><"" .od"''''~,k'p", .. ,,/C.... ''l~pllr,''l <PV
,- ,,-
t~~;[d< :;0.0'" !"" [Tolo<t !\'" 1. .. "'1"''''
o~et·~·OA ~
11:-..""",-:ld-· -
..... ~
, ~
- 'II M.
"'. ~- ..
~~.,~-~.ol ~~- .. ii]FioI,'L-

~ ,-

~~ L.'"
~' -

,.
---
i
,..,,,'"~.
- l -,T - - ", . ~ • o. •

1.1 ábra
, \ C++BuilderX hllcgráll fejlesztői Mrnyazel (IDE)

A C+ +BuilderX~ben alapértelmezésként be van ka pcsolvn a hibakeresés lehetősége. Mi


eZL a könyvben csak később fogjuk haszná lni, de addig is érdemes vele megismerkedni.

A Borland fejlcszrókörnyezete beépített szövegsze rkesztővel rendelkezik. Kattintsunk


az Új fáj l ikonra, majd pedig a Source File (forrásfáj D fülre. Ez a szolgáltatás egy fájl
szerkesztésére szo lgál. Több állományesetén szinté n az Új fáj l ikonra kell kattinta-
nunk, azonban e kkor a Console Application-t (szöveges alkalmazás) kell választanunk,
melye t a Projekt fülön találunk. Ha használjuk a varázslót, akkor az is elkészíti szá-
munkra a ke retrendszert. Ha azonban kikapcsoljuk, akkor egysze rű forráskódszerkesz-
tó ablakot kapunk.

Mentsük a projektet és minden hozzá kapcsolódó ál!ományt egy külön könyvtárba.


A projektállomány . cbx kiterjesztésű, míg a CH forráskódot taltalmazó állományok
végződése. epp. Érdemes megjegyezni, hogya szerkesztő mentéskor automatikusan
. cpp kiterjesztéssellátja el a forrásáIlományokat. Az éppen szerkesztett fájl mentéséhez
kauintsunk az aktuális fájl mentése ikonra.
a l I. rész· Bevezetés a C+ + programozási nyelvbe

Fordítás a Borland C++BuilderX-szel


A projekt va!,')' egyedi fájl fordításához és a bináris állomány összeszerkesztésf:hez kat-
linL'mnk a Make Projekt ikonra. Néhány fordító ezt a OliTveletet két lépésben o ldja meg:
d6bb fordítanunk kell aztán szerkeszteni. A legtöbb integrá lt környezet ugyanakkor eZt
már egy lépésben hajtja végre. A program - fejlesztői környezeten belüli - futtatásához
kallinLsunk a Run Project ikonm, hibakereséshez haszn;íljuk a Debug Project ikont.

Ha egy programot hibakercs6vcl fUllalunk , akkor az 1.2 ábrához hasonl6 kép fogad
minket. Ebben az esetben a hetedik sorra á!lítot!am be és egészen odáig lefutott
a program.

r.,: t . ~"'I~.,~ C
I~"":--~!I-~'"
tH"e:i - '!il."ilio ....
..
..,..... ,-., .-
1000;....,"" . "' Iio "I"""""""kbp,.lt<ltd<"",,""b~ <pp
,
~.
~
. ;, .. . . ~-
h@!E

(It""'" .....

-
I-",~ .iit..

8:::.
'"
~" /\1, ID !!!I-..."'"
'."
'._< .. ._
x·...., ... ;
----...-.
• . ... L... « .......,.
.

•• - _ ...... _ • .....0 ~
... -c >ot ..... <Mr •

... ..... 0'


"1'11 1

-
-- ",
~.:.J'-'I
.- 'ol , ~
••
' 1'

-"
~

li ,,'
• . - II
••. , . .s 'a "'. . c_. . ,. . ,. .___. . '. . . , ._-_., -
_
~
"";'~~
---

1.2 ábra
A Borland C++BuildO/X ltilxlA,'(Jr(!Sője

Am int a képerny6nkön megfigyc1het6, a barna csík jeh:i, hogy melyik le.5z a következő
programsor, a piros kör pedig maga a töréspont. A Löréspontnál ideiglenesen megáll
a program futá sa. Hibakeresés esetén érdemes már a legelső gyanús sornál töréspomot
elhelyezni. TöréspontOl :IZ adott sor bal szélére kattintva helyezhetünk el. A 5tep ikon
segítségével egy sol1léphetünk előre a futtatásban , míg a Continue ikon folytatj ..
a program futtatását a következő töréspontig vagy a program végéig. A többi ikon se-
gítségével más·más szemszögből vizsgálhatjuk a programunkat. Ezek az ikonok az ab-
lak a ljánál találhatóak, Hasznos szolgáltatása ennek a környezetnek az automatikus for-
dítás és szerkesztés, amennyiben ezt futtat.'ís vagy hibakeresés e16tt nem tettük meg.
,. óra • Kozd6 lépések 19
A Help menü Aboul menüpo ntjában található a fejlesztői környezet verziószáma, mely
1.0.0.1786. A Help menü Help Topics (Súgó témakörök) menüpontjában kimerítő C és
C++ refer enciát ta lálunk. Érdemes kö rülnélni itl is.

Tudta hogy...? Kömyezetfüggó ság6


A bemutatotl fejlesztői környezet egyik hasznos tulajdonsága a környezetérzékeny
súgó. Kattintsunk egy kulcsszóra és nyomjuk meg az F1 gombot. Egy új ablak nyílik
meg információkkal az adott parancsról.

A fordft6program és a szövegszerkesztó

Egy fordít6progmm gyakran saját beépített szövegszerkesztővel rendelkezik. C++ állo-


mányok szerkesztC:séhez használhatjuk eZt, vagy bármely ker<:!skedelmi szerkesztőt, dc
akár egy irodai programcsomagot is. Mim .I%t már korábban említellük, a Borland for-
dítója rendelkezik beépített szerkesztővel. 13ánn ilyen szövegs;:.erkeszt6t is vá lasztunk,
fontos, hogy tudjon formázás nélküli, egys;:.eru s;:.öveget menteni. Garantáltan haszmí l-
ható szerkesztő például a Windows jegyzeuömbje, a DOS-os EdiL parancsa, a Brief,
<IZ Epsilon, az EMJ\CS és a vi. S;:.ámos irodai szövegszerkeszt6 - WordPerfect, Word,
stb. - szintén lehetőséget ad egyszeru szövegfájl kéne történő mentésre.

Figyeleml Legyünk 6vatosak


Amennyiben irodai szövegszerkesztöt használunk, fontos, hogy formázás nélküli ál-
lományként mentsük a kódot, mert bár mi el tudjuk olvasni, a fordítóprogram nem
tud majd vele mit kezdeni.

A szövegszerkeszt6vel készített állományok forráskódok és C++ nyelvben. cpp, . exx,


. ep vagy. e kiterjesztéssel bírnak. A könyvpéJdáiban szerepl ő állományok kiterjeszté-
se . epp, hiszen el az alapértelmezett a Borland fordítójába n.

Apropó Forrásk6dok kiterjesztései


A legtöbb c ++ fordító nem törödik a kiterjesztéssel, de amennyiben nem állítot-
tunk be mást, alapértelmezés szerint jó néhány . epp-t fog használni.
10 II. rész · Bevezetés a C+ + programozási nyelvbe

Használjunk egyszeru szöveg szer- Ne használjunk olyan szövegszer-


kesztésére képes szerkesztőt vagy kesztőt, amely speciális formázóka-
a fordító beépített szerkesztőjél. raktereket is ment. Ha mégis szeret-
Használjuk ki a szerkesztőp rogramok nénk ilyent használni, akkor minden-
nyújtOll<l előnyöket. képp ASCII szövegként mentsük
A forrnsk6dokat . c, . ep, vagy O ki- a forráskódot.
tcrjesztések valamelyi kével mentsük
eL (A Borland fordítójánál . cpp
az alapértelmezett.)
TanulmányozllIk át az általunk hasz·
n(t!l fordítóprogram és linker doku-
ll1!.:nt{u..:ióJát.

Fordftás és linkelés
Noh:1 ,I forrás kód számos ember szi'lmára kicsit kínaimik tűnhet, azért tagadhatatlan,
hogy emberi szem számára oh'asható szöveg. A forráskód tehát nem program, közvet-
lenül nem futtatható.

Apropó Fordftás a parancssorból


Hogya rorrásk6db61 ruttatható programot készItsünk, szükségünk van egy fordítóra.
A paraméterezési m6d fordftónként változó, igy részletekkel a program dokumentá-
dója szolgálhat.
Ha parancssorból kívánjuk futtatni a fordítót a fejlesztői környezet helyett, akkor
a használt programtól és operációs rendszertől függ ően az alábbiakat kell begépclnünk:
Borland fordító esetén - bcc32 <fájlnév> -o <futtt!tható állomány neve>
GNU esetén - gxx <fáj lnév> -o <futtatható állomány neve>
Cygwin fordftónál- gH <[ájlnév> -o <futtatható állomány neve>
Linux alatt - 9H <fájlnév> -o <futtlJthlltó állomány neve>
UNIX alatt - CH <fá.jlnév> - o <futtatható állomány neve>

Fordrtás az integrá~ tejleszt6i környezetben

A legtöbb modern fordít6 integrált fejlesztői környezetet is nyújt. Egy ilyen környezet-
ben a Build vagy Compile menüpont kiválasztásával vagy a megfelelő billentyűkombi­
náci61enyomásával érhető el az alkalmazás fordítása. A C++BuilderX-ben egyszeruen
kattintsunk a Make Project ikonra.
1. óra • Kezdő lépések I II

Linkelés (összeszerkesztési
Miután lefordítottuk a forráskódot, létrejött a tárgykód. Ez még mindig nem fuUatható .
Ahhoz, ho.gy futtathatóvá tegyük, le kell futtatnunk a linkert.

Egy tipikus - C++-ban írt - futtatható program egy vagy több tárgykódú állomány és
egy vagy több függvénykönyvtár összeszerkesztésével, úgynevezett linkelésével jön
létre. Függvénykönyvtár származhat a fordítóprogram készít6jétól, külső forrásból, de
írhatunk ilyent akár mi magunk is. Maga a fü ggvénykönyvtár hasznos függvények, eljá-
rások és osztályok gyűjteménye, amelyekre hivatkozhatunk a programunkban. A függ-
vény egy olyan kódrészlet, amely valamilyen konkrét feladatot valósít meg. Összeadhat
példáu l két számot, vagy kiírhat valamit a képerny6re. Az osztály egy új típus definídó-
ja. Egy osztályban az adat és a rajta végrehajtható műveletek egyaránt jelen vannak.
A könyvben később mHjd részletesebben megismerkedünk az osztályokkal.

Egy futt<1tható állomány létrehozásának tipikus lépései a következők:

1. Forráskód létrehozása és elmentése . cpp kiterjesztéssel.


2. A forráskód lefordítása tárgykódd{t
3. A t,írgykód és b{trmely más - a futtatásl10z szükséges - fuggvénykönyvtár össze-
szerkesztése futtatlmt6 állománnyá.

A Borland fejlesztői környezetében a Make Project fordít és linkel egy lépésben. Egy-
egy modul kü lön·kÜJÖn is lefordítható, ehhez kattintsunk a jobb egérgombbal a projekt
ablakban a modul nevére, majd a Build Tools-n<'il válasszuk ki a BCC32-t (mely maga
a C++ fordító). Amennyiben a Iinkelést szeretnénk saját kezűleg végrehajtani, kattint-
sunk jobb egérgombbal a projekt nevére, majd a ·13uild Tools-nál válasszuk ki
az ILINK,,2-t (ez a fejlesztői környezethez mellékelt linker)

A fejlesztési ciklus
Ha minden program első próbálkozásra tökéletesen működne, akkor a fej lesztési cik-
lus egyszeruen így nézne ki: programírás, a forráskód lefordítása, linkelés és futtatás .
Sajnos azonban csaknem minden prognlm - még a legegyszerűbbek is - tartalmazhat-
nak és tartalmaznak is hibát. Néhány probléma már a fordításnál jelentkezik, egyesek
a linkelésnél, de olyan is szép számmal akad, amelyekre csak a program fu ttatásakor
deru l fény.

nSyntax error" (nyelvi hiba) jellegű hiba fordításkor jön ehi Ez azt jelenti, hogy hibát
vétettünk a nyelvtanban. Találkozhatunk ezen kívül "warning" (figyelmeztetés) je llegű
hibával is . Ilyenkor elképzelhető, hogy tényleg hibáztunk, de a fordító el tudta készíte-
ni a tárgykódot, men valahogyan értelmezte a hibás kódot is, csak épp gyanúsnak ta-
12 11. rész • Bevezetés a C++ programozási nyelvbe

lálta. Linke lési hibaüzenete t leggyakrabban hiányzó függvénykönyvtá rak va!,'Y elg6pelt
függvénynevek okozhatnak, Azok a hibák, ame lyekkel a program fuUatása közben ta-
lálkozunk, logikai hibák,

Teljesen lényegtelen, hogy milyen jellegű hibába botlunk, ki kell javítani a forráskódot,
utána pedig újra le fordítan i, újr.! kelllinkelni és újra le kel! futtatni az alkalmazásun-
kaL Ezt a folya matot mutatja be az 1.3-as ;íbra. L1that6, hogy in egy ciklusr61 van szó.
Nagyon ritka az a program , amelyik elsőre hibátlanul fordítható, linkelhető és ráadásul
még hibátlanul is fut.

Start

Igen Fordftási
hibák?

Igen Futási
'----'-<. hiba?

N,m

Kész

1.3 ábra
A C++ program/ej/esz/és lépései
1. óra· Kezdő lépések 113

HEllO.CPP - Az elsó C+ + programunk


A C-vel rokonságban álló programozási nyelveket oktató könyvek körében már hagyo-
mány, hogya Hello World szavak kiíratása az első program. Ezt a hagyományt - me-
lyet Kernighan és Ritchie C nyelvről szóló könyve indított el - mi scm kívánjuk meg-
szakítani.

Apropó Új program létrehozása


C+ +8uilderX-ben új projekt létrehozásához kattintsunk a New Source File-re (új
forrásáIlomány létrehozása). A könyv példáihoz mindig a Console Application-t (szö-
veges alkalmazás) válasszuk. A projekt létrehozása után elkezdhetjük a munkát
a szerkesztőablakban.

Gépeljük be dZ első prog,dmot betűről hetűre a szövegszerkesztőbe. Ha meggyőződé­


sü nk szerint nem vétettünk hi b~t, mentsük, fordítsuk le, linkeljük össze - amennyiben
a fordít6 ezt nem teszi automatikusan - és pr6báljllk meg elindítani. Képernyőnkön
II J Jell0 World szavak jelennek meg. )elenesetben nem maga II program a lényeg, ha-
nem az, hogya fejlesztési ciklust és a fordítót megismerjük. Pár lecke után teljesen vilá-
gossá v{tlik majd a program m űködése.

Apropó Néhány szó a könyvben található forráskódokról


Az alábbi forráskódban a sorok az adott sor számával kezdődnek . Ezek csupán hi-
vatkozások megkönnyítése végett vannak ott. Az 1.1-es lista O. sora például így néz
ki begépelv"e:
#include <iostream>
A sorszámok tehát megkönnyítik a magyarázó szöveg értelmezését. Egyes fejlesztői
környezetekben - ilyen például Borland is - szintén találkozhatunk sorszámozással,
de ezek nem részei a forráskódnak.
A könyvben minden program sorszámozása a o. sorral kezdődik, mert maga
a C++ nyelv is O-ról kezd a számozást. A korábban bemutatott fejlesztői környe-
zetben a számozás 1-től kezdődik, de ha ezt valaki zavarónak találja, nyugodtan ki-
kapcsolhatja. Ezt a lools menü Editor Options menüpontjában található Une
Numbering jelölőnégyzet kikapcsolásával tehetjük meg.
A könyvben található összes forráskód a mellékelt CD lemezen is megtalálható,
mégpedig a programot bemutató blokk fejlécében szereplő néven.
14 11. rész· Bevezetés a C+ + programozási nyelvbe

\.\ usta - hallo.cpp. az eII6 C+ + _ _


o: 8include <iostrcaffi>
1.

,.
2 , int main()

4: std : : cout « "Hello World!\n";

,.
5: ret.urn O,

Gy6z6djünk meg róla, hogy hibátlanul gépcltük be a kódot, különös tekintettel az spe-
ci{llis ír.isjdckrc. A 4. sorban találhat6 « egy átidmyítást jelképez. Angol kiosztású bi!-
lcntyuzcten nyomjuk le egyszerre a Shiftet és a v(.!ssz6t. (Magyar kiosztás esetén hasz-
nMjuk az AltGr és a J ~ billenty{ikombináci6t.) A 4. sort pomosvesszO:vel fejezzük be, fi-
gyeljünk rá.

Apropó Nevek és névterek


Az std : : cout parancs két részból áll: std és cout.. A cout -tal frath atunk ki va·
lamit a képernyőre. Ez része az alapcsomagnak. Minthogy azonban sok ember ír új
fü ggvényeket és objektumokat a C++-hoz, könnyen előfordul, hogy ugyanazt
az azonosltót használják más célra. Hogy ezeket megkmönböztessük, meg kell ad-
nunk a használni kivánt névteret vagy változatot is. Jelen esetben tehát Igy közöl·
jük, hogy az std csomagban szerep lő cout objektumot szeretnénk használni.

Fontos, hogy pontosan a fordítóprogram dol.:umentáci6jának megfelel6en járjunk el.


A legtöbb fordító - így a Borlandé is - aUlomatikusan összeszerkeszti a t.'ÍrgykMot-
vllgy tárgykódokat - futtatbató álJománnyá, de ha más fordítót használunk, minden-
képp olvassuk el a leírásál. Ha hibaüzenetet k:lpunk, nézzük meg alaposan, ho&')'
ugyannt gépdtük-c be, mint ami a korábbi l.l-es progmffi k6djában látható. lIa nem
a l3orhll1d fordít6ját használjuk, további hibaüzeneteket k:tphatunk. I [a O. sorra a rend-
szer a ~CannOl find iostream hibát adja fordításkor, akkor II fordító nem talál ja a meg-
H

felelO: fejlécállornányt. Ennek a helyét a fordító konfigurádós á!1ományáhan vagy kör-


nyezeti vá ltozók segítségével állíthatjuk be. Ha a fordító II 2. sornál nem találja
a main () fi.lggvény prototípusát, akkor egy int ma i n () ; sort kelJ a második sor elé
beszúrni Ebben az esetben ezt a könyv összes példájánál meg kell ten nünk. A legtöbb
fordító nem igényli ezt (a 130rland sem), de néhány igen.

A módosítás után így néz ki a programunk:


o: tinclude <iostrearn>
1.
2 : int main() ;
3 : int main!)
4: {
5: std :: cout « "Hello World!\n';
6: return O;
7.
1. óra • Kezdő lépések 115

Próbáljuk meg lefordítani a hello. cpp állományt és fu ttassuk le. Ha mi ndent jól csi -
náltunk, kiírja a képernyőnkre hogy:
He l lo Wo rl d!

lia sikerülL, akkor gratulálok, sikeresen begépeltü k, lerordítottuk és lefuttattuk első


C++ nye1vű programunkat. Szinte hihetetlen, de csaknem minden profi C+ + programo-
zó s7:ámára ez volt az első program.

Fordftási hibák
Számtalan oka lehel a fordítási hibáknak. A leggyakoribb az e!gépelés vagy más apró
hiba . Néhány fordító nemcsak a hibára mut.1t rá, de arra is, hogy az a forráskódban hol
van. Néhfiny akár még javítiisi tanáccsal is szolgálhat

Nézzük meg, hogy reagá l a fordít6tlk egy sZ{lI1dékosa.n elhelyezelt hibám.


Ha a HELlO.CPP hibátlanullefutot! , (Igy n(:zzük meg, mi történik, ha töröljük a 6. sorbrm
található kapcsos zárójelet. A programunk az 1.2 programlism mása kell, hogy legyen.

1.2 Ua.. - Példa a forditáai hibára (hallobad.cppl


o : #include <iostream>
1.
2 : int main()
3.
4: std : : cout « "Hello World! \n' ;
5: rctllrn O;

Fordítsuk újra a programot. Az alábbihoz hasonló hibaüzenetet fogunk kapn i:


"hellobad . cpp· : E2134 Compound statement mi s .s i ng } in function main (
at line 7

A hibaüzenet - noha kissé rejtjclesnek tűn ik - közli, hogya probléma a forráskód vé-
gén található. A 7-es sorra hivatkozik, hiszen az lenne a következő sor a szerkesztőnk­
ben . Sokszor a hiba közvetlen a probléma közelében található. Amennyiben a fordító
pontosan azonosít nlinden problémát, megpróbálja kijavítani forráskódot.

Apropó EmlékeztelÓ a könyv forráskódjaival kapcsolatban


Ne feledjük, hogy a könyvben a forráskódok sorai előtt található sorszámozás csu·
pán referencia. A számozás - akárcsak néhány fordító esetén - , O-ról indul. Más
fordítók - így a Borland is - viszont l -től kezdik a számozást.
1s l l. rész· Bevezetés 8 C+ + programozási nyelvbe

Kérdések és válaszok
Kerdés: Mi (l különbség az egyszefü szövegszerl..'eSZfŐ és az irodai szőIK!j!,szerh.'SZt6 Mzött?

Vá/asz: Az egyszeru szövegszerkesztőve l egyszenl szövegfájlt készíthetünk. Semmi olyan


speciális jel vagy forrn~Í7..ás nem kerül bele, amely az irodai szövegszcrkeszt6nél igen.
Az egyszenl szövegfájlok nem tartalmaznak automatikus sortörést, félkövér, illetve dall
betUket stb. Természetesen az irodai szövegszerkeszt6kkel is menlhetünk egyszen1 szö-
veg formátum az esetek nagy részében, de körtiltekint6en kell eljárnunk.

Kérdés: Afordít6m beépftcfI sz6vcgszerkeszfövel rendeU.tazik. llasználjam inkább az t?

VúfC/~'z: Csaknem minden fordító képes szinte bá rmilyen szövegszerkeszl{Svl::1 írt forr.1s-
kód lordítúsár:1 . A beépített sze rk esz tő előny e többnyire az, hogy felgyorsít ja a fo.::jlcsz-
t~si ciklust. Komolyabb fordítók összkomfortos szerkesz16felületle! re ndelkcwck, mely
a fejlcszt6i környezet elhagyása nél kül segít megoldani ;_ fordítási és tárgyk6dszerkesz-
tési problémák:lt, de sokszor találunk gyors segítséget is. Természetesen a kü lö nálló
szerkeszt6nek is megvmumk az el6nyei. Rendelkezhet példáu l olyan kiegészítésekkcl ,
amellyel a fcjleszt6 i környezet nem. De az is el6fordu lhat, hogy már rnegszoktuk és
hatékonyabb a 111lJnkavégzés vele, mint a beépített szerkcszt6vel.

Kérdés: FilJye/men kíVlllhagyhatom a fordít6 fi8yellllczleléseit?

Válasz: Nem . Swkjuk mcg, hogya figyehneztet6 i.lzent:tek is hibflk. A C++ fo rdítója
minden esetben szól, H111ikor o lyan dolgot cselekszünk, amely nem ajánlott. Tegyi.lk
meg a szükséges lépéseket, hogya figye lmcztctések is e1ttlnjenek. A hibaüzenet azt je-
lenti, hogya fordító a forráskMu nkat nem ILldja gépi k6ddá alakít.ani. A figyelmeztetés
azt jelenti, hogy sikerült ugyan lefordítani, de nem biztos, hogy úgy, ahogyan mi sze-
rettük volna.

Kérdes: Mil jeleni az, hogyfordirás; idö?

Válasz: A fordítási id6 az az id6, am ikor a fordító fut. A Iinkelési - vagy szerkesztési-
idő a<:, amikor a linker fuI. A fu tási idő pedig amikor maga a programunk működik.

Gyakorlatok
Ebben a szakaszban o ly:m kvízkérdéseket és gyakorl6feladatokal gytljtönünk össze,
melyek segítenek megszilárdít.ani az elsajátíton tudás!.
l. óra· Kezdő 17

Kvfl

1. Miért fomos projektet használni?


2. Mi a hibakeresés célja?
3. Miért érdemes olyan fordítót használni, me!y támogatja a nemzetközi
sZábványokat?
4. Milyen segédprogramokkal $zerkeszthet6 a forráskód?

Feladatok

A feladatuk 1t::hetóv~ teszik az önálló gyakorlást. Nincs egyetlen helyes megoldásuk.

1. Pr6báljuk ki a fejlesztő környezet gombait és menüit. Mit csinálnak? Könnyebbé


teszik az életet? Vagy éppen megnehezítik?
2. Pr6báljuk meg lefordítani, összelinkeini és lefull:!lni a fejlesztői környezethez
mellékell példákat. Vessünk egy pillantást a forráskódra, hogy kitaláljuk miért és
hogyan keletkezik a végeredmény.
3. Pr6báJjuk ki a Borland - vagy bármely más - fordító parancssoros változatát.
Könnyebb szerkeszleni, fordítani, linkelni, futtatni más eszközökkel? Vagy ~pp
az integrá lt fejlesztői környezet könnyíti meg a helyzetünket?

Válaszok a kvfzkérdésekre

1. A projekt összefogja a futtatható ,11lomClnyhoz szükséges komponenseket. Egy


aJk,llmazás sok-sok különböző f:íjJt ig~nyelhet , de projektbe összefogva együtt
kezelhetóek, útl{IlJmtóak.
2. A hib:lkeresés célja természetesen a hibák (bug) fe lkutatása. ilyenkor olyan logi-
kai hibákat keresünk, melyek a program fordítását nem befolyásolták ugyan, ele
az mégsem aZlleszi, amjt a programozó elképzelt.
3. Az egyszeru válasz: követi a szabályokat. A fordítótól elvárható, hogy minden
esetben ugyanúgy viselkedjen. Talán nem is gondolná a kedves O lvasó, de még
a benzinkutakról és az autók benzintankjár61 is szabvá ny rendelkezik . Ha min-
den gyártó saját feje után menne, akkor nem lenne biztos, hogy A gyártmányú
autóba B gyártmányú benzinkúton mdunk tankolni.
4. Bármilyen programot használhanmk, amely képes egyszerű ASCll szöveget
menteni. Használhat juk az operációs rendszerhez melléke[t szerkesztőt (DOS
alatt az edit, Uni..Vlinux alatt a vi és az EMACS, Windows alatt a Jegyzettömb),
kereskedelmi alkalmazást (mint például a BrieO, integrált fejleszt6i környezetet
vagy akár egy irodai szövegszerkesztőt is. A lényeg csupán az, hogy tudjon for-
mázás nélküli szöveget menteni.
2. ÓRA

Egy C++ program részei


Ebben az órában a következőkról esik majd szó:
• Mikor megfelel6 választás a C++ nyelv
• Milyen részekból áll össze egy CH program
• llogyan al kotnak egészet a részek
• Mik a függvényck , és mire használhat6k

Mit611ehet jó választás a C+ + nyelv


M;:mapság il legtöbb profi szoftverfejlesztő C++-ban dolgozik. Ennek elsősorban az az
oka, hogy ezen a nyelven gyorsan működé, mégis kis méretG programokat írhatunk,
ehhez pedig egy robus:ltus és különböző platformok között könnyen hordozható kör-
nyezet áll rendelkezésünkre. A ma használatos fejlcszt6eszközök segítségével kifejezet-
ten összetett és hatékony kereskedelmi alkalmazásokat írhanlOk viszonylag egyszeru-
en, ám ennek ára van: meglebetósen alapos ismeretekkel kell rencleJkeznünk a C++
nyelv szolgáltatásir61.
20 II. rész • Bevezetés a C++ progfllmoz.ási nyelvbe

Mfls programozási nyelvekhez képest a C++ viswnylag fiatalnak számíl. Persze az is igaz,
hogy maga a programozás is mindössze hatvan éves múltra tekinthet vis.<;za, de a C++
még ezen a skáltin scm számít réginek. Az első nyelv megszületésc óta a progrdllloti'ls
módszertana igen jelentós fejl6désen esett át, amelynek egyik lépfse éppen a C++ volt.
Ez a nyelv ugyanis történetileg a C nyelv továbbfejlesztésének tekinthető. Mivel pedig
maga a C is minJössze 30 éves, világos, hogy a C++ sem lehet ennél iJŐsebb.

fl.programozók kez<.!(!llx:n ,l létező legegys7.eruöb gépi urasításokkal, vagyis gépi kód-


ban dolgoztak. Ennek a nyelvnek az utasításai egyes(!k és nullák hosszú sorozataiból
állnak, ami érthető módon nem túl könnyen kezc1hel6. Hamarosan megszOlettek tehál
az els6 assemh1cr(!k, amelyek ember számám is olvasható reprezenlflciókat, úgyneve-
zett nmemonikokat rendeltek a gépi utasításokhoz. Ilyen volt például az ADD vagy
a MOV, amely(!k egy-egy konkrét gépi k{x.!ú mOvc1elnek fele l teth etők meg. Id6vel aztán
ki<llakultak az úgynevezett maglIs szintű programnyc1ek is, mint amilyen a BASIC v~lgy
a eOUOL. Ezek a nyelvek m(lr lehetővé tették, hogya progr.unozók olyan a szavl\k 6s
mondatok közé es6 kifejezések segítségével alkossá k meg progmmjaikat, mir1t p6kl(lUI
Let I = 100 (legyen az Tv~lt()zó tartalma 100). Ezeket:1 magas szinn1 rmlvdewkcl az-
tán az úgynevezett értelmez6k (inlerpereter) vagy fordítóprogramok (compiler) vissza-
fordították gépi nyelvre. Az interpcrclerek, mint például a I3ASIC első megval6sítá:,:ai
.olvasás közlx:n" fordítj:'ik le gépi nyelvre II magas szinn1 utasitásokat, majd azonnal
végre is hajtják azokal. Itt tehát a begépch kód gyakorlatilag lIzonnallefut.

A fordítóprogramok (compiler) ezzel szemben elóbb végigulv:lssák a teljes forráskó-


dot, és abh61létrehozn:1k egy úgyncvczctlt~rgyk6dú 511om(tnyt (objecl code). A feldol-
gozásnak ezt :lZ első lépését nevezik fordításmk (compiling). A fordító tehát egy l:írgy-
kódú állományt (object file) állft elő, amit ti második 1 6p~sben a szerkesztő (linker) ala-
kit full:ltllató progmmm:i. A futtatható progmm az az :illúmány, amit aztán az open'\ci6s
rendszer segítségével lefutlatharunk. (Zárójelben talán érdemes megjegyezni , hogy
aZ itt említett lárgyk6dnak (object code) semmi köze sincs a könyvben késoób tárgya-
landó objektumokhoz.)

Mivel az interprcterek közvetlenül végzik II gépi kódra alakítást és a végrehlljtást,


a programozó számára rendkívül kénye\mesen használhatók. fl. fordítóprogl1lmo k ez-
zel szemben elsőre kissé k6nyclrnetlenek, hiszen segíL~égükkel csak több l6p6sben ~I­
lítható e l ő a futtatható állom{my. H,l ;lzonban jobban megvizsgáljuk a dolgot, kiderül,
hogya kényelem elvesztése jelentős haszonnal is jár: az így létrehozott gépi kód sok-
kal gyorsabban fut, mint amilyen sebességgel az incerpreterek működni képesek.

A programozók legfőbb célja hosszú éveken át az voll, hogy kis méretú, gyorsan futó
programokat alkossanak. A programnak azért kellett kicsinek lennie, mert drága VOll
a memória, a sebesség és hatékonyság pedig azért volt alapkövetelmény, mert a h6s-
korban a számítási teljesítmény sem volt különösebben olcsó. Aztán ahogya számító-
gépek egyre kisebbek, gyors:tbbak és olcsóbbak lettek, és ahogya memóriaárak is
visszaestek, a progranúejle~ztéssc\ kapcsolatos prioritá~ok is megváltoztak. Manapság
2. óra • C++ részei 21

a programozó munkabére sokkal de sokkal magasabb, mim az ót foglalkoztató vállal-


kozás által használt bármelyik számítógép ára. Ennek megfelelően ma az igazi értékel
a jól megírt és könnyen karbantartható programkód jelenti, nem a hardver. A .jól meg-
irt" jelzőt bizonyára nem kell magyarázni, a könnyű k~lrbantarthatóság pedig :IZt jelenti,
hogy ha a szoftver szolgált:llásaira támaszkodó vállalkozás igényei megváltowak, ak-
kor a programot különösebben nagy beruházás vagy erőfeszítés nélkül át lehessen írni,
vagy szolgáltatásainak körél ki \chessen terjeszteni.

Procedurális, strukturá~ és objoktum-kÖ2pontú programfojlesztés

A procedurális vagy djár-ds központú fejlesztés alapelképzelése szerint a progr.un nem


egyéb, lrlint a bemenő adatokon végrehajtandó műveletek sorozata. A stmkturált prog-
ramozás nagy találm:1nya ezeknek a műveletcknek a rendszerbe szervezése, szisztema-
tikus megközelítése volt, mégpedig azz<l l a céllal, hogya programukk<ll nagy mennyi-
Séglt adatot is könnyen és átlátható módon lehessen kezelni.

Amúgy ez az ötlet egy:'iltalán nem új, hiszen ha jobban belegondolunk, semmi másról
nincs szó, mint az ~oszd meg és umlkodj" elv digitális alkalmazásáról. H:l tehát egy
olyan feladattal találkozunk, amelynek a megoldás.a túlságos:m összetett :lhhoz, hogy
egyszeruen és könnyen áttekinthető módon le tudjuk írni azt, akkor elkezdjük logikai
részfeladatokr-.t bontani, és ezt egészen addig folytlltjuk, :lmig a részek már kell6en
egyszerűe k és által:'inosak, és így könnyen érthet6ck.

Nézzünk egy pékh'íl. I-la az a feladat., hogy számít.~uk ki egy vállalat valamennyi :l lk:ll-
mazottjának áliagkeresctét, akkor a dolog elsőre meglehetősen összetettnek tűnik.
Ugyanakkor nl!mi gondolkodással a következ6 részekre bonthat juk a megoldandó
problémáI:

1. Kés:dtsilnk egy !istál, ami tartalmazz.1 az összes dolgozó bérét.


2. Számoljuk meg, hány alkalmazottja van a cégnek.
3. Adjuk össze :l fizetéseket.
4. Osszuk cl egymással az imént kapott két számot.

A vállalatnak nyilván V:ln valamiféle adatbázisa, amelyben a dolgozók adatait tárolja,


tehát a fizetések összegzéséhez a következő részlépéseket kell végrehajtanunk:

l. Nyissunk meg egy adott dolgozóhoz tartozó bejegyzést.


2. Emeljük ki az adacszerkezetból a fizetés értéké\.
3. Adjuk hozzá a kiolvasott értéket annak a változónak a tanaimához, :Imely
az összeget fogja tartalmazni.
4. Nyissuk meg a következő dolgozó bejegyzését.
22 11. rész • Bevezetés a C+ + programozási nyelvbe

A dolgozói adatok kiolvasásának művelete teljesen hasonlóan a következ6 részlépé-


sekre bontható:

1. Nyissuk meg a dolgozók adatait tarta lmazó fájlt.


2. Ugorjunk a kiv{llasZLoU dolgozónak megfelelő rckordra.
3. Olvassuk föl az adatokat a l emezről.

Összességében elmondható, hogya strukturált programozás nyújtotta megközelítés


a mai napig rendkivül fon tos és hasznos paradigma az összetett feladatok megoldásá-
val kapcsohttban.

Ezzel egy(iu azért marddLak még jócskán megoldandó problC:mák. Ahogy növekszik
a feldolgozandó adatok mennyisége, egyre nehezebb és nehezebb lesz mt!gbirk6zni
az adatok és az őket kezdő eljárások szétválasztásával. Minél többféle műveletet aka-
runk végezn i egy adathalmazzal , annál zavarosabb lesz :l végeredmény.

A procedurális megközelítést alkalmaz6 fejleszl6k időről időre azon kapják maguklH,


hogy már megint kitaláltak egy (lj megoldást ugyanannak a régi problémának a kezelé-
sére. Erre szokták fejlesztői körökben azt mond,mi, hogy az ilyen programozók wújra
fölfedezik a kereket", és ez az, aminek az ellentéte az újrahasznosítható kód. Az új ....J.-
hasznosítás alapötlctc mindössze annyi, hogy a programokat lehet6ség szerint olyan is-
mert tulajdonságokkal rendelkez6 általános épit6elemekb61 kell összerakni , amelyeket
szükség eset.én egyszeruen lx:emelhetünk a .saját kódunkba anélkül, hogy újra mcg kel-
lene azokat írmmk, vagy akár csak meg kellene vizsg:'ilmmk a belső szerkezetüket. Ter-
mészetesen ez a megközelítés sem tlj, hiszen a val6s objektumokkal dolgozó m6rnökök
is pont ezt a módszert alkalmazZ{Ik. Ha egy villamosmérnöknek egy készülék elkészíté-
séhez szüksége van egy tranzisztorra, nem azzal fogja kezdeni, hogy fölfedezi a tran·
zisztort, hanem bemegy a raktárba, lecmeli a pokr61 a megfelel6 alkatrészt, vagy ha na-
gyon mus7-áj, akkor módosít rajta egy kicsit. Fu rc.sa kimond.mi , de az objektum-közpon-
tú prog,dm07-ás fölta lálásáig ezt a megközelítést a programozók nem alkalmazták.

Az objektum-központú paradigma lényege mindössze annyi, hogy az adatokat és a ve-


lük dolgozó eljárásokat egy egységnek, egy objektumnak tekintjük, amelynek nevet
:lclunk, és amely különt"éle tulajdonságokkal és viselkedésformákkal rendelkezik.

A C+ + nyelv és az objektum-központú programozás


A C++ nyelv teljes egészében támogatja az objektum-központú paradigma alkalmazá-
sát, IniVel tartalmazza. azt 3. három alapszolgáltatást, amelyekre épícve ez megvalósítha-
tó. Ezek a beágyazás (enclIpsulation), az örökl6dés (inheritance) és a többalakúság
(polimorfizmus; pulymorphism).
2. óra • Egy C+ + program részei 123
Beágyazás (encapsulation)

Amikor egy villamosmérnök egy új eszközt épít, gyakorlatilag összeforraszt néhány ké-
szen kapott 'Ilkatrészl. Az új eszköz bizonyosan tartalmazni fog például ellenállásokat,
konden7.1itorokat és tranzisztorokat. A tranzisztor egy eszköz, amelynek vannak bízo-
nyos tulajdonságai és viselkedésformái. A mérnök ezt az eSlközt munkája során anél-
kül használhatja, hogy pontosan ismernie kellene annak a bels6 felépítését, vagy mű­
ködési mechanizmusait. Egyszeruen csak annyival kclllisltában lennie, hogy az illető
alk:ltrész mire jó. Ahhoz, hogy ez a dolog ebben a formába n működjön, ;1 tranzisztor-
nak mint alkatrészLípusnak tökéletesen meghatározott visclkedésformákkal és rulajdon-
ságokkal kell rendelkeznie. Egy bizonyos, jól körülhalárolható funkciót kell megval6sí-
tania, de azt teljesen. Ha egy dolog egy funkciót teljes egészében képes ellátni, azt rö-
viden l:x::;igyazásnak nevezzük.

Az objektum-központú metodológill szóhasználatával élve a lran zisl'.tor tulajdonságai be


vannak ágyazva a tranzisztor objektumba. Ezek a bizonyos Lldajdnnságok és funkciók
teh:h nem szanaszét hevernek vala hol az áramkörök dzsungelében, hanem a szerkezet
egyetlen helyére összpontosu lnak. Pontosan ebből adódik aztán az az elf5ny, hogy tran-
zisztort használni az is tud, aki ncm is ismeri annak a bels6 működését vagy relépítését

A C++ nyclv a beágyazás mcgvalósítását II fel h:lsznál6 által definiált típusok, az úgyne-
vezett osztályok használatán kcreszl'Ülteszi lehetcívé. Egy j61 átgondolt és felépített osz-
tály miután létrehoztuk pontOSan úgy működik, mint egy teljesen beágyazott egyed.
Egyetlen egészként haszmíl1mtjuk, de nem kell tudnunk, hogy mi van benne pontosan,
és az hogy működik. A működési mechanizmus a felhasznál6 el6tl ált.1lában teljesen
rejtve maradh:ll, C&1k aZI kell tudnia az H1etőnek, hogy pon{osan mire való az adott
elemtrpus, és hogyan kell azt használni. Az osztályok létrehowsár6l először II 7. órJ
anyagában lesz sz6 részletesen.

Öröklődés és ~jrahasznosftás

Az 1980-as évek végén aCiLibanknál dolgoztam, ahol az volt a feladatom, hogy fej-
lesszek ki egy eszközt, amivci az ügyfelck otthonról intézhetik banki ügyeiket. Mivel
gyors:1n pi:1crn akamtnk kerülni ezzel a szolgáltatússal, nem a scmmib6l kezdtem cl meg-
tervezni a készüléket, h,mcm egy már létező dologból, nevezetesen a tdcfonbó! indul-
tam ki, és annak a képességeit fcjlcsztctLem továhb a bank igényeinek megfcleWen. Úgy
is fogalmazhatnék, hogy amit végül megcsináltam, az egyfajta telefon volt, de néhány
extr-.!. szolgáltatással kiegészítve, hiszen a megval6sítás során végig támaszkocltam a már
meglév6 funkciókra, például a Iúvások kezelésére, viszont ahol kellett, ott továbbfejlesz-
tettem azokat. Kicsit tömörebben újrahasznosítottam II telefon meglév6 funkcióit.

Ugyanez! a dolgo! a C++ is tudja, hiszen az öröklődésen keresztül támogatja a k6dok új-
mhasznosítását Egy új típust, vagyis egy új osztályt ugyanis létrehozhatunk egy már
24 11. rész· Bevezetés a C+ + programozási nyelvbe

meglév6 alapján is. Ilyenkor azt mondjuk, hogy az új alosztály t a régib61 származtatjuk,
az általa képviselt fe lhasználói típust pedig származtatott típusnak (derived lype) nevez-
zük. Ezzel a S7..6ha.sználattal élve tehát az én .felspécizen telefonom nem egyéb volt,
H

mint egy a közönséges telefonok osztályából származtaton új telefontípus. Származtatás-


nál az új osztály örökJi a régi ntinden tulajdonságát, de annak funkcióit újakkal is kiegé-
szíti. Az örökl6désr61 és annak használatáról a 16. órában lesz majd szó részletesen.

Többalakúság (polimorfizmus)

Egy okos telefon ugyanabban a helyzetben néha máshogy viselkedik, mint egy közön-
séges "bula~ készülék. Ha hívás fut be, akkor a hagyományos telefon egyszen1en meg-
csörren. /\7. okos készülék ezzel szemben bekapcsolja a képerny6 világítását, ma jd em-
beri lmngon közli "lIív{\sod van kedves gazdám". A dologban a legérdekesebb n,
hogy mindehhez a leJefontársaságna k semmi köze sincs. Hívúskor ponlOs~tn ugyanaz
a jt'l fUl be a buta 0s ~lZ okos telefonba, csak az erre :IdOlt v51as7.Uk tér el egym(\stól.
Az egyik csöng, ;1 músik beszél, vagyis mindkett6 éppen a "megfelela dolgot" esiníi lja
a külső jelre.

Az cffi:le ~hclycs viselkedésformák megvalósítását :l C++ is támog!ltja n úgynevezeu


H

többalakú.súgon (polymorphism) keresztül. A progr.unokban függvények és osztályok


egyaránt mUlathmnak többalakíságOl, ami nagy vonalakban annyit jelent, hogy egy
prug. . ..unban lIgyan:......z~11 a névvel egy függvény vagy osztály több különböz6 megval6sí-
lfisa is létezhct, 6s ezek minden helyzetben éppen a megfelel6 dolgot csinálják. A több-
:Ilakúsúgról, és annak kifinomult használatáról a 17. és 18 órában lesz szó b6vebben.

Egy egyszeru program részei


Az ds6 ór.íban múr lútlunk egy egészen egyszeru C++ progmmol. Ez volt a he110.epp,
amelynek cgyszcrGsége dlenére súmos érdekes épít6eleme van. Ebben a szakaszban
közc1ebbr6l is megvi"_'>gúljuk a program felépítését. A progmm k6dja, amit ill csak
a kényelem végett isméuek meg a 2.1 Listában látható.

2.1 Lista - A hel10.COD Droqram ellYSZerúséae ellenére k1válóan alkalmu IHIV C+ +


program épft6elemeinek bemutatására
o: ~i.ncludc <iostream>

"2 : int main ()


3: t
4: std :: cout « 'Hello World!\n';
5 : return O;
6,
2. óra • Egy C++ program részei 125

Hello World!

A O. sorban beemeljük a program k6djába az iosLream n evű fájl tal1almál. A fordító-


program szempontjából ez gyakorlatilag ugyanolyan, mintha begépeltük volna
a hello.epp fájl elejére az iostrcam nevlÍ fejlécá llomány teljes taltalmát.

Az #include utasftás vizsgálata

Amikor elindítjuk a rordítóprogramoL, az el6ször is meghív egy másik programot,


az úgynevezett előfeldolgozót (preprocessor). Az el6fddolgoz6t soha nem kell kézzel
indíta nunk, ezl a fordítóprogram mindig megteszi helY(;Hünk.

A O. sor első karaklere egy kett6skeresZl (#), ami nem m,ís, mint egy az eI6felc1olgo-
zónak szóló jelzés. Az el6feldolgozó ugyanis els6 közelílésben semmi egyebet nem
tesz, mim végigolvass,1 a fo rráskódot, és olyan sorokat keres benne, amelyek
kellős kereszltel kezdődnek. Ha wlá l ilyent, akkor al utána szerepl6 utasításoknak
megfelelően módosíuisokat végez a fornískódon. Amit tehát a fordítóprogram tényle-
gesen megkap, az már ez a módosított változat lesz.

Az előfeldolgozó tehát olyasmi, mint könyvnél a szerkeszt6, csak ez fordít;\si id6ben


működ i k,6s forráskódot szerkeszl. Ezt az aUlOl11atikus szerkesztőt a fent említett
el6feldolgoósi direktívákkal (preprocessor directive) vezérelhetjük.

/\ kellóskereszt után következő szó, az include egy ilyen, az e16feldolgozónak szóló


utasílás, amely aZl mondja: "Ami most következik, at; cgy fájlmk a neve lesz. Keresd
meg ezt a fáj lt, vedd a teljes tartal mát, és szúrd be ide, ahol én vagyok." Ha a fordít6-
progJ<ltllUnk jól van beállítva, akkor megkeresi a kac~acsőrök között megadott
iostream nevu fáj lt abba n a könyvtárban, ahol a fordító fejlécállománya i (header file)
ta lálhatók, és becmeli a Larlalmál a forráskódba . Ezeket a fájlokat szokás amúgy "h fáj-
loknak" vagy "include fájloknak" is hívni, a neviik pedig rendszerint . h kiterjesztéssel
vég7.ődi k .

Aprop6 Afejlécállományok kiterjesztéseiról


Gyakori, hogy az új ANSI szabványnak m egfelelő fejlécálJományoknak semmiféle ki-
terjesztésük nincs. Ugyanakkor a "történelm i hagyományok" miatt a legtöbb fordí-
tóprogram kétféle változatot is tartalmaz ezekből. Az egyiknek a régi, a másik már
az új szabványhoz igazodik. Ha például megvizsgáljuk, hogy az imént említett
26 11. rész • Bevezetés a C+ + programozási nyelvbe

i ostream nevű állománnyal mi a helyzet, igen nagy valószínűséggel két változatot


is találunk majd beléle. Mi ebben a könyvben mindig az új szabványnak megfeleló
változatokat fogjuk használni, hiszen a másik garnitúra elavultnak számít.
Ugyanakkor ha valaki egy régebbi kiadású fordítóprogramot használ, akkor kényte-
len a hagyományos írásmódot alkalmazni, vagyis ebben az esetben a O. sor helyes
alakja a kővetkezó:
~include <iostream.h>

Az i ostream Onput-output-stream) fejlécállomány bcemel~ére :I COUl használata miaU


van szüks(:günk. Ez az az utas1"t{ls, amivel a képerny6re tudunk írni dolgokat.

Figyeljük meg, hogya cout-ot megelőzi egy s td : : jeJz6, ami a fordítóprogram számá-
ra azt jelenti, hogy a szabványos be/ kimeneti könyvtá ml kell használnia. Hogy ez a do-
log pontosan hogyan is m('iködik, és mi is az, ami ilyenkor a háttérben történik, majd
az elkövctkez6 6r{lk során fog kiderüJni. Egyelőre maradjunk annyiban, hogy
az std , : cout az lIZ objektum , ami a szabványos kimenetet kezeli, testvére {X..odig,
az std : : cin, ugyanezt teszi, csak abemencttel.

Amint azt már említettem, H nulladik sorban tal;~lhar6 i ncl ude direktíva hatása pontosan
ugyanolyan, mimha a k6cl elejére bcgépeltük volna az iost erarn állomány teljes tartal-
mát. A.mikor a fordítóprogram megkapja a forrásfájll, ;lbban már Olt van ez a t::ut:lloln.

Elemzés soronként

A program ténylegesen a 2. sorban kezdődik, ahol rögtön egy main () nevli függvé nyt
IMunk . Minden C++ programban kell lennie egy ilyen ncvl.1 függvénynek. A rüggvény
úgy általában véve nem egyéb, mint egy k6drészlet, :unely egy van több m('iveletet hajt
végre. A fllggvénybe n foglalt műveletek végrehajtását szokás a fí.iggvény végrehajtásá-
mik vagy meghívásíinak is nevezni, és talán az sem túl meglep6 , hogy függvények is
mcghívhatnak más függvényekel. A main () mindazonáltal a függvények közölt speci-
ális helyet foglal el , mivel ez a program belépési pontja. Ez azt jelenti, hogy amikor el-
indítunk egy lefordíton programot, akkor a rendszer automatikusan a main () függvény
végrehajtás;íval kezdi a művel et ck sorát.

A main () megadásánál - akárcsak az összes többi függv(>nynél- előre meg kell mon-
danunk , hogy az milyen típusú ért(:ket fog visszaadni. Ezen a ponton a main () megint
különlegesnek számít, mivel kötelez6en egész (integer) értékkel kell visszatérnie. Ezt
a típust a CH nyelvben az int kulcsszó jelzi, de err6! majd csak a harmadik órában lesz
szó. Ami pedig a visszatérési értékeket illeti , ezekről először a 4. órában tanu lunk. majd
a kifejezések és utasítások kapcsán.
2. óra· Egy C++ program részei 127

Tudta hogy...? Értékek visszaadása?


A main () függvény által visszaadott egész értéket maga az operációs rendszer veszi
át. Ez azért hasznos, mert ez az egyetlen szám főlhasználhat6 bizonyos hibaállapotok
}elzésére. Ez amúgy teljesen áttalánosan használt módszer a termelési vagy kötegelt
feldolgozást végző rendszerekben. A dolog a gyakorlatban úgy néz ki, hogya program
által az operációs rendszernek visszaadott hibakódol egy másik program vagy szkript ki
tudja olvasni, és meg tudja tenni a megfelelő lépéseket, hogy értesítse a kezelóket. (Ha
például egy kifejezetten nagy jelentőségű adatfeldolgozási folyamatról van SZÓ, akkor
üzeneteI küld egy személyhívóra, természetesen szigorúan az éjszaka kellós közepén.)

Minden függvény törzse egy nyitó kapcsos Lár6jellel kezd6dik, és - nem túl meglep6
módon - ennek <I záró párjával végződik. Amint látható, a mi main () tUggvényünk
nyitó 6s záró kapcsos z{lfóielei a 3. és a 6. sorba kerültek. Minden, :uni e között a két
z.'iró jel között lnlálhatú, a függvény része. A kapcsos :dlr6jclek egyébként programblok-
kok jelölésére hasznáhlLOsak. Nlen esetben a függvénytöí/..set, mint önúlló egységet ha-
tárolják, dc kés6bb egyéb blokkfajtúkról is olvashatunk majd.

Programlmk veleje a 4. sorban látható. Itt a cout objektum segíts(:g(:vd a k6pcrny6re


küldü nk egy üdvü:dó üzenetet. Az objekru mokról részletesebben majd a 8. órában lesz
szó. Most d 6gedjünk meg annyival, hogya coul és cin objektumok <I C++ progr:amok-
ban adottak, azokat a fordítóprogr"'dm készítője hozza lélre, v.. gyis nekünk c-S;;lk has7.nál-
nunk kell 6kct. A cout a képernyőre írásra, a cin a billentyűzetről olv:lsásr::1 v:ll6.

Nos, akkor vizsgáljuk meg közelebbr61, hogyan is kell használni pontosan ezt a bizo-
nyos cout objektumot. EI6ször is írjuk le magának a cout-nak a nevét, azt!in ur!ina
rögtön a kimenet !itirányításámk o perátorát (<<). Utóbbi nem egyéb, mint két egymás
utá n írt kisebb-nagyobb jel, vagyis senki ne keresse a bil1entyl.1zeten. Bármi , :unit ez
után az operátor után ínlnk, a progmm futása közben kikerül a k~ perny6re. Ha egy
szöveget - vagyis egy karakterláncOl- akanmk kiíratni, ne felejtsük el kett őS id 6 zője­
lek közé zárni azt (Igy, l.llninl az a 4. sorban is látható. f igyeljük meg, hogy magát a ki-
ímndó szöveget nyomtatható karakterek alkotják, a sor végén azonban van egy érde-
kes jel, az a bizonyos \n . ez nem egyéb, mint az újsor kar::lkter, vagyis egy olyHn nem
nyomta tható jel, ami arra utasítjH H cout objektumot, hogy emeljen sort :l képernyőn.
A végeredmény tehát az lesz, hogya Hello World! mondat külön sorb;·ll1 jelenik meg.

Az 5. sorban az operációs rendszernek O értéket Hdunk vissza. Egyes rendszereken ezt


a számot sikeres vagy sikertelen végrehajtás jelzésére swkás használni. 1\ megegyezés
szerint nulhl ti sikeres, az ettől eltérő érték a sikertelen végrehajtást jelenti. Ugyanakkor
hozzá kell tennünk, hogy a modern, grafikus felüleuel ellátott operációs rendszerek-
ben ezt a szolgáltatást gyakorlatilag semmire nem használják, így az ebben a könyvben
bemutatott valamennyi program nulla értékkel fog visszatérni.

A main () függvény kódja a 6. sorban ér véget a záró kapcsos zár6jelle1.


28 11. rész· Bevezetés a C+ + programozási nyelvbe

Megjegyzések
Amikor írunk egy progmmot, rendszerinlteljcsen világos és magától értetőd6, hogy mit
is akarunk megvalósítani egy adon kódrészleltel. Az emberi természet azonban mulat-
ságos dolgokat képes produkálni. Ha megnézzük ugyanazl a kristálytiszta kódot egy
hónap múlva, gyakr-.m megesik, hogy már nem értjük, mi is folyik ou, vagy legalábbis
hoss7..an kell gondolkodnunk, hogy megértsük a saját alkotásunkal. Ez a jelenség pedig
teljesen által:ínosnak mondható, hiszen minden valamire való kódban ott van valahol
ez a bizonytalanság. Persze azt nem lehet el5re megmondan i, hogy hol lesz, de hogy
oU lesz valahol, az biztos.

Pontosan ennek a kezcll:sl:re találták ki a megjegyzéseket, amelyekkel nem csak ma-


gunkat segíthetjil k meg, hanem mások számár;] is könnyebben értheté'ívé tehetjük
az általunk írt kódokat. A megjegyzés egyszeruen o lyan szövegrész, amit a fordítóprog-
ram figyelmen kívül hagy, e amelynek tartalma alapján könnyen elclönthet6, mi is fo-
lyik éppen a programnak azon a részén.

A megjegyzések tipusai
A megjegyzés tehftt olyan rész a forráskódban, amelyalefordított program mO"ködését
nem befolyásolja, viszont mankót jelenet annak, aki meg akarja érteni a kódot. A C++
nyelv kétféle megjegyzés használatát tesz lehet6vé. Az egyiket két perjel (J / ) előzi
mcg. A továbbiakban ezt a típust C++ stílusú megjegyzésnek fogjuk hívni. Ennél a tí-
pusnál a két perjel ami utasítja a fordítóprogramot, hogy az adott sorban :1 jel után
hagyjon mindent figye lmen kívül, egészen a sor végéig.

A másik típllSÚ megjegyzés kezdetét egy perjel és egy csillag Ct") jelzi, mégpedig eb-
ben:l sorrendben, a végét pedig egy csillagot követő perjel ( .. t) mutatja. Az ilyen meg-
jegyzésnél a fordítóprogram minden, a két említett jelzés közé esó szövegrészt figyel-
men kívül hagy. Ezt a megjegyzéstípust a továbbiakban C stílusú megjegyzésként fog-
juk em líteni, mivel valójában a C nyelv hagyatékának tckinthet6. Ha ezt a típust hasz-
náljuk, akkor ügyeljünk rá, hogyaforráskódban minden nyitó ; .. jelnek meg kell le-
gyen valahol a záró" I párja.

Számos C++ programozó csak a C++ stílusú megjegyzéseket haszná lja, a C stílusút pe-
dig kizárólag akkor, ha a forráskód egy blokkját átmenetileg üzemen kívül akarja he-
lyezni. Ezt a módszert szokás .kikommentezésnek" is ncvezni. Fontos megjegyezni,
hogy az így hatástalanított kódrészlet tartalmazhat C++ stíl usti, vagyis két perjelicI kcz-
d6d6 megjegyzéseket is, de C stnusúakat nem.
2. óra • Egy C+ + program részei 129

Figyelem! Kódrészlet üzemen Idvül helyezése


Igyekezzünk körültekintően eljárni, ha C stílusú megjegyzés segítségével akarunk
átmenetileg hatályon kívül helyezni egy blokkot a forráskódban. Ha ugyanis ez
a kódrészlet más C stllusú megjegyzéseket is tartalmaz, akkor megeshet, hogy
csak egy kisebb részletet sikerül kiiktatnunk, nem pedig azt a blokkot, amit eredeti·
leg szerettünk volna. Főszabályként elmondható, hogya C stílusú megjegyzések
nem ágyazhatók egymásba. Hiába van ugyanis a kódban két nyitó I " jelünk, a for-
dítóprogram belső logikája szerint a legelső *j bezárja az összes megnyitott meg-
jegyzést. Az ilyen hibák elkerülésében nagy segítségünkre lehet egy jó grafikus fej-
l e sztői környezet, amely más színnel jelzi a megjegyzésbe tett részeket, fölhíwa így
a figyelmet az esetleges tévedésekre. Szintén jó hír, hogya ,.fölösleges" záró" 1 je-
leket a legtöbb fordítóprogram hibának tekinti, és megáll.

Megjegyzések használata egy egyszenl programban

A megjegyzések semmiben sem befolyásolják a lefordílott k6dol. A megjegyzésekt61


sem a program, sem annak fordítása nem les:! lassabb, hiszen a rordítóprogmm egysze-
ruen figyelmen kívül hagyja 6ket. A megjegyzések használatára nllltat példát a 2.2 tis-
tában látható egyszerű program .

2.2 Usta - Megjegyzésolc hasznélsta (comment.cpp)


o: ~includc <i ostream>
1.
2 : int main~)
3: {
4 : 1* Ez egy C stílusú megjegyzés
5 : ami addig tart, amig a fordító
6 : meg nem ta lálja a záró csillag-perjel kettőst * I
7 : std : : cout« "Helló Világ ! \n";
8 : /1 Ez egy e++ stílusú megjegyzés . ami a sor végéig tart
9 : sto. : : cout« "rtt a megjegyzés vége!";
10 :
11 : /1 Egy sor állhat egyetlen kettős perjel l el inditott mcgjegyzésb6l is
12 : 1* Ugyanez igaz a C sti l usú megjegyzésekre is */
13 : r"eturn O;
14 :

Helló Világ!
I t t a meg j egy z és vége !
I
30 L rész • Bevezetés a C+ + programozási nyelvbe

A 4. és 6. sorokban l:tlható megjegyzéseket a fordítóprogram teljes egészében Ilgyel-


men kívül hagyja. Ugyanez: igaz természetesen a 8. , ]"t. (:s 12. sorokban láLhat6 szö-
vegrészekre is. A 8. sorban induló megjegyzést a sor vége árja, vagyis ennek jelzésére
nincs szükség. Ugyanakkor a 4. és 12. sorokban láthatók régi stílusúak, tehál itl szük-
ség van az explicit lez.árásrd.

A megjegyzések írá~ olyasmi, antivel kapcsolatban a progmmozóknak csak egy kis


csoportja tekinthet6 ~mesternck ".

Általánosságban elmondható, hogy bár az "olvawközönségünk" bizonyára ismeri ma-


gát a C++ nyelvet, vagyis elvben el tudja olvasni, amit írtunk, azért <ll emberek igen
kis hányada rendelkezik ezzel egyidejűleg <I gondolatolvasás képess6gével. M:1gáb61
a kódból nyilván egyénehm1en kiderül, hogy mi az, ami II programban történik. A jó
tm::gjegyzésnek az a dolga, hogy világossá tegye az olvlls6 számárd, mU:rt pom az tör-
ténik, ami.

Függvények
Bár a main () is egy függvény, azért ,I függv(:nyck egy meglehet6sen swk<ltlan fajtáját
képviseli. Amint azt már llldjllk, a main () az, amit az operáci6s rendsz:er el6ször meg-
btv, amikor a programunk elindlll. Ezzel aztán kezdetét veszi a kód végrehajtása,
amelynek során a main () egyéb függvényeket hív meg, és persze a main () által indi-
tott függvények is hív haLnak más függvényeket.

Maga a main () mindig egész, vagyis int tíPllSÚ értékkel tér vissza. Amint azonban
az elkövetkező órák során látni fogjuk, ennek a többi függvénynél nem föltétlen kell
így lennie. Egy függvény a legkülönbözóbb visszatérési énékekkel rendelkezhet, 56t
az sem kizárt, hogy egyáltalán nem ad vissza semmit a hív6 félnek. A program kódját
a rendszer sorról sorra h:-dadva hajtja végre egészen addig, amíg egy függv6nyhíváshoz
nem érkezik. Ilyenkor a végrehajtási sorozat fonalán egy elágazás keletkezik, a rend-
sz:er leflJtt3tja a függvény törzsében megadott kódot, majd a végrehajtás a fuggvényhí-
vás utáni soron folytatódik.

Ez a logika amúgy nem idegen a mindennapi viselkedésünktől sem. Képzeljük el pél-


dául, hogy önarcképet készítünk. Lerajzoljuk a fejünk körvonalát, majd berajzoljuk
a szemeket meg az orrot, de akkor hirtelen kitőrik a ceruzánk hegye. Ilyenkor a meg-
kezdett múveletsorba egy elágazást kell iktatnunk, és végre kell hajtanunk a . hegyezd
ki a ceruzát" függvényt. A függvény törzsében a következő részlépések találhatók: szé-
pen abbahagyjuk a rajzolást, fölállunk, odasétálunk a hegyezőhöz, kihegyezzük a ceru-
2. óra· Egy C++ program részei 131

zát, visszamegyünk a helyünkre, aztán ott folytat juk a rajzolást, ahol abbahagyruk. (Na
ja, az arcképünkön marad egy kósza vonal ott, ahol a hegy kiLöröLt, de ezt m ajd egy
másik füru,'\'ény elimézi.)

Függvényhlvások

A függvényeket lekimhetjük amolyan speciális szolgáltatásoknak. Amikor a f6program-


nak szüksége van egy ilye n szolgállatásra, akkor meghívja a megfelelő függvényt, majd
annak le futása után o tt folytatja a végrehajtást, ahol a z abbamamdt. Ezt a működési lo-
gikát szemlélteti a 2.3 Lista.

r2.3 um - függvényhlvú bemutatá.. lcallfunc.cpp) l


o : *i nclude <i oat r earn>
1.
2 : II Fűggvényhivás bemu t atá sa
3 : II Ozenet mcgjclenitése a képerny6n
4 : void DemonstrntionFunction{)
5: {
6 : std : : cout « "A DemonatrationFunction foggvénybcn vagyunk\n ";
7: )
B.
9 : II A main() fQggvény - kiír egy Qzenetet , majd
10 : II meghivja a Demon~trati.o nFunction nevű függvényt , végűl
11 : II pedig kiír egy második üzcnc t et
12 : int main{)
13 : (
14 : ~td : : cout « "A fMin fOggvényben vagyunk\n"
15 : DcmonstrationFunction() ;
16: std : : cout « "Ismét a main-ben vagyunk\n";
17: return O;
18 :

lCImiinII J
A main függvényben vagyunk

-
A Demonstrat ionFunction függvényben vagy\mk
Isrn6t a main-ben vagyunk

-.
A Demons trationFunction (l függvé ny kódját a 4-7. sorok tartalmazzák. Ez a kód-
részlet a program elkülönült része, vagyis le van benne írva , mit kell csinálni, de amíg
meg nem hivják a függvé nyt, addig nem csinál senunit. A hívás a 15. sorban látható,
melynek hatására a függvény kiír a képerny6re egy tájékoztató szövegel, majd visszatér
a hívóhoz.
32 11. rész· Bevezetés a C++ programozási nyelvbe

A progr.Hn tényleges kezdete a 12. sorban látható. A 14. sorban a main () függvény kiír
a képernyőre egy üzenetet, amelyben közli, hogy most éppen a main () végrehajtásá-
nál tart a program. A következő lépésben, vagyis a 15. sorban a main () meghívja
a DemonstrationFunction () függvényt. Ennek hatásár.! lcfutm.k a Demonstration-
Function () törzsében leírt utasítások, ami jelen esetben mindössze egyetlen k6dsort
(6. sor) jelent. Ez kiír egy újabb üzenetet, amelyben jelzi, hogy a végrehajt1~s most épp
a meghívott függvény belsejénél tart. A 7. sorban aztán a Demonst rat ionFunct ion ( )
véget is ér, és a vezérlés visszakerül a hívóhoz, vagyis esetünkben a main () függvény-
hez. A végrehajtás a függvényhívást közvetlenül követő soron, vagyis esetünkben a 16.
sorban folytatódik, ahol a main () kiírja az utolsó üzenetet, és maga is véget ér.

Függvények használata
Egy függvény vagy egyetlen adatta l lér ViSSZ:l, vagy vissz,nérési értéke void, ami azt je-
lenti, hogy nem ad vissza semmit a bívónak. Ha példáu l írunk egy függvenyt, ami két
egész S7.rl1l1Ot tud összeadni, akkor <lZ összeget nyilván célszen." a visszatérési értékben
elhelyezni. Ilyenkur tehát a függvényünk egész (inI) típusO visszatérési éltékkel rendel-
kezik. Ha ugyanakkor van egy olyan függvényünk , ami csupán egy üzenete ír ki , de
amúgy nem csin:'t1 semmi említésre melLÓl, akkor nyilván nincs mit visszaadni a hívó
félnek. A függveny visszatérési értéke tehát ilyen esetben célszcn.len void.

A fiiggvények mindig egy fejlécból (4. sor) es egy törzsból (5-7 sarok) állnak. A fejléc
tartalmazza a viss7.atérési érték típusát, a függvény nevél, valamint a bemenő paramé-
terek (ha vannak) listáját. A bemenő pamméterek segíL~égével adhatunk :1t egy függ-
vénynek külölúéle értékeket, amelyekkel a mUködését vezérelhetjük. Visszaté/Ve
az imémi , egyszeru összeadást végző függvényünkhöz nyilván célszenl ennek pammé-
terként átadni a két összeadaoclót. Egy ilyen függvény fejléce tehát a következőképpen
nézhet ki:
int Sum (int a, int b)

TiSZlázandó az elnevezé.~eket a függvényparaméter megadha cSllp:'in egy deklaráció,


amelyben azt írjuk e16, hogy milyen típusú adatot kell a hívó félnek :'itadnia. A függ-
vény hív{\sakor ténylegesen átadott érték neve argumentum. Ezt a két fogalmat számos
progmmoz6 szinonimaként használja, míg mások kínosan ügyelnek a helye.~ elnevezé-
sekre. Mi ebben a könyvben az első - kétségkívü l könnyebb - megoldást fogjuk vá-
lasztani, vagyis hol par.lmélelt, hogy argumentumot írunk m<ljd.

A függvény nevét és paramétereinek listáját (vagyis a fejlécct a visszatérési érték típU5<1.


nel kül) együttesen a függvény szignatúrájának nevezzük. A függvény törzse egy nyitó
kapcsus zárójellcl kezdődik, nulla vagy több utasításból áll és egy záró k.. pc.sus zárójel-
lel vég7.6dik. A függvény által elvégzendő munkát a két zárójel közti utasítások jelen-
tik. Értéket visszaadni a return utasítással tudunk, amely egyben be is fejezi a függ-
2. óra • C++ részei 33

H~ny futá!;át, akárhol legyen is a k6djában. l-la egy függvényben egyáltalán nem szere-
pel ez az utasítás, akkor automatikusan void lesz a visszatérési értéke. Ügyeljünk rá,
hogy a retur n után valóban olyan típusú érték szerepeljen, mint amilyent a deklaráci-
óban visszatérési típuském megadtunk.

Függvényparaméterek hasmálata
A 2.4 Listában bemutatunk egy egyszerű függvényt, amely kél egész számot vesz át
a hívőtől bemenő paraméterként, összeadja azokal, majd az eredménnyel - amely szin-
tén egész é rték - té r vissza a hívóhoz.

2.4 Ua.. - Egy bemen6 paraméterekkel randalk..6 agyazer4l1lggvény llune.eppl


o: ninc l ude < i ostream~
1.
2 : int Add (int x, int y l
3: {
4 : std :: cout « "Itt az Add ( ) függvény, a kövctkcz6 értéke ket kaptam "
_ « x « • és « y « ' \n ' ;
5 : return (x+y) ;
6.
7.
8 : int mai n()
9: (
10 : s td : :cout« "A main() fo.ggvé nyben vagyu n k ~ \ n " ;
11 : std :: cout« " \nMeghivjuk az hdd() fü ggvényt\ n';
12 : st d :: cout« "A visszaadott érték : " «Add (3 , 1) ;
13 : std :: cout« " \nIsmét a ma in() függv é nyben vagyun k. \n" ;
14 : std : : cout « "\nKilépünk ... \n\n" ;
15 : rcturn O;
16 :

monat
A main () fü ggv ény b e n v agyun k!

Meghivjuk a z Add() függvé nyt


It t az Add() függvé ny , d következő értékeket kaptam 3 és 4
A vissz aadott ér t é k: 7
Ismét a mai n () függ"vényben vagyunk.

Kilépünk . .

Az Add () függvény meghatározása a 2. sorban kezdődik. Látható, hogy két cgésL': érté-
ket vár bemenő paraméterként, majd egyetJen, szintén egész értékkel tér vissza. Maga
a program a 10. sorban k ezdődik , ahol kiír egy tájékoztató üzenetet.
34 11. rész · Bevezetés a C+ + programozási nyelvbe

A 12. sorban a main () egy üzenet kíséretében kiírja az Add () függvény által vissza-
adott értéket. Ez az érték az Add (3 , 4 J hívás hatására keletkezett, ami szintén a 12. sor-
ban látható. A végrehajtás e rről a pontról ágazik el úgy, hogya hívás után végrehajtott
első művelet a 2. sorban látható. A függvényen belül a neki átadott két értéket az x és
y változók reprezentálják. A kód ezeket összeadja, majd az eredményt az S. sorban
visszaadja a hívó félnek.

Ez az a visszatérési érték (7), am i a ] 2. sor hatására a képernyőn is megjelenik.


A mai n () ezután a 1.~. és 14. sorokban kiír egy-egy üzenetet, majd maga is kilép,
visszaadva ezzel a vezérlést az operációs rendszernek.

Kérdések és válaszok
Kérdés: Mi a szerepe az #include utasítéisllak?

Válasz : El. egy az clőfeldolgozónak (preprocessor) szó[ó direktíva. Az el6feldo lgozó


egy olyan program, .l mi! a fordító hív meg automatikusan minden egyes fordítási meg-
kezdésekor. Ennek a múködésér lehel vezérelni ezckkel a direktívákkal. Konkrétan
az Itinclude aZl jelcnti , hogy az e[6feldolgozóna k be kell sz(u·nia az adott helyre
az #include lll,ín megadotl fájl lcljes larl~l l m,Íl ugyanllgy, mintha azt mi magunk gé-
pdtük volna be oda.

Kérdés: Mi az alapvető külóllbség a II és a 1 * lípllSIÍ meg;egyzéseM Mz6tt?

Válasz: A keu6s perjellel indított megjegyzés a sor végén automatikusan véget ér.
A perjel és csillag kombimídóva l jelzett ezzel szemben a záró p{lfáig, vagyis az els()-;
karakrcrkettősig tart. Ne felejtsük el, hogy az ilyen típusú megjegyzésnek "mag{ltól" so-
ha nincs vége, az még a függvényeket lezáró kapcsos zár6jelen is {ltnyúlhal. Minden-
képpen ki kell írnunk tehát a záró -; kombináci6t, különben fordítási hibát kapunk.

Kérdés: Mi különbóz lel Illeg egy jó es egy rossz megjegyzést?

Válasz: Egy jó megjegyzés azt mondja el a kód olvasójának, hogy miért pont az töné-
nik az adott helyen, anti, vagy általánosabb esetben információt s7.o1gáltat arról , hogy
az adott kódblokKnak mi a célja, mi az a feladat, amit meg akalUnk vele oldani. Rossz
megjegyzés például az, amelyik azt próbálja megmagyarázni, hogya k6d eb')' adott so-
m mit csinál. A k6dsorokat úgy kell megírni, hogy azok magukért beszéljenek, és
a megértésükhöz ne kel!jen semmilyen további magyarázat. Az igazság az, hogya for-
ráskódok dokumentálása amolyan önálló művészeti tevékenység, amit magához
a nyelvhez hasonlóan tanulni kell.
2. óra · Egy C++ program részei 135

Gyakortatok
Most, hogy már tudunk ezt-azt a C++ nyelv ről, álljon itt néhány megvá laszolandó kér-
dés és megoldandó feladat, amelyekkel megszilárdíthatjuk a fordítóprogrammal kap-
csolatos eddigi tudásunkat.

Kvfz
l. Milyen típusú a main () függvény visszatérési értéke?
2. Mire valók a bpcsos zá rójelek'
3. Mi a különbség a fordítóprogram (compiler) és a parancsértelmező (interpreter)
között?
4. Mién o lyan fontos il kódok újrahasznosítása?

Gyakorlatok

1. Vegyük H 2.1. Listllban bemutatott kódot, és bontsuk ketté a 4. sorllba n hítható


művel etet úgy, hogy az egyik sorban a Helló a másikban a Világ sZót írassuk ki.
Történt valami változás a kimenet form<Ítum(lbHn? Hogyan o ldhatjuk meg, hogy
a két szó kü lön sorba n jelenjen meg?
2. Próbti ljuk meg lefordítani saját rendszerü nkkel a 2.1 Listában látható kódot de
úgy, hogy az üinclude dostream> direktívllt átírjuk I/inc1ude
<iostream . h> alakúr.L Ezzel kideríthetjük, hogy az álta lunk haszn{llt fordító-
program elfogadja-e mindét alakot, vagy ha nem, akkor melyikkel hajlandó mű­
ködni. Ennek ,lZ információnak később még nagy hasznát vehetjü k.
3. Pr6búljunk meg lefordítani egy kódit az integrált fej l esztői környezet (IDE) segít-
ségével, majd parancssorból is. Van-e bármi különbség a keletkezett bináris állo-
mányok kÖlött?

Válaszok a kvlzkérdésekre

l . A mai n () függvény mindig egész (int) értékkel tér vissza .


2. A kapcsos zárójelek egy programblokk elejét és végét jellik. Ilyenek közé kell
zárni példáu l egy függvény törzsét, de léteznek más fajta blokkok is.
3. A fordítóprogram (compiler) a teljes forráskódot lefordít ja gépi k6dra, m i előtt eJ-
kezdené végrehajtani azt. A parancsértelmező (interpreter) ezze! szemben so-
ronként halad végig a kódon, és minden utasítást azonnal végrehajt.
4. Nos, mién használunk föl többslör bizonyos e1képzeléseket az élet más területe-
in, nem csak a programozásban? Azért, mert ez így sokbl könnyebb, gyorsabb
és bizonyos helyzetekben olcsóbb is, mint újra és újra megírni ugyanazt a kódot,
vagy megcsinálni ugyanazt. Az az igazság, hogy az információ megosztását és
a helyes gondolkodást is tanítani kell.
ÓRA

Változók és állandók
. . lisz szó ebben az órában:

• .gyan vezethetünk be új változókat illetve ;í.lland6k:it.


• H ~-an adharunk éJtékeket a változ6knak t!s hogyan v:íllozlitLhatjuk meg.
• ~'3n írhatju k ki egy változó értékét a képernyóre.

a változó?
~:;:; lZÓ néz6pontjából a változ6 egy Lerület a számítógép Jnf;!móriájtlban, flhol
I 1I u.rolhawnk, amelyeket később visszaolvashalunk.

",:..~k, ez pontosan mit is jelent, először meg kell ismernünk v:damennyirc

:
:~~~-'P mem6riájának mŰködését. A számitógép memóriáját úgy tekinthetjük,
__ '-<Jfozatát, amelyek egyetlen hosszú sort alkomak. Minde n egyes cella (vagy
~..-wet:) sorszámozva vao. Ezeket a sorszámokat nevezzük mcm6riacímeknek.
38 11. rész • Bevezetés a C+ + programozási nyelvbe

A változóknak azonban nem csak címük, hanem nevük is van. Létrehozhatunk péld,íul
egy myAge nevű változót. 1\ változónk nem más, mint az egyik cel1árd hivatkozó címke,
amely alapján a cella könnyedén fellelhető anélkül , hogy ismernénk a mcmóriacímet.

A 3.l.ábm ennek az elképzcJésnek a vizuális vázlata. Ahogy az ábrán látható, bevezet-


tünk egy myVariabl e nev(} váJtozót, amely a 103-as memóriacímen kezd6dik.

myVariable
váltoronév
RAM
elm
rnmm
'00
'" '" '03 ", '" '"
3.Ubra
A mcm6rl# Il/z/lálls ml1Rfelcl6Jc

Aprop6 Mi is az a RAM?
A RAM a Random Acces Memory (véletlen elérésü memória) rövidftése. Ez az az
elektronikus memória, amelyet a számftógép a program végrehajtása során hasz-
nál. A számítógép kikapcsolásakor a RAM-ban tárolt minden információ elvész.
Amikor a programunkat futtatjuk, az a merevlemezen található fájlból a RAM-ba töl-
tődik. Ugyanfgy a változók is a RAM-ban jönnek létre. Amikor a programozók a me-
móriát említik, valójában a RAM-ra gondolnak.

Mem6riafoglalás
Amikor C++-ban meghatározunk egy változ6t, a fordítóval nem cs:.k a nevét kell kö-
zölnünk , hanem azt is, hogy milyen cípusú információt fogunk :.bban tárolni: egész
sz,ímm, kamkt.ereket, és így tovább. Ez a változó típllsa. A másik kifejezés, amellyel ez-
zel kapcsoJmosan találkozhatunk, az adaffípus. A változó típusa meghatározza a fo rdí-
tó számára, hogy mekkont ten:.iletet foglaljon le a memóriában a vá ltozó ertékéIlek táro-
lásához.

Mi ndegyik cella egy bájt mérení. Ha a létrehozoct változó kél bájt mérení, két bájtm
van szüksége a memóriában, azaz két ceUára. A vállQZó típusa (például: int) határozza
meg a fordító számára hogy mennyi memóriát (há ny memóriacellát) kell lefoglalnia
a változó s;dI1llára. Mivel a számítógép bitek és bájtok formájában fejezi ki az értékeket,
és amemória méretét is bájtokban méri. Nagyon fomos megértenünk ezeket a fogal-
makat és otlhonosan mozognunk közöttük.
3, óra • Változók és állandók 139

Az egész számok (Integer) mérete


Egy char típusú változó (karaktNck tárolásilm használjuk) szinte mindig egy bájt
hosszúságú Egy short int a legtöbb számítóg6pen két bájtosj egy l ong i nt általiíbiln
négy bájtos, az im trpusú vá1t07-ó (short és l ong előtagok nélkül) pedig kctt6 vagy
n é~,.y bájt hosszú lehet. Ha Windows 95/98, Windows XP vagy Windows NT/2000/2003
rendszereken dolgozunk , négy bájtos inl mérettd számolhatunk, feltéve , hogy kell őe n
új fordítóprogramoL használunk.

Ám ezt nem szabad készplmznek venni, mivel nem biztos, hogy minden rendszeren
így működik. Csak annyit tudhatunk biztosan, hogy l!gy short int mérete kisebb
vagy ugyanakkora mim egy int mérete, valamint hogy egy inL mérete kisebb vagy
ugy,makkor mint egy long int mérete . A l ebegőpontos adatok kérdése már egy egé-
szen más történet, l:rr61 is hamarosan szót e jtünk.

A 3.1 Lista segítségévcI mcgCIJlapíthatjuk ezeknek a tipusok nak a pontos méretét


az adott számítógépen, az adott fordítóprogramot h asználva.

3,IIilIta - A ....611.pItja • _PUSH . . - • oz6m~ptIak6n 'siz.',.pp)


o: ~include <iostroam>
"
2 , int main ()
) , {
4, std : : cout « "Egy int t ipusú változ6 méretc :\t \t" ;
5, std : : cout « sizeof(int) « , bájt.\n";
6, std : : cout « "Egy short tipusú változ6 méretc : \t";
7, std : : cout « sizeof(short) « , bájt . \n";
8, std : :cout « "Egy short tipusú változó mérete : \t";
9, std : : cout « sizeof(long) « " bájt.\n';
10: std : : cout « 'Egy char tipusú változó mérete : \t\t";
ll : std : : cout « sizeof(char) « " bájt.\n' ;
12 : std : : cout « 'Egy 0001 t ípusú vált.ozó mérete : \t\t";
13 : std : : cout « sizeof(bool) « , bájt . \n' ;
14 :
15 :
std : : couL
std: : cout
«
« sizeof(float ) « .
'Egy fl oat tipu sú változó mércte : \t\t';
bájt.\n' ;
16 :
17 :
std : : cout
std : : cout
«
« s1zeof (double) « .
"Egy double típusú változó méret e : \t";
bájt . \n ' ;
18 :
19 : return O;
20 :
40 II. rész· Bevezetés a C+ + programozási nyelvbe

Kimenet
Egy int tipusú változó mé r ete: 4 báj t .
Egy short tipus\Í változó mérete : 2 bájt.
Egy sho rt tí pusú vá l tozó mérete : 4 bájt.
Egy char ti pusú változó m6retf! : 1 bájt.
Egy bool tipusú változó mé re te : 1 bájt.
Egy float tipusú vá l tozó mérete: 4 bájt.
Egy doub le tipusu vál tozó mé ret e: 8 bájt .

Apropó A futási eredmény eltérő lehet


Az olvasó saját számítógépén futtatva a programot eltérő eredményt kaphat.

A 3.1 Lista legtöbb eleme azonnal élthet6. Az egyetlen újdonság ti kódban az 5. és 15.
sor köúitt találhmó sizeof () függvényhívások sorozata. Ezt a függvényt a fordít6prog-
r:.lmunkkal k.apotL könyvtár tartalmazza és annak az objekwmtípusnak a mérct6L adja
vissz,!, amelyet. pHraméterként átadunk neki. Példaként az 5. sorban az int kulcsszót ad-
tuk át a sizeof () függvénynek. A sizeof () használatávalmegállapítotluk, hOh'Y
az adott gépen az int eh'Yenl6 hosszúságú a long típussa l, mivelmindkett6 négy bájtos.

signed és unsigned
Az összes típusnak kél fajtája van: el6jeles (signed) és el6jel nélküli (uns i gned). id őn­
ként szükségünk van negatív számokra, időnként nincs. Az egész tipusokat (short és
long) az unsigned kulcsszó nélkül el6jelesnek tekinti a fordító . A s igned egész típusok
lehetnek negatívak és pozitívak egyaránt. Az unsigned cgés:.::ck mindig pozitívak. Ne
feledjük: a signed az alapértelmezett az eg~sz típusoknáL

Mivel a változó bájtokban (ennélfogva bitckben) meghatározott mérete mind


a signed, mind az uns igned egészek esetében ugyanakkora, egy előjel nélküli egész
típusban kétszcr akkora értéket tudunk tárolni, mint egy előjeles egészben. Egy
unsigned short típusú változó Oés 65535 között tudja ábrázolni a számokat. Egy
signed short ripusú változó által ábrázolt számok ICle negatív, ennélfogva II signed
short ~32768 és 32767 közötti számokat tárolhat.

Tudta hogy...? Bitek a signed és unsigned típusok esetében


Az előjeles egészeknél egy bitet magához az előjel tárolásához használunk. Előjel
nélküli típusok esetében ezt a bitet is számok tárolására használjuk. Ha megfigyel-
jük, akkor látható a signed és unsigned esetekben a tárolgató értékek száma azo-
nos, a különbség tehát csak az ábrázolás módjában van.
3,6ra • Változ6k ás álland6k 141

Alapvető változótfpusok
Számos egyéb változótípus van még a C++-ban, amelyek nem .:;orolhatók az eddig tár-
gyait egész típusok közé. llyenck például a lebegőpontos és karakter típusú változók

A lebegőpontos (float) válLOzók l(lrtszámok formájában kifej ezhető értékeket, azaz


valós számokat tárainak. A karakter (char) típusok egy bájtot foglalnak magukban, és
a 256 elembő l álló ASCII illetve kiterjes .... tetL ASCII karakterkészlet karaktereinek tároIá-
sárd haszn~Hjl1k őket.

Az ASCll karakterkész/el számítógépes l1asználalra szabványosítoLl karakterek halmaza,


Az ASCII mozaiksz6 az American Standard Code for Information Interdlange rövidíté-
se. Csaknem valamennyi operációs rendszer támogatja az ASC II kódk(:s:.dct h<lszn{t!atát,
persze sok más nemzetközi karakterkészlethez hasonlóan.

A C++-ban használt változótípusokat a 3.1 Táblázat foglalja össze. A t{lbh1z,lt tartalmaz-


za a változó típusát. várható méretét, és hogy milyen típustl értékek tárolására szolgá l.
A tárolt értékeket a vá ltozótípus mérete határozza J11~g, úgyhogy ne fele jtsük el a 3.1
Lista kódjának kimenetével összevetni.

r3,1 Táblázat - Vához6tlpu.ok I


Típus Móret Értókek
unsigned short int 2 bájt 0-65535
short int 2 bájt 32768-,32768
u!1signed long im 4 bájt 0-4294967295
long inl 4 bájt 214748.~648-2147483647
int. 4 bájt 214748364&-2147483647
unsigned int <I bájt 0-4294967295
char 1 bájt 256 féle ka rakter
bool 1 bájt igaz vagy hamis
float 4 bájt 1.2e-38-3.4e.~8
double 8 bájt 2 . 2e-308-1.8e:~08

Bár sokan úgy tartják, hogy ez rossz programozói gyakorlat, dc a char típusú változó-
kat használhatjuk nagyon kis egész számok tárolására is.
42 11. rész · Bevezetés a C+ + programozási nyelvbe

Egy változó meghatározása


Vához6t annak típusának megadás.'ival, majd egy vagy több sz6közt követően a válto-
zó nevének leírásával, ponlosvesszővel lt:z{irva ké.szíthelünk, illetve határozhatunk
meg. Egy v,1ltozónév csaknem bármilyen betűt tartalmazhat , de szőközt nem. Érvényes
változónevek például a következ6k: x, J23qrsnf, myAge. A jó változónév kifejezi,
hogy azt a v;íltozót !lure is használjuk. A megfeleléS m:veket használva könnyebbé válik
a progmm folyamatának megértése. A következO: utasítás egy myAge nevú egész típu-
Sli változ6t határoz meg:
int myAge;

Ne feledjük , hogya CH érzékeny a kis- és nagybetuk közli különbségre, azaz a myAge


és a MyAge két különböző változót jelöl. Általános progr.:unoz6i gyakorlatként pr6bál-
junk olyan v,lItoz61l\.!veket :Idni, amely llta! arra, hogy milyen célra használjuk az adott
változót. A eletKor és a mcnnyiAzAnnyi váltowneveket könnyebb megértenünk és
eml(:keznünk rájuk, mintha xJ4, v<lgy egeszErtek neveket adtunk volna nekik.

De nézzünk egy hosszabb példát Pr6báljuk meg kitalálni az els6 pfir sor alapján, bogy
mit csinálnak az alábbi kódrészletek.

1. példa:
main()
(
unsigned !:lhort X;
unsigned short y ;
unsigned int z ;
z = x * y;

2. példa:
main()
(
unsigned short Szelesseg ;
unsigned short Hosszusag ;
uns i gned int Terule t;
TerOlet = Szelesseg * Hosszusag ;

A kis- és nagybelÚk eltéró jelentéséről


A CH érzékeny a kis- és nagybetúk közti külö nbségre. Magyarul a kisbetűket és
a nagybetűket a fordítóprogram megkülönbözteti. Az age és <lZ Age nevű változók en-
nek megfelelően különbözőek , és mindkettejükt61 különbözik az AGE nevű változó.
3. óra • Vá~oz6k és állandók 43

Apropó Ne zavarjátok betúimet!


Néhány fordító megengedi a kis- és nagybetúk közti különbségtétel kikapcsolását.
Ne essünk. ebbe a kisértésbe; a programunk nem fog működni más fordító kkal, és
a többi C+ + programozót is összezavarja a forráskódunk.

Sok programozó szeret csupa kisbctűs váltowneveket használni. Ha a név két szóból
áll (például my car), kél elterjedt konvenció szeri nt nevezbetjük el őket: my_car vagy
myCar. Ez utóbbit teve jelölésnek (carne! notatioo) nevezzük, mivel a nagybetúk úgy
néznek ki, mint a púpok. Tekimhctjük úgy is, hogy ezek a névadási szokások .Uni."("
és .Microsoft" strlusok. Azok a progmmoz6k, akik Unixon tanujlak, és hosszan dolgoz-
tak Unix környezetben, az első stílust választják. Ezzel szemben a Microsoft rendszeren
nevelkedett fejleszt6k igyekeznek a második változatot használni. A fordít6progr:lmot
mindez persze nem igazán érdekli, csak legyenek a változ6neveink követke7.ete.~ek.

Kulcsszavak
Néhány szó foglalt II C++ számára , ezek nem h,lsználhatók változ6nevl;!kk6nt. Ezek
azok a kuksszavak, :Imelyeket a fordító használ a progmlllunk vezérlé~ére. Kukssza~
vak pékl(lul az :l1{lbbiak: if, while, for és main. Általában véve minden értelmes vál-
tozónév bizonyosan nem kulcsszó.

A változóink L:lrtalmazh:lmak kulcsszavakat a nevük részeként, de nem áll hatnak csak


abból. Az o lyan változónevek mint például main_nag vagy forEver megengedettek, de
a main vagy a for nevek nem.

[ HoIyn I
Ilalároz7.uk meg a vállOZÓl a típusá~ Ne has;m:íljunk v:íltoz6névkém C++
nak majd nevének megadásával. ku1csszavakall
HasznMjunk beszédes változ6neve- Ne használjunk el6jeJ nélküli vállOZÓ~
ket. kat negatív számokhoz!
Ne feledjük, hogya C++ érzékeny
a kis- és nagybe tűk közti különb~
ségre!
DeríL<;ük ki, hogy sa ját rendszenJn~
kön az egyes változók mekkora me-
móriaterület foglalnak, és mekkora
értékeket tárolhat unk bennük.
44 11. réSZ · Bevezetés a C++ programozási nYeNbe

Egynél több változ6 megadása egyszerre


Egynél több azonos típusú változ61 is létrehozhatunk egyetlen UI~sílással, ha a változó
típu.!:ia után vessz6ve\ elválasztva írjuk le az egyes változóneveket. Íme egy példa:
unsigned int myAge, myWeigh t : Ilkét el6jel nélküli egéG 2 változó
long area, width , l ength : Ilhárom long típusú változó

L<1that6, hogy a myAge és a myWeight változókat egyaránt cl6jel nélküli egészkénl ha-
tároztuk m ég. A má.!:iodik sor három ö náll6 long típusú V{lltozót határoz meg arca,
w idh t, length nevekkel. A típus (long) mindhárom változóhoz hozzárcndel6dik, azaz
nem keverhetjük a típusokat a változ6k meghatá rozásánál.

Aprop6 Hogyan frjunk hasznos megjegyzéseket


A magyarázó megjegyzések elhelyezése a változók meghatározása után ugyanazon
sorban jó gyakorlat. Nemcsak minket emlékeztet a változó céljára, hanem támpon-
tot nyújt a többi programozó számára is ezzel kapcsolatban még akkor is, ha neve
erre esetleg nem utal.

Értékek hozzá rendelése a változ6khoz


A változóknak a hozzá rendel6 operátor (=) segítségével adhatunk értéket. Ennek meg-
felel6en a Widht n evű vá ltozónak az alábbi módon adhatjuk az 5 é rtéket:
uns i gned short Widht ;
Wid th - 5:

Összekapcsol hat juk a két lépést és kezd6értéket adhatunk a v:í ltozónak II meghatáro-
7.áskor így:
unsigned short wi dth c 5;

A kezd6én ék megadása nagyon hasonlít az egyszeru értékadás r-.J., és az egész típusú


változ6knál alig va n különbség a kenó kÖZÖLt. Később , ami kor az állandókról fogunk
beszélni , látni fogj uk, hogy néhány elem esetében kötelez6 II ke zd őérték megadása,
mivel kés6bb má r nem tudunk értéket hozzárende1ni.

A 3.2 üsta bemutat egy fordításm kész programot, amely egy téglalap terü letét számítja
ki, majd kiírja a vá l ~s zt a képerny6re .

3.2 Lista-A _ _ ak _ (lII8VIIr.cpp)


0 , II A vá lato26k használ atának bemuta tása
l , l i nclude <iostream>
2,
3. óra • Változók. és állandók. 145

3 : int main()
4,
5, unsigncd short int Width 5, Length ;
6, Length" 10 ;
7,
8: II kés zi t egyelo j e l nélkül i egész vál to z6 t , k ezdoértékként
9: I I a szél esség é s a hosszúság szorz atával fel tö l t v e
10 : un signed short int Ar e a " Wi d th * Leng t h:
11 :
12 : std: : c ou t « .. Sz é le s ség : • « Width « • \ 0 · ;
13 : std : : cou t « 'Hosszú s ág : • « Le ng t h « s td : : end l :
14 : std::co ut « ' 'l'erü let : « Area« std: : e nd l ;
15 : re tu rn O;
16 :

menet
Szé l esség : 5
Hosszú ság : 10
Terület : 50

Elemzés

Az első sorban beemeljük a cout utasítás haszmílat{jhoz szükséges iostream függvény-


könyvtárat az inclllde utasitás segitségével. A program a 3. sorban kezdődik.

Az 5. sorban a wi d th nev(!' vá ltozót előjel nélküli egésl.ként határoztuk meg, kezcl őér­
tékét S-re áJlítottuk. Egy másik egész típust is meghat{lroallnk Lengt h néven, cic nem
adtunk neki kezclőé rté ket. A 6. sorban rendeltük hozzá a ]0 értékel.

A tizedik sorban al. Area nev(!' egészet határoztuk meg, kezdőértékkém a Width és
Height v{lltozók szorzatát adtuk. A 12-14. sorig a változó értékeit írtuk a kimenetre.
Megemlítjük még, hogy az endl kulcsszó sortörést vált ki.

Apropó Sor lezárása


Az endl kifejezés a sor lezárásáért felel (end-L és nem end-egy). Általában így ejt-
jük: "end-ell".

typedef
Jelővel fárasztóvá, unalmassá és ami ennél is fontosabb lehetséges hibaforrássá válik
az unsigned short int folyamatos leírogatása. A typedef kulcsszó használatával szina-
nilllát készíthetünk egy már llleglév6 típusra, amelyet a változó típusának Illeghatáro-
zásánál használhatunk.
46 11.rász • Bevezetés a C+ + programozási nyelvbe

Fontos megkülönböztetnünk az új típus l6trchozását61. (Ezt a 7. 6ráb'ln az alaposztá-


lyok tárgyalásánál fogjuk megismerni.) II typede f használata úgy tört(:nik, hogy
a t ypedef kulcsszó után írjuk a meglévő típust, majd a típus új nevét. Például a
typedef unsigned short int USHORT

utasítás egy új tipusnevel hoz létre, amelyet mindenhová leírlJatunk, ahol unsigned
short int-nek kellene szerepelnie. A 3.3 Lista a 3.2 Lista kódjának olyan átalakítása,
amelyben típusmeghatároz{lsként az USHORT nevet h<lsználjllk az unsigned short int tl-
pllsnév helyett

3.3 Ulla - Atypedef ....... _ _ 'typedef.eppl

1 : II A typedef kulcsszó használat4nak bemutatása


2 : tincludc <ios t renm>
3,
4 : t ypede f unsigned short int USHORT : I ltypede f megha t ározás
5,
6 : int mainO
7: {
8, USHORT width 5,
9, USHORT Length;
10 : Lengt h = 10 ;
11: OSHORT Arca • Wi dth * Length;
12 : öt d : :cout « " Szóleaség : ' « Width « "\ n ' ;
13 : std : : cout « " Ho~~zúság : ' « Len'ijlt h « std : :endl ;
14 : !:ltd : : cout « "Terület : " « Area « std : : endl ;
15 : return O:
16 :

ICiIllll1ll
Szélessóg : 5
Hosszúsúg : 10
Terület : 50

Apropó Nem lényegtelen, ha szükségünk van rájuk


A fenti kódot lefordítva néhány ford ító figyelmeltetö üzenetet dob "konverzió során
lényeges számjegyek veszhetnek el" felkiáltással. Ez azért van, mert a 11. sorban
létrehozott két USHORT típusú változó szorzata nagyobb lehet, mint amekkorát egy
unsigned short tárolni tud, és ha ezt a szorzatat az Area változ6ba tesszük,
az a szám csonkításával járhat. A fenti esetben ezt az üzenetet nyugodtan figyel-

-
men kívül hagyhatjuk.

A 4. sorban a Lypedef segítségéve! meghalároztuk az USHORT lípust, amely az uns ig -


ned short int típus szinonimája. A program egyébként megegyezik a 3.2-es Listában
bemuratottal, és a kimenet is ugyanaz.
.3. óra • Vá~ozók és állandók 147 1
Úgy is gondolhatunk a typedef -re, nlintha az az alapjául szolgáló meghatározásI
(unsigned short int) helyeuesítené bárhol, ahol leírjuk (USHORT).

Mikor használjunk short és mikor long típust


A kezdd C++ programozóknak gyakran okoz fejtörést, hogy !lukar kell egy változóI
shon illetve long típusúként meghatározni. A szabály, miután megénettük, egészen
egyszeru. lia a legcsekélyebb esély van arra , hogy a kérdéses változóban táro lni kívánt
énék túl nagy lesz a v31lozó típusához képest, használjunk nagyobb mé re n1 típust.

Ahogya 3.1. Táblázatban láthaltuk, az unsigned short lípusCJ egészek, feltételezve hogy
2 bájl mérctt1ek, 65535-ig tudják ábrázolni a szflmokat. A signed short típusúak ermek
a feléig . Az unsigned long egészek elképeszl6cn nagy számokalludnak tárolni
(4294967295), de még mindig eléggé korlátozottak. lia ennél is nagyobb sz{irnok tárolá-
sá ra van szükségünk, használjtmk float vagy double típusokat, ezeknél viszont elve-
szítjük a pontosság egy részé t. A lebeg6pol11oS és dupla hosszúságú lípllsok hatalm,ts
számok tá rolás1írn képesek, de csak az első 7 illetve 19 számjegy lényeges a legtöbb
rendszeren. Ez azt jelenti, hogy az ezek utáni sz,ímjegyeket ;t sz;'i mítóg(:p le ke re kíti.

Előjel nélküli egészek túlcsordulása


Az a tény, hogy az unsigned long típusú egészek nem képesek tárolni bizonyos é né-
keknél nagyobbat , csak ritkán o koz problémál. De mi van akkor, ha kifutunk a memó-
riaterületl:>ól?

Amikor egy el6jel nélküli egész eléri a maximális értékét, körbefordul, és újrakezdi, ha-
sonlóan mint az autók kilométe rórája. A 3.4 Lista azt mutatja , mi törtétlik , ha túl nagy
érté ket pfÓb:1lunk egy short tipusú egész változ6hoz rendelni.

3.4 llIta - Bemutatja. mi t6rt6nlk. h8 1ú1nagy értéket pnlb6l••k _rendoIni oI6jo1


n6lkll11 og6u riltoz6h.. (toobiau.cpp)
o: #includc <iostrcam>
l,
2: int main()
3,
4: unsigned shor t int smallNumber;
5: smallNumber ~ 65535 ;
6, !ltd : : cout « • small number: • « smallNumbcr « std : : cndl;
7, smaIINumber++ ;
8, std : :cout « • small n~r:' « smallNumber « std : : cndl;
9, smaIINumber++ :
10 : std : : cout « 'smaII number : • « smalINumber « std : : endl;
ll : return 0,
12 :
48 11. rész· Bevezetés a C+ + programozási nyelvbe

small number: 65535


small number: O
small number: l

A 4, sorban a smallNumber nevű változót short im-ként határoztu k meg, ami a fultató
sz:'unítógépcn 2 bájt hosszúságú. Ez azt jelenti, hogy legfeljebb O és 65535 közötti szá-
mok tárolására alkalmas. Az 5. sorban a maximális értéket rendeltük a változóhoz,
amelyet a 6. sorban ki is írattunk.

A 7. sorban megnöve[tük a smallNwnber változó értékér, azn hozz1iadtunk l-et . A nö-


vt:l6 opedtor a ++ (úgy millt a C++ nevében: inkrementálisan SZ~\T'111azik a C-b(1).
A smallNumber változó értékének most elvileg 65536-n:lk kellene lennie, de
az unsigned short egészek nem tudnak 65535-nél nagyobb értéket t[lrolni. A válToz6
taltalma így körbefordul, vagyis O-ra álL Ezt ímttuk ki a 8. sorban.

A 9. sorban a smallNurnber érlékét újból megnöveltOk, az új értéke 1 lett, amelyet


szintén kiírartunk.

Az előjeles egészek kömefordulása


Az el6jeles egész abban különbözik az el6jel nélküli egésztől, hogy a l ehe~ges érté-
kei nek fele negatív. A hagyományos kilométer6m helyet képzeljünk el egy olyat,
:Imely előre forog a pozitív- , hátrafelé a negatív számok esetén. A nullától mért egy ki-
lométer távolság lehet l és -1 is. Amikor kifulunk a pozitív számokból, rögtön a legna-
gyobb negatív számf:1 ugrunk, és innen számolunk visszafelé nulláig, A 3.5 Lista azt
mutatja, hogy mi történik, ha hozzáadunk l -et az unsigned short egész típusú vá!(O-
z6nk ImximáJis értékéhez.

3.5 Us" - B......tja, ml történik, ha túl nagy értéket pnlbálunk hozz6rendelni al6101..
ogÓlZ vához6hoz jtoobigs.cpp)

o: Jlinclude <iostream:>
1.
2: int main()

<I: short int srnallN~r :


5: smallNumber = 32767 :
6: std: :cout « 'smal l nWllber:' « smallNumber « std : : end!;
7: smalINumber~~ :
8: std: :cout « 'small number:' « smallNumber « std: :endl:
9: smalINUmber~~ ;
3. óra • Változók ás állandók ( 49

10 : std : : cout « "smal l number : ' « sma l lNumber « std: : endl;


ll: re turn O;
12 :

smal ! number : 32767


smal l number : - 32768
small numbe r : - 32767

r
A 4. sorban a s mallNumber neVÍÍ változ6t ezúttal signed shan típusú egészként határoz-
tuk meg, (Ha külön nem adjuk meg, hogy előjel nélkülit szeretnénk, automatikusan el ő­
jeleset kapunk.) A program ub'Yanúgy folytatódik , mint az el őző esetben, de a~ ered-
mény egész más. I !ogy j61 megértsük II fenti eredményt, tudnunk kellene, hogy egy elő­
je les egbz ponlosan hogyan is ábrázo16dik b it szinten, vagyis k6L bájtot elfoglalva .

A lényeg mindazonáltal az, hOh'Y habár pont úgy llHlködik, mint egy előjel né lküli
egész, az előjeles egész a legnah'Y0bb pozitív énékéh61 a legkisebb m.:gmív értékébe
fordu l át. A -32767-r61 csökkentjük az értékél, -32768-at kapunk, tovább csökkenlve
32767-et, 32766-ot, és igy tovább.

Állandók
A válloLÓkhoL hasonlóan ,lZ <Í lland6k is adaltároJ6 memóriateriiletek. Ám amíg a vá lto-
zó k értéke V[tllOzhal, addig, ahogy m{tr bizonyára rájöttünk, aL áll,mdóké nem.

Meghalározáskor az állandónak kezdőérlékel kell adnunk, k ésőbb aztán m,ír nem ren-
delhetünk hozzá új értéket. Miutá n egy {tllandó megkapta a ke zdőé rtékét, n evébő ! ere-
d6en az éltéke állandó.

A C++ kétféle állandót ismer, ezek: nyelvi állandó (litera!) és nevesített állandó
(symbolic).

Literális állandók
A literális állandó egy olyan érték, amit egyenesen a programk6dba írunk, ahol arra
szükségünk van.
i nt myAge " 39 ;

A myAge egy int tipusú változó, a 39 pedig egy nyelvi állandó. A 39-hez nem rendelhe-
tünk értéket, és nem is változtathat juk meg azt.
50 II. rész· Bevezetés a C++ programozási nyelvbe

Nevesített (symbolic) állandók


A nevesített állandók olyan állandók, amelyeket a nevük azonosít, akárcsak a változó-
kat. A változókkal ellentétben azonban az értékük a bevezetést követ6en nem válLaz-
tatható meg. Ha van egy programunk egy tanul o k és egy oszt alyo k nevő vállOz6val,
az osztályok sz.,"ímának segít.<;égével ki tudjuk számítani , hány lanulónk van, ha tudjuk,
hogy minden osztályba 15 tanuló jár:
tanulok ~ os z talyok * 15 ;

Apropó Az első operátorunk


A • operátor a szorzást jelöli. Az operátorokról később bővebbe n szót ejtünk.

A fent i példában a 15 nyelvi konstans. A kódunk könnyebben karb:tntarthat6 és olvas-


ható volna , ha ezt az értéket egy nevesített állandóv:tI helyeuesítenénk.
t~nulo k = oszt ~ lYOk ~ oS7.talylet$zam :

Ha a későbbi ekben úgy dönten&nk, hogy megváltoztatjuk a tanulók számát az egyes


osztályokban, megtehetjük azon a helyen, ahol bevezettúk osztaletszam állandót, anél-
kül, hogy a programban [m1shol bármit is vt'tltoztatnunk kellene. igy ni ncs az a kocká-
zat, hogy esetleg valahol elfe le jtjü k átírni , ami azt.'Ín nehezen kinyomozható logikai hi-
bát eredményez.

Állandó bevezetése a #define kulcsszóval


Ha a régimódi, veszélyes, részben helytelen módon szeretnénk álbndót bevezetni, így
tehetjük meg:
ndefi ne oszta l ylet8zam 15

igy határoztuk meg az állanclókat a régi C változatokban. AztflO az ANSI szabvliny be-
vezette const ku1csszót a C nyelvbe is. Figyeljük meg, hogy a osztalyleL'i7..am állandó-
nak nincs rögzített típusa (int, cha r stb.). A #de fi ne egy egyszeru szövegbehelyette-
sítést csinál. Valahányszor az el6feldolgoz6 a osztalylctszam szóval talá lkozik, a 15 ér-
téket teszi :il helyére a k6dszövegben.

Mivel az el6feldolgozó a fordítóprogram el6tt fur, a fordító soha nem ta lálkozik az ál-
landónkkal , a 15-ös számot látja hdycne.
3, 6ra • Változók és 51

Állandó bevezetése a const kulcsszóval,


Hab,ír a IIdefine is mt1ködik, a C++-ban van egy jobb, kevésbé egészségtelen és sok-
kal ízlésesebb módja a konstansok bevezetésének:
const unsigncd short int o~ztaly let szam = 15 ;

A remi példa szimén egy osztalyle tszam nevű nevesített állandót vezet be, dc ebben
az esetben az álland6nak típusa is van: unsigned short int.

Tovább tart ugyan begépelni , de a használata s7..ámo.~ d6nnyel jár. A legfontosabb kü-
lönbség, hogy az állandó típussal rendelkezik, így a rordító kényszerít bennünkel,
hogy a típusának megfelele> módon haszn~Hjuk.

Felsorolt (enumerated) állandók


A felsorolt álland6k ii llandók halmaz.át jelentik. Meghatározh:llunk például egy felsoro -
lást SZIN néven, majd meg:ldhatjuk, hogy erulek ötféle értéke lehet: PIROS, KEK, ZOLD,
FEHER és FEKETE.

Nyelvtanilag 3 felso roiL állandók bevezetése a következ6 képp néz ki: használjuk
az enum kulcssz6t, II típus nevét, majd kapcsos zárójelek közt: írjuk vesszővel elvá-
lasztva a lehetséges énékekcl. íme egy példa:
en\lm SZIN ( PIROS , KEK, ZOLD, FEHER , FEKETE ) ;

A fenti utasítás két feladatot hajt végre egyszerre:

l. Egy felsoro lást vezet be SZIN néven , azaz létrehoz egy új típust.
2. Létrehoz egy PIROS nevű nevesíten állandót O értékkel, egy KEK nevű nevesített
álland6t 1 énékkcl, egy ZOLD nevű nevesített állandót 2 értékkel, és így tovább.

Minden felsorolt állandónak megfeJel tehát egy egész érték is. I [a külön nem adjuk
meg, akkor az első ,\llandó a O ért.éket kapja, a többi pedig mindig eggyel nagyobb
az e16zŐnél. Az állandóknak persze adhatunk egy meghat:írozott értéket is. Ilyenkor ha
vala melyiknek az értékét nem határozzuk meg explicit m6don, akkor az az el6tte le-
v6nél eggyel nagyobb értéket kap. Lássuk mindezt egy péld:ín keresztül:
cnurn S zin { PIROS=lQO, KEK, ZOLD=500, FEHER, FEKETE=700 J;

A PIROS értéke 100 lesz, a KEK értéke 101, a ZOLD értéke 500, a FEHER értéke SO l , vé-
gül pedig II FEKETE nevúé 700.

Ermek a megoldásnak az az előnye, hogy névvel hivatkozhatunk az amúgy jelentés nél-


kiHi sZámokrd, jelen esetben 1 vagy 700 helyett azt írhatjuk, ho&'Y FEKETE vagy FEHER.
52 11. rész · Bevezetés a C++ programozási nyelvbe

Bevett szokás, hogy mind a hagyományos, mind ~t felsorolt állandók neveit csupa
nagybetűvel írjuk. Ez első látásra is nyilvánval óvá teszi, hogya nb, amit olvasunk, egy
állandó, és nem valamilyen változó, amelynek megváltoztathatnánk az értékét. A fordí-
tóprogram maga különben nem sokat tö rődik azzal, hob')' következelesen nevezzük-e
el a változókat és az áIJandókat, ezzel tehát csak a saját és embertársaink életét
könnyítjük meg.

Kérdések és válaszok
Kérdés: Hu ti short illf lípusok tlilcsordu/halnak. mié/111em hasz ná/unk nlÍlIdig /ollg
típl/sli (xUtozókal?

Vá/asz: Mind a short mind a long egész tipusok tú1csordulhatnak, de n long típusú
egészek ezt sokkal nagyobb számok esetéhen teszik csak. Ugyanakkor a legtöbb gé-
pen a hosszú egészek kétszer annyi memóriát fogb lnak. Nyilván, ez ma már kisebb
probléma, mim régen, mivel a legtöbb személyi s7!tmítógép memóriája több milliónyi
(ha nem milliárdnyi) bájllJÓl áll.

Kérdés: Mi /ór/éllik, ha lelx!gfipon /os típllS helyett ep,C-szhcz ref/ddtil/k Ifzedcsjc8gyel


nmdelkczó szá mot, //Iint pé/dálll az alábbi sorba II :
int aNumbcr z 5 .4;

Válasz: Egy jó fordító progr::un kűld ugyan egy figyelmeztetést az efféle megoldásokra ,
de az érté kadás ilyen módj.t megengedett. A hozzá rendelt számot a fordító ilyenko r
egésszé csonkolja. így teMr ha az 5,4 értéket rendeljük egy egész rípusú változóhoz,
annak értéke egyszt!ruen S lesz. Ügyeljünk rá , hogy ez a művelet adatveszréssel jár, va-
gyis kés6 bb hiába próbáljLIk meg az egé.~z típlts(t változónk érték61 cgy lebt!g6p()nIOS
lípusúba áthelyezni, annak az értéke is 5 lesz.

Kérdés: Miért ne haszlláljllnk Ilye/vi OiterálisJ állwuJókati miét1 kell nevesftelt ál/clI/-
dók bevezclésével töltentlllk a drága idŐn/..'(ft?

VálCIsz; lia sok helyen használjuk ugyanazt az értékeL egy progmmban, akkor a neve-
sítdt kons\;\nsok segíL<;égévcl az összes előfordulásl megvá itoZlalltaLjuk ügy, hogy
egyetlen helyen árírjuk az érlC:ket. A nevesílett állandók ráadásul elárulják magukról,
hogy mksodák. Általában például nehéz lehel rájönni, hogy egy számo t miért is szo-
rozumk mt!g éppen 90-nel, dt! ha azt olvassuk valahol a kódban hogya derekSzog
sokkal könnyebb dolgunk van .
3. 6ra • Vá~oz6k és állandók 153

Gyakorlatok
Most már kip róbálhatjuk a változók és állandók műk ödését. Válaszoljunk erre a né-
hány ké rdésre, és csin álju nk felada to kat, hog)' meger6sítsük a ford ító p rogram mű kö­
désével ka p csolatos tu dásunkat.

Kvíz
1. Miért célszerű előjel nélküli változ6kat használni előjelesek helyett?
2. Mi a különbség a kezdóérték megadása és a változó meghatározása között?
3. Azonosak-c II DOG, dog, Dog, és a doG változ6k?
4. Mi a különbség a IIdefine és a const ut.1sítások között?

Feladatok

1, Egészítsük ki rt 3.1. Listában sze replő programot úgy, hogy az összes, a 3.1. tllb-
lázalbar1 szereplő változ6típusr tarta lmazza. A kapolt értékek e l térőek lehetnek
azoktól, amelyek a táblázatb,lll szerepelnek!
2, Gondolkozzunk el azon, hogy mílyen számokkal és egyéb információval tal<1l-
kozunk mindennapi életünk során, Milyen típusú változ6kban lenne a legjobb
ezekcllárolni? Milyen beszédes ne veket adhatnánk ezeknek a vllllozóknak?
3. Ha van tapasztalt programfcjlesa6 ismcr6sünk, kérdezzük meg t6le, hogy mi-
lyen változ6elnevezési stratégilll haszn:ll? Ha elég sok ismer6sünk van, tapasztal-
ni fogjuk , hogy különböz6 módszereket alkalmaznak. Kérdezzük meg 6ket,
hogy miért. Lehet, hogy meglep6dünk majd a válaszokon,
1. Az előjel nélküli egészek nagyobb pozitív sámok tárolására alkalmasak, mint
az el őjelesck . Ugyanakkor nem vehetnek fel negatív értékeket. Programozóként
a mi feladatunk eldönteni , hogy melyik a l(:!gjobb választás egy konkrét problé-
ma megoldására.
2. A meghatározás (definíció) az, amikor adunk egy típust és egy nevet egy később
hasznáhli kívánt változónak. A kczd5érték megadás.1 (jnicializáci6) ezzel szem-
ben az első érték hozzá rendelése a7. adon változóhoz. A meghatározás és a kez-
d6érték megadása szerepelhet ugyanabban az utasításban is.
3. Nem, nem azonosak. A C+ + különbséget tt!sz a kis- és a nah'Ybetúk közőtt, így
mindegyik írásmód küJönböz6 vá ltozókat jelent :1 fordít6progmm sz.imám. Azt,
hOh'Y milyen elnevezési konvenciót használunk , nekünk, vagy a Illunkacsopor-
runknak kell eldöntenie.
4. A IIdefinc egy <ll.: eJ6feldolgoz6nak szól6 utasítás, :Imely névvel helyettesíti
a meghatározot! értékeket (nyelvi álland6kat). Hatására az e16feldolgoz6 a név
miden d6fordulási helyre behelyettesíti a megadott t:rtéket. A const min6sít6vd
bevCl.:clett változó ezzel szemben csak Cgy helyen fordul e16, és van típusa is.
4. ÓRA

Kifejezések és utasítások
Ebben az órában a következ6króllesz szó:

• Mi az utasítás?
• Mi a kifejezés?
• Mtlveletek operátorok kal
• Ig:lZ vagy hamis - logikai műveletek

Utasrtások
Utasításokbl idnyítjuk a program fu tását, értéke1hetünk ki kifejezl!sckct, vagy speciális
esetben (null) nem hajtunk végre :;emmit. C++-ban minden utasítást pon tosvessző zár.

íme egy példa egy nagyon egyszeru utasításra:


x = a 1" b;

A matematikával ellentétben ez nem azt jelenti, hogy x egyenl6 a és b összegével, ha-


nem azt, hogy a és b összegét rendeljük x-hez. Röviden: a és b összegét helyezze el
a program x-ben. Noha látszólag két műveletet hajtunk végre ezzel az utasításS<'lI, még-
=---==='--'-====_ _________
56 11. rész· Bevezetés a C++ programozási nyelvbe
__----=-1.::-

is csak egynek számít, ezért csupán egy pontosvcssz6vel zárjuk le. A hozzárendelési
operátor a jobb old:llon található kifejezés eredményét hozzárendeli a balolda lon talál-
ható elemhez.

Űres helyek

A szóköz, a vízszintes tabulátor és az újsor karaktereket úgynevezett üres karJkterek


(whitespace). A fordító automatikusan fib'Ye\me n kívül hagyja az felesleges üres helyeket,
íb'Y egy szóköz helyére nyugodtan írhatunk tabul<1tort vagy újsor karaktert is. Az üres ka-
rakterek els6.sorTh1n az olvashat6ságot javítják, a fordító nem foglalkozik velük.

A korábban bemutat.ott él1ékadásunk:

írható akár így is:


x .a
• b

Noha a második megoldás is hibátlan, mégis butaság. A tabulálás kétélű fegyver. Meg-
felelő alka lmazása eseté" megkönnyíti a fo rráskód olvasását és karbanlartását, ha
nonban ÖSS:lCviSS7';:1 használjuk teljesen érthetetle" zagyvaság lesz az eredmény. Maga
a C++ amúgy a józan íté l6 képességünkrc bíZ"';:1 a dolgot.

Pontos megjegyezi, hogy az üres kamkle rek lényegesek egy változó, vagy egy fü gg-
vény nevében, dc ne m LlgyHnazt jelentik, mint Cgy közönséges kamklerláncban.
A secondsPerMinute például teljesen m:1st fog jelenteni, ha seconds Per Minute for-
mában írjuk le, (!kkor ugyanis a fordító három különböző változókénl fogja kezelni.

Összetett utasftások
Minden olyan helyen, ahol szerepelhe t egy egyszeru utasítás, használhatunk összetett
utasítást (compollnd staternent) is. Az összetett utasítás nyitó kapcsos zár6 jelle l n{" kez-
d6dik, és záró kapcso.~ z:1r6jellel "}" végződik.

Minden utasítás pontosvesszővel zárul, dc maga az összetett utasítás nem, amint


az a következő példában is láthatá:

temp'" a ;
a b-
b " temp;

Ez a kód az a és b változók tartalmár cseréli fel.


4. óra • Kifejezések é. uta. ltá. ok 157

I
Minden nyitó kapcsos zár6jelnek le- Összetett utasításaink sose It::gyenek
gyen záró pá rja . túl hosszúak, hogy átláth:rt6 mardd-
Minden utasítás pontosvessz6vel zá- jon a kód.
rul.
Forrásk6dunkban pr6bálju k meg ér-
telmesen használni <lZ üres karaktere-
ket úgy, hogy :. zok ;:rvílsák annak
o lvashat6ságál.

Kifejezések
A kifejezés olyan ul:lsÍ\.1s a C++-b:lll , amely é rtéket ad vissza. Minder' kjf~j cz(:s ulasíl:í s
teh5t, de o{:!m minden ut,lsít:1s kifejezés.

Sok kódrészlet mcglcp6 mó<lon kifejezésként viselkedik, ak:í rCSilk a kÖvr.:Lkc:.::6 három
példa:
3.2 /1 visszaad ja a 3 . 2 értéke t
PI /1 lebeg6pontos á l landó , mely a 3 .1 4 et adjo vissza
SecondsPerMinutc II egé~ z t ipusú ko ns t ans , maly a 60-a t adja viss za

Tekintve, hogya PI egy konstans, amely 3. 14-gyel egyenl6 , illetve a SecondsPe r -


Mi nut e szintén konstans és 60-al egyenlő, mindhárom utasítás egyben kifejezés is.

Az x = a i' b nem csupán összeadja az a-t és b-t, valamint hozziÍrendeli az e redményt x-


hez, dc visszaadja az x értéké! is. Ez :L művelet igy szi nté n kifejc:d:snek számít. Mivel
pedig kifejezés, szerepelhet egy é nékadó operátor jobb oldalán a következ6képpen:
y _ x = a i' b;

A hozzá re nd e l ~s i vagy C:rtékadó operátor (=) a baloldalon sz erep l ő értékel fe lülírja


a jobb olda lon Található kifejezés énékével.

Az o pcl1U1dus egy matematika i kifejezés, ami azt az objektumot jelenti, amelyen


az operátor segítségével valamilyen műveletet hajtunk végre. Az imé nt bcmutHoTt kód-
sor például az alábbiak szerint hajtódik végre:

• A program összeadja a és b énékét.


• A kifejezés eredményét hozzárendeli x-hez.
• A hozzárendelés Ilunt kifejezés eredményét hozzárende li y-hoz.
58 11. rész • Bevezetés a C+ + programozási nyelvbe

Ha a, b, x és y egészek vallak és a énéke 2, b értéke pedig 5 VOll, akkor x és y értéke


egyaránt 7 lesz. A 4.1 Listában bemutatjuk néhány összeteU kifejezés használatát.

O: ,j nclude <iostrearn>

"2 :
3:
int main()
{
4: int a=O, b"O, x..,O, y ~ 35:
5: std : :cout « 'beforc a : • « a « • b : • « b;
6: std :: couL« • x: . « x« • y : ' « Y« std :: endl ;
7: a " 9;
8: b " 7;
9: y _ X " a+b;
10: std : : cout « 'after a: ' « a« b :' « b;
11: std : : cout « • x: " « x « « y « !:ltd : : endl:
12 : return O;
13 :

beforc a , ° ° °
b:
after a: 9 b: 7 x : 16 y : 16
x: y : 35

A 4. sorban deklar~iljuk és inicializáljuk a változókat. Az 5. és 6. sorban kiíratjuk az éc*


tékeiket. A 7. sorban az a ért6két 9*re v{lltoztatjuk, a 8. sorban a b értékét pedig 7*re.
A 9. sorban kisz:í.moljuk a és b összegét és hozzárendeljük azt x*h(!z. Miután az x=a+b
kifejezés kiértékelése megtörtént, ennek eredményét hozzárendeljük y*hoz.

Operátorok
Az operátor olyan jel, amely utasítja a fordítót a megfeJel6 műve let elvégzésére.

~rték.dó operátor

Az értékadó operátor baloldalán található kifejezésI balél1éknek (I*va]ue), a jobb oldali


operancJust pedig jobbénéknek (r*value) nevezzük.

Apropó Konstans csak jobbérték lehet


A konstans jobbérték, fgy nem szerepelhet értékadás baloldalán.
x ,, 35 ; /1 így helyes
35 = x; /1 ez helytelen , konstans nem s zerepelhet baloldalon
4, óra • Kifejezések és utasftások 59

Abalérték (l-value) a kifejezés bal oldabn szerepelhet, míg ajobbérték (r-value) a kife-
jezés jobb oldalára kerül. Jegyezzük meg, hogy ntinden balérték szerepelhetnek egy
kifejezés jobb oldalán is (vagyis lehet jobbérték is), de fordítva már nem fel tétlen igaz
az állítás. Például az x =5 ; helyes, míg az 5 =x; helytelen.

Apropó Miért figyeljünk a bal- és jobbértékekre?


Bizonyára felmerült az olvasóban a kérdés, miért kell megtanulnunk efféle furcsa ki-
fejezéseket mint balérték meg jobbérték? Nos, az egyik ok az, hogy a fordítási hiba-
üzenetek megértéséhez ez is fontos. Ha nem értjük e két fogalom közti különbsé-
get, néha nem fogjuk tudni, hogy mi is a probléma oka. Azt is fontos átlátni, hogy
nem fogalmazhatunk meg akárhogyan egy adott kifejezést. Vannak helyes és hely-
telen alakok, amelyek elsö olvasásra logikailag egyformának tűnhetnek.

Matematikai operátorok

Összesen öt matematikai openhor van: összeadás (+). kivonás C- ), szorzás C*), osztás
CJ), és maradékos osztás (%). A C++ a C-hez hasonlóan azon kevés nyelvek egyike,
ahol nincs beépített hatványoz6 operátor ex
az Y-adik IHltványon). Ehelyett függvényt
kín:íJ a felad::lt megolclflsára.

Az összcad{ls, a kivonás és a szo rzá~ úgy viselkedik, ahogy várjuk. Nem így azonban
az osztás.

Az egész számok közti osztás kicsit különbözik a matematikában mcgszokottóJ. Nor-


m{tlis esetben ha a 2"]-et elosztjuk 4-gye!, akkor egy valós számot kapunk (a v,116s sz-tlln
egész- és tőrtrészből áll) . Az egész típusú v,íltoz6k azonban nem rendelkeznek tört-
résszel, így ebben az esetben a C++ egyszerűe n levágja a Jőlősleget". így aztá n a 21/4
kifejezés C++-ban dolgozva 5-öt ad eredményül.

A mamdékos osztás vagy modulo operátor Ct) a fenti egészosztás maradékát adja
vissza . 21 % 4 például l-et ad, hiszen 21/4 =5 és a manldék 1.

Apropó Amaradékos osztás ról


,,21 % 4"-et huszonegy modulo négynek olvassuk. A modulo az a műve l et, amelyet
a maradékos osztás operátora végez el. A modulo művelet végeredménye az osztás
maradéka (modulus), ami példánkban 1.

Meglepő múdon a mamdék meghatározása igen hasznos lehet. Jó példa erre, ha egy adOlt
műveletet egy másik művelet ntinden tizedik futásám akarunk c.~ak végrehajtani.

Világos, hogy minden 1O-zel oszthatú szám lO-es maradékos osztás esetén nulla mara-
dékot ad. Így például nullát ad eredményül a 20 % 10, a 30 % 10, és így tO\;ább.
60 II.rész· Bevezetés 8 C+ + programozási nyelvbe
A lebeg6pontos osztás ugyanúb'Y viselkedik, mintha 7_-;ebszámológéppel hajtanánk
végre, vagyis a C++-ban ez az "igazi" osztás. Ezzel a múvelettel helyreáll a világ rcndje,
vagyis 21/4.0 - 5.25. Ha bármely operandus lebeg6pontos, akkor az eredmény is le-
beg6pontos lesz.

Matematikai és értékadó operátorok kombinálása


Gyakr::ln cl6fordul, hogy egy változó értékéhez hozzá szeretnénk adni egy másik szá-
mot és az eredményt az eredeti változóba visszaírni. Például Im a myAge változót szc-
retnénk kCH6vel növelni, akkor azt így is írhatnánk:
int myAqc ,. 5 ;
int temp;
tcmp • myAge + 2 ; II összeadjuk az 5-öt 65 a 2-t majd ' mentjo.k "
II a temp változ6ba
lny Age .. temp ; II a melyet aztán itt vis.nahelye:r.ul1k a myAge változ6ba

Lássuk be, ez elég nyakatekert és helypazarl6 megold(ls. C++-ban Szerencsére egy vá l-


tozó egyszerre szerepelhet a hozzárendelési operátor mindkét oldalán:
myAge ~ myAge ! 2;

Ugye mennyivel szebb?! Matematikában ez a kifejezés értelmellen, de C++-ball azt je-


lenti, hOgy növeljük meg kett6vel a myAge változó értékét, majd rendeljük azt hozzá
magához a myAge változóhoz.

Meg egyszerűbb - noha kezdetben kevésbé átlátható - írásmód II köverkez6:


myAge += 2 ;
Az önhozzárendel6 összeadás operátord (+=) (self-assigned addition) hozzáadj:1 a jobb
oldalt :l balhoz és az eredményt hoz7.árendcli a baloldalhoz. Szóban ~ plusz-eb'Ye nI6~
oper:.'itornak hívjuk, vagyis jelen példánkat myAge plusz-egyenl6 kettőne k olvassuk.
lia például a myAge értéke 4 volt, akkor a fenti múvel!;:t után 6-r;\ vá ltozik.

Önhozzárende l ő operátor egyébként bármely matematikai operátorból hasonlóan ké-


pezhető: kivonás ( -=), szorzás (*=), osztás CI,;), valamint a mamdékos OSZlás (t=).

Növelés és csökkentés
Leggyakmbba n egyet adunk hozzá egy változóhoz vagy vonu nk ki bel6le. Az eggyel
történő növelés az inkrementálás, az eggyel tönén6 csökkentés pedig a dekrementálás.
A C++-b:1I1 ezek a műveletek saját operátorral rendelkeznek.

Az növe l ő operátor (H) eggyel növeli a változó értékét, a csökkentő operáto r (--) pe-
d ig eggyel csökkenti. Például ha a c változót növei ni szeretnénk, azt a követk ezőkép
teheljük meg:
C++ ; II a c é rtékét megnöveli eggyel
_ _ _ _ _ _ _ _ _ _ _ _ __ _ _ _ _ _---'4::..:ó:r=a -·"Kife
= je"zó", """s"ltá",::ok.,l__s,-l- - ---C'.
"=k."ós,-ut

A fenti sor végeredményét tekintve eh'Yenértékű ezzel


c '" c + l;

illetve mint ahogya korábbi példák mutatják akár ezzel is:


C += 1 ;

Tudta hogy...? Honnan szánnazik a C+ + elnevezés?


Miért is hfvják C+ +-nak azt a nyelvet. amit éppen tanulunk? A C+ + nem teljesen
új nyelv, hanem a C nyelv továbbfejlesztése, egy szinttel val6 HnöveléseM,

Elótag Iprefix) és utótag Iposlfix) operátorok

Mind II növelő CH), mind pedig a csökkentő (--) operátor ha~zn;ilhal6 d6tagkEnt
(prefix) és ut6tagként (poslflX) egyaránt Prefix esetén (a ~p re" jelentése: el6) a z operá-
tort II változó elé írjuk (példáu] ++mYAge), míg postfix haszn51al eselén ( a ~pOSl" jelen-
tése: utó) az operálorr II vállOZÓ neve után írjuk (példfíu] myAgc++).

Egyszerűbb utasításoknál általában mindegy, hogy melyik alakot h:lsználjuk. Ha azon-


ban az inkrcme ntál6 vagy de krementál6 kifejezés egyben értékadás része, már egyálta-
I:ín nem mindegy, hogy .elöl" vagy . hárul" van az operátor. A prefix operiítor ugyanis
a hozzárendelés el6u ért é kelődik ki , míg a postnx csak hozz:'írendelés után .

A dolog logikája röviden: prenx esetén előbb növeljük a változ6t és utána eZI használ-
juk az értékadásnál, pűSlfi x esetén viszont elóbb kiolvassuk az é rtéket, elvégezzük
az értékadást és csak utána növeljük a változó tartaimát eggyel.

Elóször L-waros lehet, de nézzük csak az alábbi példát, ahol x eredeti é rtéke legyen 5:
int a ,. ++ x;

A fordító a lln1velet hatására előbb növeli az x-et (6 lesz) és utána eZl kapjuk m~g
az a változóban. Végül te hát mindkét változó értéke 6 lesz.

Ha viszom így pr6bálkozunk:


int b '" x++ ;

akkor ill a fordító fogja az x értékét, beleteszi a b változ6ba, majd pedig növeli t!ggycl
az x-el. Következésképp a b értéke 6 lesz, az x értéke viszont 7. A 4.2 Listában jelen
van egyszerre mindkét típus.
62 11. rész· Bevezetés a C+ + programozási nyelvbe

4.21i1ta - prefix ól POIIfiX . . . . - t.....-x.cpp)


o: II 4.2. Lista
l: II Operátorok p r e fix és postfix használatának
2: II bemutatása
3: 'include <iostre arn>
4.
5: int maln()
6: f
7. int myAge : 39 ; II é r té ke k beállitása
8. int yQurAg e : 39 ;
9. s t d : : cou t « " I am: \ t" « myAge « "\tyears old . \n" ;
10 : std : : cout « "You are : \t" « you rAge « "\tyears old\n ";
11 : myAge+ +; II postf ix növelés
12 : !+y ou rAgc ; II p ref ix n Ov elés
13 : s t d : : oD u t « "On e y ear p asse~ ... \n";
14: s td: : oou t « "I a m:\t" « myAge « " \tyear s old. \ o';
15 : s Ld : : oout « "You a r e : \t " « y ou r Agc « " \tycars old\ n ";
16 : o t d : : oout « "Ano ther y ear passes \ o" ;
17: std : : oDut « " I am :\ t " « myAgc+ + « "\tyears o l d .\ n ";
18 : std : : oout « "You a r e : \ t" « ++ yourAQe « "\tyears o l d\n ";
19 : st d: : oout « " Let ' s print it aga in. \ n " ;
20 : sLd : : cout « " I arn : \t " « myAge « "\tycars old . \n" ;
21 : std : : oout « ' You are : \t " « yourAge « " \tyeilrs old\n" ;
22 : return 0 ,
23 :

I arn 39 years old


You arn 39 years old
One year passes ...
I am 40 yea r s old
YOU are 40 years old
hnother year passcs
I am 40 y ears old
You are 41 yea rs o l d
Lct' s print i t agai n
I am 41 y ca r s o l d
You ure 41 yea rs o l d

A 7. és 8. sorban két egész változót deklarálrnnk és beállítottuk mindkett6 értékét 39-


re. Él1ékeiket a 9. és 10. sorokban írat juk ki.

A Il. sorban prenx módon növeljük a myAge változ6t, a you rAge változ6t a 12. sorban
pedig postfL'{ módon. A végeredményeket a 14. és 15. sor itatp ki. Mindkettő egyaránt 40.
4. óra • Kifejezések és utasftások 163

A 17. sorban a myAge változó értékét növeltük a kiíratás alatt postflx módon, emiatt
szintén 40-e t írt ki és csak később növekedell az értéke. Ezzel szemben a 18. sorban
prefLx növelést hajtottunk végre a yourAge változón, a képernyőre kerüld érték ennek
megfelelően 41 lett.

Végü l a 20. és a 21. sorokban ismét kiíratmk a két változó énékét. Mindkét változó ese-
lén megtörtén a növelés mindkét esetben így a myi\ge és a yourAge is 41 letl.

Apropó Ha háromféleképp csináihatom ugyanazt, melyiket válasszam?


C++-ban az elmondottak alapján háromféle módon növelhetjük egy változó értékét
eggyel: A=A+l , A+=l vagy AH . Mármost joggal kérdezheti az olvasó, vajon me-
lyiket és miért az érdemes használni.
A processzor felépítése miatt jó ideig az A+= l hatékonyabb volt az A=A+l-nél, il-
Ietve az A++ hatékonyabb volt az A+ =l-nél. Egyes helyeken ez va lószínűle g ez még
ma is igaz, de az optimalizálást végző fordítók manapság igen hatékony gépi kódot
álHtanak elő.
Én személy szerint a t- + operátort szoktam használni, ha nyilvánvalóan eggyel sze-
retném növeini egy változó értékét. Ha valami más értékkel szeretném növelni, ak-
kor a += operátort használom, míg több változó összegzése esetén a = és t operá-
torokat alkalmazom a "klasszikus" módon.
A különböző helyzetekhez igazodó különböző megoldások számomra átláthatóbbá
teszik a forráskódot.

Precedencia, avagy a műveletek végrehajtási sorrendje


A precedencia a műveletek végrehajt,ísi sorrendjét jelenti.

Egy olyan összetett utasítás, mint az


x=5+3 ~ 8 ;

felveti a kérdést: az összeadást vagy a szorzásl hajtjuk végre először? Ha az összeadlíst,


akkor az eredmény 64 lesz, ha pedig a szorzáSl, akkor 29, vagyis a dolog egyáltalán
nem mindegy.

Minden opt!rátor re nclelke:lik precedencia s:únttcl. (A részletes lista a könyv elején ta-
lálható.) Esetünkben a szorzás például magasabb rangú művelet , mint ai összeadás,
így a helyes válasz a 29.

Ha két operátor ugyanolyan precedenciával rendelkezik, akkor balról jobbra haladva


hajtjuk végre a megfelelő műveleteket. Például az
x = 5 + 3 + 8 * 9 + 6 * 4;

eserén a 57:0fzások értékekXlnek ki először: 8"9-72, 6*4-24.


64 11. rész· Bevezetés a C+ + programozási nyelvbe

Ezután a kifejezés így néz ki:


x - 5 + 3 + 72 + 24 ;

Balról jobbra haladv;t ,tZ összeadásokkal: 5 + 3 ... 8, 8 + 72 = SO, 80 + 24 ... 104,

Figyelem! Néhány oper{ILor esetén - ilyen például a hozzárende1és is - jobbról balld


történik a kiértt':kcl6dés.

Mi a helyzet, ha a precedencia sorrend nem megfelel6 számunkra? Nézzük eZt a peldát;


TbtalSeconds - NumHinutesToThink + NumHinutesToType ~ 60
Ennél a kifejezésn(Ol nem a NumMinutesToType ~rté két szeretnénk hatvannal megszo-
rozni, majd hozzáadni a NumMinutesToThink értékél, hanem el6bb elvégezni
HZ összeadást, majd pedig a szorL.ást.

A z{jrójelezéssel il yen esetben felülbírálható a precedencia. A zárójelben !év6 elemek


bármely matematikai operátorr.tl szemben els6bbséget 6[veznek. A mi példánk ennek
megfelel6en így néz ki;
TotulSeconds = (NumMinutesToThink + NumMinutesToType) • 60

Tudta hogy... ? Zárójelezés


Bizonytalanság asaMn bátran zórójelezzünk. A program nem lesz lassabb és a fordl-
tási idö növekedése is elhanyagolható.

Egymásba ágyazott zárójelek összetett kifejezésekben

Összetett kifejezéseknél el6fordllllm , hogy egy m ~sba kell ágyaznunk zár6jelpárokat.


Az előző példánknál m:tr.ldva tegyük fel , hogy nem csa k a másodperceket kell kiszá-
molnunk.
TotalPcrsonSeconds ( ( (NumMinu tesToThink + NumMinutesToType ) ~ 60) ~
(People l nTheOffice + PeopleOnVacation) )

Ezt a bonyolult kifejezést belü l1'61 kifelé haladva értékeli ki a fordító, Először
a NumMi nutesToThink váiL07:ót adja hozzá a NumMinutesToType-11OZ, mert ez a leg-
belső zárójelpár. Az összeget megszorozza 60-nal. Ezután a Peopl e lnTheOffice érté-
két hozzáadja a PeopleOnvacation-hoz. Végül pedig az emberek számát összeszoroz-
za a másodpercekkel.

A példa megint felvet egy érdekes kérdést. Ezt a kifejezést a számítógép könnyen fel
tudja dolgozni, de emberként nehéz átlátni, megérteni vagy módosítani. Az előző kife-
jezést így is írhauuk volna néhány ideiglenes változór bevezetvc ;
4. óra • és utasftások 65

TotalM inut es : NumMinutesToThink + NumMinutesToTypc;


TotalSeconds : TotalHinutes • 60 ;
TotalPeople : Peoplel nTheOffice + PeopleOnVacation;
TotalPersonSeconds = TotalPeople * TotalSeconds;

Ez a p&lda hosszabb és több ideiglenes változót haszná l, mint az előző , de sokkal


könnyebb megérteni. A 60-at szimbolikus konstansra cserélve &s az egészet egy ma-
gyaráz6 megjegyzéssel ellátva könnyen érth ető és ka rbantartható forráskódot kap unk.

Tudta hogy...? Az igazság természete


AC++ korábbi változatai az igaz és hamis értékeket egészként ábrázolták,
az ISO/ANSI szabvány azonban bevezetett rájuk egy új típust, a bool-t. Ennek két
értéke lehet: IGAZ vagy HAMIS.
Minden kifejezés kiértékelhetö logikailag. Azok, amelyek matematikailag nullát ad-
nak hamisak, minden más viszont igaz.
Régebbi fordítók long int-ként kezelték a bool típus!, melyek így 4 bájtot foglal -
tak. Az. ANSI kompatibilis fordítók a legtöbbször már csupán 1 bájton tárolják
a bool típusú változ6t.

Relációs operátorok
Relációs operátorok segíts6gével két számról eldönthetCS, hogy egye n lőek vagy az
egyik kisebb a másiknál. Minden reláció I-et (igaz) vagy O-át (hamis) ad vissza. Reláci-
ós operátorokról a 4.1. Táblázat nyújt áttekimést.

Ha a myAge értéke 39, a Y0 \.lrAge ped ig 40, akkor:lZ egyen l őség operátorr:l l elc.lönthc-
tő,hogy vajon egycnl6ek-e?
myAge == yourAge ; II ugyanaz a rnyAge és a yourAge értéke?

A kiértékelés végén nul1:'it - vagyis hamisat- kapunk, men a két változó értéke nem
egyenlő.

myAge > yourAge; II d myAge nagyobb a yourAge-nél?

szi ntén nullát - vagy hamisllt - ad .

Figyeleml Hozzárendelés vagy egyenióség?


Számos kezdö C+ + programozó összekeveri a hozzárendelö operátort (=) és
az egyenlöség operátort (= =), amely aztán sok alaUomos hiba forrása lehet.
A gondot az jelenti, hogy a relációs operátor végeredményét hozzárendelhetjlik egy
változóhoz! Teljesen szabályos például, ha a = b > c vagy a o=c b == c jellegű kifeje -
zéseket használunk. A fordító figyelmeztethet. ha például hozzárendelést irtunk,
amikor az egyenlöségnek több értelme lenne. Ilyenkor gondolkodjunk el, mert nem
biztos, hogy tényleg azt írtuk, amit végre szereUűnk volna hajtatni.
56 11. rész • Bevezetés a C+ + programozási nyglvbe

I lat relációs operátor van: egyenlőség (= "'), kisebb «), nagyobb ( », kisebb vagy
egyenlő « =), nagyobb vagy egyenlő (>-=), illetve a nem egyenlő (! =). A 4.1 Tábhízat
ezekről ad áttekintést, használatukat is bemutatva.

4.1 T6b1Ú81- _ _-

Név Operátor Péld a A példa kiértékelése


egyenl6seg 100 - - 50, hamis
50 -- 50, igaz
oem egyenl ő !- t OO!- SO; igaz
5O!- SO; hamis
nagyobb > 100 > 50; igaz
50 > 50; hamis
nagyobb vagy egyenlő >- 100 >- SO; igaz
50 >- 50; ign
kisebb < 100 < 50; hamis
50 < SO; hamis
kisebb vagy egyenlő <- tOO <- SO; hamis
50 <- 50; igaz

Az ff utasftás

Alapesetbe n a programunk sorról ~rra fut , vagyis .lbban a sorrendben hajtja végre
az utasítfIsokat, ahogyan azok a fOmlskódban szerepelnek. Az if utasítás azonban le-
hetCívl: teszi felt.ételek vizsgálatát - példáu l két vá ltozó egyenlő-e - és a kiénékel€!stő\
függ6en ágazik el és fut lovább a program.

Egy egyszerű példa az i f pal".mcsra:


if (kifejC2.ésl
utasitás :

A zár6jclen belül bánnilyen kifejezés lehel, dc leggyakrabban relációs kifejez6s1 ta lá-


lunk. [la a kifejezé.~ értéke O, akkor hamisnak minősü l és a számítógép nem fu ttatja le
az utasításr. Ha nem nulla az értéke, akkor ig:lznak minősül és lefut az utasítás, Nézzilk
a következő példát:
i t (bigNumbc r > smallNumbcr)
bigNumber " smallNumber :

Ez a kódrészlet összehasonlítja a bigNumber-t éS:il sma llNumber-t , Ha a bi gNumber


nagyobb, akkor a mflsodik sor fut le, így a bigNumber énékét a s mallNumber értéké-
vel tesszük egyenlővé.
4. óra • Kifejezések és utasítások 167
------------------------------~~~~~~==~-~----~l·

Az else ág
Feltételvizsgálatkor sokszor használunk az igaz ág mellett egy hamis ágat is.

A korábban bemutatott módszerrel külön-külön vizsgálhatnánk a feltételeket. Noha


működik , mégis kicsit kényelmetlen megoldás. Az else kulcsszó használatával lénye-
gesen olvashatóbb a forrásk6dunk:
i f (kifejezós)
utasí tás;
e lsc
utasítás;

A 4.3 .Lista az else kulcsszó használatát mutatj<1 be.

4.3 usto - Az 01.. ""_ 6 használatának bemutatása (H.....cppl


o: 114.3. Lista - Az else ku lcsszó haszná l ata
1: fflnclude <iost r eam>
2.

,.
3: int main()

S: int first Number • secondNumber ;


6: <ltd : : cout « • Please enter a b ig number , ';
7: s td: : cin » f irstNumber ;
8: std : : cout « • \nP1ease enter a sma11er number : ';
9: s td: : cin » secondNumber ;
10 : if (UrstNumber > secondNumber)
11 : std: : cou t« '\nThanks !\n ';
12 : e l se
13 : std :: collt« '\nOops . The sccond is bigger ! " ;
14 :
15 : rcturn O;
16 :

Pl ease e nt e r a big number : 10


Pl ea se ent er a smal1er n umber. : 12
Oops. The second i s bigger !
M,ís számókkal más eredményt kapunk:
Please ent e r a b ig number : 12
Please enter a smaller number: 10
Thanks!

A 10. sorban található i f utasítás kiértékelése után, ha az igaz, akkor a ll., ha viszont
hamis, akkor a 13. sorban fo lytatódik a program futása. Ha a 12. sorban található else
ss l l. rész • Bevezetés. C+ + programozási nyelvbe

ágat kivesszük, akkor a l,). sor mindig lefut, függetlenü1 a reláció igaz vagy hamis vol-
tától. Ebben az esetben az if uLasítás a 11. sorban véget ér. I [a nincs az el se ág, akkor
a 13. sor fut le következőnek.

A parancsokat kapcsos zár6jel párok közé foglalt kódblokkokkal is helyeucsíthetjük.

Megismerkedünk egy újabb utasMssai


Az std : : cin utasrtás segftségével olvashatunk be C+ + alatt a billentyűzetről
adatokat egy változó ba.

i f (kHejczés)
utasítás;
kövctkez6 utasítás ;

Ha a kifejezés igllZ, akkor ldut az utasítás majd pedig <1Z i f uL{tni utasítás. H a ha-
mis, úgy az cJs6 nem fui le, hanem egyb61 a következő sorra ugrik

Ne feledjük, ;\ pontosvessz6vellczá rt utasítás helyére kapcsos z.'irojelekkel határolt,


összetett ulasítássorozatot is írh.ltnánk.

Összetettebb if konstrukciók

Érdemes megjegyezni , hogy bármilyen kifejezés szerepelhet U!asításkent az if és


az else ~gban , akár egy m~sik .i f vagy else is. A kövelkcz6 példában bemutanmk
egy összctett if szerkezetct:
i f (kiCejczés l )
(
iC (kHcjezés2)
utasításl:
el!lc
(
if (kifejezés3)
utasitás2 ;
elflc
utasitás3 ;

else
utasitás4 ;

Ez a kissé ormótlan kódrészle t a kövelkez6t jelenti: ha a kifejezésI és kifejezés2


is igaz, akkor az utasitásl-ct hajtjuk végre. Ha a kifejez ésl igaz, a kif ej ezés2
hamis és II kifejezés3 igaz, akkor az utasitás2 fuI le. Ha kifejezésI igaz, de
a kifejezés2 és kifejezés3 hamis, akkor az utasitá s 3-at fUlIatjuk. Végiil pedig ha
4. óra • _sok 69

az első kifejezés hamis, akkor az utasitás<l fut le. Amint ez ebből a példából is látha-
tó az összctctt i f utasítások néha elég zavaróak tudnak lenni.

Az összetett döntési szerkezetek használatám mulal példát a 4.4 Lista.

4.4 Liota -lIIIZOIOiIO ogym6sba ágYUOIt H- . . . I-H.cpp)


O, II 4 .4 Lista - Össze t ett, egymásba ágyazot t if utasitások
l : ~i ncl ude<iostream>
2,
3: int mai n ()
4,
5: II Beolvasunk két számot
6: Illetároljuk 6ket a bigNumber és littleNumber változ6kba
7: II Ha a bigNumoor nagyobb, mint él littleNumber ,
8: II megnézzük, hogya kc t t6 mara dék nélkül osztható-e egymással
9: I I ha i gen , megné;o:;o;ük , hogy ugyanaz-e él két szám
10 :
11: int f i rstNumber, sceondNumber ;
12 : atd : : eout « "Enter two numbcrs . \nFirst , ";
13 : std " cin » firstNumber ;
14 : std , : eout « "\nSecond : . ;
15 : std , : cin » sccondNumber ;
16 : std : , eout « " \n\n";
17 :
18 : if (firstNumbcr >_ secondNumber)
19 : {
20 : if ( (firstNumber 11 secondNumbcr) ::= O)
21 ,
22 : if (firstNumber == sccondNumber)
23 : std :: eout« "They are the same!\n" ;
24 : else
25 : i::ILd : : eout « ' They are evenly divisible!\ n ' ;
26 :
27 : else
28 : std :: cout« "They ara not evenly divisible!\n" ;
29 :
30 : else
31 : std :: cout« "Hey! The second one is largcr!\n' ;
32 : return O;

-
33 :

Enter two numbe rs .


First: 10
Second: 2
They arc evenly divisiblc !
70 II. rész · Bevezetés a C++ programozási nyelvbe
Más számokkal más végeredményt kapunk:
Enter two numbers .
First, 2
Second : 2
They arc the same!

Enter two numbers .


First : 3
Second : 2
They arc not evenly divisible !

• S

A két bekér! számOl összehasonlít juk. Az első - 18. sorb;lfl (a!;llh:ltó - j f ut::lsítássa l el·
len()rizzi.ik, hogy az első szám nagyobb·e, mint mási k. Amennyiben nem, ügy::l 30.
sorban található ebe ágm ugrik a programunk,

Ha igt!n , akkor a 19. sortó l folytatódik a programunk. A másod ik i f utasítás a 20. sor·
ban ell enőrz i , hogy az első szám oSZlhal6·e a másodikkal mamd(!k nélkül. I-Ia igen, ak·
kor a 22. sorlxlI1 találl1:l lÓ i f utasítással eldöntjük, hogya két szám egyenI6·e, és :Hl·
n:lk megfel el6cn kiírmjuk a megfelelő üzenetet.

Ha nem teljesü l a 20. sor feltétele, ::Ikkor 27. sorb:m található else ágra ugrik a vezérlés.

Kapcsos zárójelek hasmálata egymásba ágyazott ~ utasftások esetén

Amennyiben csak egy utasítást kívánunk végrehajtlmi, ügy az i f után elhagyhat6


a kapcsos zárójel:
i r (x > y) /1 Ha x nagyobb y-nál
i f (x < z) /1 és x k isebb mint z
)( = y ; /1 akkor x-et értékét hozzárendeljük y-hoz .

Nagy és összetett egymásba ágyazott utasítások esctén ugyana kko r ez II megold:ís je·
lentőse nmegnöveli :l hiba lehetóségét. Ne feledjük, az üres k:uakterek használata
könnyebbséget jelent a programoz6nak, de a fordító figyelmen kíviJ l hagyja, Zár6jele·
zés hiányában könnyen előford ulhat, hogy nem a m egfelelő H·hez írjuk az else ágat,
amely így logikai hibát fog eredményezni. Erre láthatunk pék!;1t a 4.5 Listában.
I
4. óra • Kifejezések és utasítások 71

4.5 Usta - Hoavan teszik ádáthatóvá a kaDCSOS Zlir6ie1ek döntési szerkezeteket


(bracosü.cpp l
0, /1 4 . 5 program - Bemu tat juk, hogyan t es zik átlátha t6vá
1, /1 a kapcsos zárójelek az if múve l eteinket .
2, Hi nclude <i ost ream>
3.
4, i nt main()
5.
6: int x ;
7, std : , cout « "Enter a nunIDer less than 10 or greatet" than 100 , ";
8, s t.d " cin» x;
9, std " cout« " \n ";
10 ,
11 : i f (x> 10)
12 : i f (x > 100)
13 , std : , cout. « "More t.han 100 , Thanks! \n " ;
14 : else / / nem megfelelő helyen lévő else
15 : st.d :: cout. « "Less than 10 , Thanks ! \n" ;
16 :
17 : return O;
18 :

Kimenet
Enter a number less than 10 or greater than 1 00 , 20

-
Less than 10, Thanks!

A program egy lQ-nél kisebb vagy 100-n{11 nagyobb számot vár. Amennyiben megkap-
ja, úgy megköszöni. De a jelek szeri tlt a programunk nem ezt csiná lja.

Ha a 11. sorban igaz a kifejez6sünk, akkor a 12. sorban foly tatjuk a futtatásL Ekkor
a kapott s:lám lO-nél nagyo bb. Te nnés:wtesen a 12. sorban is található egy feltérelvizs-
gálat, ami igaz értéket ad, ha lOO-nál nagyobb a megadott szám. Ebben az esetben
a 13. sortól folytatódik ,l program futása.

Ha a megadott szám kisebb lO-nél vagy egyenlő azzal, akkor az első feltétel hamis, tehát
a 16. sor f"lH Ic. Ha a megadott szám kisebb lO-nél, akkor az alábbi kimenetet kapjuk:
Enter a numb er less than 10 or great e r than 100 , 9

A 14. sorban található else ágat a 11. sor feltételvizsgálatához szerettük volna használ-
ni, valójában azonban a 12. sor t"eltételvizsgálatához tartozik. Ez bizony egy alattomos
logika i hiba a programunkban.
n II. rész· Bevezetés a C+ + programozási nyelvbe

A fordító nem jelez fordítási hib,lt, hiszen hibátlan C++ progmmunk, de mégsem csi-
nálja azt, amit szerettünk volna. Ráadásul mindaddig nem is jelentkezik a hiba, amíg
100-nál nagyobb számot adunk meg.

Összességében tehát sikerült egy igen faramuci hibát előállítanunk. Maga a kód nyelvi
szempontból helyes, ezért fordít ási hibát nem kapunk. Ugyanakkor részben mégis azt
csinálja, amit akartunk, vagyis ha valaki teszteini kezdi, akkor el6ször csak az csetek
kis hányadiiban tapasztalhatja a hibás ffillködésl. Amíg nem adunk meg WO-nál na-
gyobb számot, minden a legnagyobb rendben van .

Apropó Nekünk fontos az átláthat6ság, nem a gépnek


Jegyezzük meg: üres helyeket a saját érdekünkbe érdemes használni, nem a fordító
miatt. Afordító nem foglalkozik vele, hogy szép-e az i f utasítás. Olyan formátumot
kell használnunk, amely könnyen megérthetö, átlátható számunkra. Persze kicsit
bosszantó lehet, ha a programunk nem úgy viselkedik, ahogy azt a behúzások alap-
ján várnánk.

A 4.6 Listában javítotluk a hibát a megfele16 helyeken dheJyezell kapcsos zár6 jelekkel.

4.8liata - H..... h_náliulc _ • Iul..... ,6r6ie101cet f f _


(propolbra.os••pp)
o, /I 4.6 program - bemutatJ uk a kapcsos zárójelek helyes használatát
1: II if utas.itÓssal
2: *.include <ios t ream>
J,
4, int main!)
5,
6: int x;
7: std : : cout « ' En t er u number less tha n 10 or greater than 100 : ' ;
B: std : , ci.n » x;
9: std : : cout « '\n";
10:
11 : Lf(x>lO)
12 : (
13 : i f (x > 100)
14: std :: cout« ' More tha n 100 , Thanks!\n' ;
15:
16 : else II mos t működik
17 : std: : cout « "Less than 10, 'I'hanks ! \n" ;
18 : return O;
19 ,

Enter a number less than 10 or greater than 100 , 20


4. óra • Kifejezések és utesftások 173
----------------------------~~-=~~~==~-~---- -

A 12. és l;. sorban elhelyezett kapcsos zárójelekkel e1éniik, hogya 16. sorban találha-
tó else ág most már a 11. sor if-jéhez kapcsolódik, épp ahogy szerettük volna.

lia a felhaszná ló 20-al gépel be, akkor a 11. sorban igaz lesz a feltétel, viszont a 13. sor
fe ltétele hamis, így nem ÍI.nunk ki semmit. Tökéletesebb lenne a progmffiunk, ha egy
else ágat helyeznénk el a 14. sorban, így tájékoztatva a felhaszná l61 :1 hiba okáróL

Apropó Oefenzfv programozás


A könyv példaprogramjai mindig egy-egy - éppen tárgyalt - példára világrtanak rá.
Egyszerűek, hiszen nem állt szándékunkban a felhasználói hibák teljes k örű kezelé-
se, vagyis ezek a k6dok nem "bombabiztosak", Egy professzionális programnál
azonban minden hibalehet6ségre fel kell készülni és megfelelöen kell azokat kezelni.

A logikai operátorokról b6vebben

Gyakr.U1 nem csak egy reláció igaz vagy hamis mivolt:'íl",J vagyunk kíváncsiak, hanem
többre. Példáu l ig<lz-e, hogy x nagyobb y-nM és y nagyobh z-nél? A programnak el kell
tudni:. dönteni, hogy mindkettő igaz-e vagy hamis, és ennek megfelelc5en cselekedni.

Képzeljünk el egy olyan kifinomult riasztórendszert, amely az :llábbiak szerint mGkö-


dik: Ha az ajtól ki nyitják ÉS elmúli este 6 óra ÉS ez NEM ünnepnapokon VAGY hétvé-
gén történik, akkor hívja a rend6rségcL A C++ három logikai operátol':c' Ichet/5vé teszi
ilyen fcltétclrcndszer kiértékelését is, ezeket <I 4.2-es tábláz.1tban fogla ltuk össze.

4.2 T6bIúat-LotIlkII ~

Operátor Jelölés Példa


ES (AND) && kifejezési && kifejezés2
VAGY (OR) II kifejezés l I I kifejezés2
NEM (NO'I) !kifejezésl

Logikai ~S

A logikai ÉS két kifejezést értékel ki. Ha mindkettő igaz, akkor az ÉS végeredménye is


igaz lesz. Például ha éhesek vagyunk ÉS van pénzünk, AKKOR meg tudunk ebédelni.
if ( (x =~ 5) && (y __ 5) )
74 11. rész· Bevezetés a C++ programozási nyelvbe

Az előző példa akkor lesz igaz, ha x és y is öttel egyenlő, minden más esetben hamis,
Az ÉS kifejezés csak akkor ad igaz értéket, ha mindkét kifejezt:s igaz.

Jegyezzük meg, hogya logikai ÉS kifejezésnél két & jelel kell írnunk!

Logikai VAGY
A logikai VAGY szintén kél kifejezést értékel ki. Ha bármelyik igaz, a kifejezésünk vég-
eredménye is ign lesz. Például ha van pénzünk VAGY hiteJkártyánk, AKKOR ki tudjuk
fizetni a számlát. Nem szüks{:ges egyszerre a pénz és :\ hitelkártya, persze az scm je lent
probJémát, ha mindkclt6vel rendelkezünk.
i f ( (x ==5) II (y"'''' 5) )

Ez pélcb ig:lt~ , h~ ~ k:í r az x, akár az y értéke 5. Gyakorlatilag hi! llZ x éltéke 5, akkor
a fordító sosem vizsgá lja az y-l.

Fontos: a logikai VAGY kifejezésnél kél I jelel írunk!

Logikai NEM

A logikai nem igu3t ad, ha a kifejezés hamis és h:lIniS31, ha a kifejezés ig:lZ.


if ( !(x =,.5) )

Ez igazat ad, ha az x éltéke nem öt. Teljesen úgy viselkedik, mintha ezt írnánk:
i f { x! =5)

Reláci6k precedenciája

A relációs és logikai operátorok kifejezésként viselkednek a C++-ban. l -et (igaz) vagy


O-{tl (hamis) adnak eredményül. Akárcsak a matematikai operároroknál, itt is a prece-
dencia határozza meg, milyen sorrendben értékel6dnek ki. Ezt fontos ismerni péld{tul
az alábbi utasítás helyes megfogalmazásához:
if ( x > 5 && Y > 5 I ( z > 5)

A példában a programoz6 cl6bb az ÉS kapcsolatO{ szerene volna kiértékelni, utána pe-


dig a VAGY kapcsolatot. V:lgy esetleg épp fordítva?

Ha például x értéke 3, y és z értéke 10, akkor az el56 értelmezés szerint IGAZ az állírás
(z nagyobb lO-nél, ezén az figyelmen kívül hagyja a fordíró x és y értékeit). Ezzel
szemben a második esetben HAMIS az ál1ftás. (x nem nagyobb 5-nél, vagyis teljesen
min de~,'y , hogya VAGY feltélel teljesül-e).
4. óra • Kifejezésok és utasftások 175

Habár a precedencia határozza meg a kiértékelésl, zár6jelezéssel megváltoztatható


a sorrend és az utasítás is álláthat6bb lesz:
if ( (x :> 5) && (y :> 5 II z > 5) )

A korábbi éltékeket behelycltesítve HAMIS lesz a feltétel, hiszen x nem nagyobb 5-nél,
így az ÉS nem lehet IGAZ. Az ÉS-nél mindkét oldalnak igaznak kell lennie. Hiába sze-
retünk egy ételt, ha nem jó az íze, nem esszük meg.

Apropó Csoportosflás zár6jelezéssel


Érdemes plusz zár6jelekkel csoportositani a kifejezéseket. Ne feledjük, a cél egy
olyan program létrehozása, amely működik , könnyü olvasni és átlátni.

Bővebben az igazság természetéről

C++-ban a O reprezentálja a hamis értéket, minden más pedig igaz logikai értéknek fe-
lei meg. Számos C++ programozó ki is használja ezt az i t-es szerkezeteiben, Íme erre
egy ~szép" példa:
i f (x) II ha x igaz (nem nulla)
x =: O;

EZl tehát valahogy úgy ken olvasni, hogy "ha x értéke nem nulla, akkor állítsuk nullá!"'",",
Itl persle kicsit csaltunk, és valójába a következől al:lkot kellett volna használnunk:
i f (x ! = O) II ha x nem nulla
x =: O;

Igazából mindkét megoldás helyes, de az utóbbi talán könnyebben átláthatÓ, Érdemes


inkább logikai ki€=nékel€=sre használni a CH ezen tulajdonságát, semmint egy változó
nem nulla én€=k€=t ellenőrizni vele,

Az alábbi két sor jelentése szintén azonos:


if ( !x ) II ha x hamis (nulla)
if (x =:=: O) II ha x nulla

A második utasítást ezzel együll va lamivel könnyebb megérteni és hiszen sokkal ki-
fejezőbb .
7s l l. rész • Bevezetés a C+ + programomsi nyelvbe

Kérdések és válaszok
Kérdés: Mié,-t haszlláljakfe!esleges ziú'Óje!ezést, lia úgyis a precendia határozza meg
az operátorok kié,tékelésél?

Válasz: Noha 3 fordítóprogram természetesen ismeri a precedenciáját és a progmmozó


megtal111lhatja azt, megfele16 zárójelezéssel azért sokkal könnyebben tarthat juk karban
a programjai nkat

Kérdés: Ha a relációs operátorok él1éJ.>e l-et llagy O-át ad, a t6bbi éltéJ.'Cl lIlié/t tekillf-
ji/k igaznak?

Vú/asz; Relációs operátorok l-et vagy O-ál adnak vissza, de minden kifejezés ad vissz:!
értéket, így akár az if ut:lsításban is ki én(! kelhelő. íme erre egy példa:
if ( (x .. fl ... b) =" 35 )

Ez egy szabályos CH utasítás_ A bels6 zárójelben szere pl ő hozz::'irende1és még akkor is


lefut, ha maga a feltéle1viugálat hamis eredményt ad.

Kérdés: II tabuláto/; a szóköz és az 1íjsor milyen. hatással V(lIl a p rogral11unkra?

Válasz: A tabulátor, a szóköz és az újsor - ezeket nevezzük üres karaktereknek - nem


befolyásolják a program múködésél, csupán olvashatóbbá teszik a forrásk6dllnkat.

Kérdés; A negatív számok igaz vagy h(,llllis logikaI é/1éket jelentel/ek?

Válasz: Minden nem nulla szám - legyen az pOZitív vagy negatív - igaz értékként
viselkedik.

Gyakorlatok
MOST, hogy már rudunk egyet s mást a kifejezésekr6l és az utasításokr61, válaszoljuk
meg a kvízkérdéseket és a !"'Yakorlatok segítségével mélyítsük el a megszerzett tudást.

Kvfl
l. Mi a különbség az XH és a HX között?
2. Melyik más nyelvekben gyakori operátor hiányzik a CH -ból?
3. Mi a különbség a 001- és jobbérték között?
4. Mit csinál a modlllo operátor?
4. óra • Kifejezések é, uta'fiá,ok! 17

Feladatok

1. Írjunk egy programot, amely a három tanult módon (a "'3+1, 3+ =1 ét; aH) növeli
egy változó értékét. Fordítsuk [e, linkeljük és futtassuk. Más leLt a méretük? Ész-
leltünk a futási sebességben különbséget?
2. Gépel jünk be egy matematikai kifejezést az integrált fejlesztői környezetben.
Pozicionáljuk a kurzort a valamelyik matematikai operátorra és nyomjuk meg
az Fl billentyűt. Mennyire hasznos a kapott információ?
3. Írjunk egy programot, amiben egyszerre növelji.ik prefix és pOSlftx módon egy
változó értékét egy lépésben (++x++). Lefordul? Van értelme? Mi történik, ha
egyszerre használjuk a növelést és a cs6kkentést?

Válaszok a kvfzkérdésekr.
1. Az első ut6tagkénl való (posrfix) növelés. Ekkor e l őbb kiolvassuk <l változó é rté-
ké t és utána növeljOk. A második elótagk6nt Llirtén6 (prefix) növelés. Itt elóbb
növeljlik az é rtéket és a már növelt értékel olvassuk kL
2. A hatványozás openítorn hiányzik. Van rá függvény, amely elvégzi, cle operátor
nincs .
3. A bal6rték az egye nl őségjel baloldalán, míg a jobbérték m: egyenlőség jobb ol-
dalán jelenik meg. Minde n ba lérték lehel jobbéné k is, de a legtöbb jobbérték
nem szerepelhet a baloldalon.
4. Az egésLOsztás maradékál adja vissza.
5. ÓRA

Függvények I
I,\ir6llesz szó ebben az órában:

• Mi a függvény és milyen részekból áll


• Hogya n deklarálhatunk és definiálhatunk függvényeket
• Hogyan adhatunk át paramétereket egy függvénynek
• Hogya n adhatunk vissza értéket egy függvényb6!

Mi is az a függvény?
llggvény ahtpvct6cn egy alprogrdlll, amely bizonyos Iln1veleteket tud végezni adato-
'o és e nnek eredményeként egy értéket tud visszaaclni. Minden C++ programban van
gaJább egy függvlmy, nevezetesen a main () . Ez az a pont ugylmis, ahol a program
~g:rehajtása elkezcl6dik. A main () természetesen maga is hívhat más függvényeket,
az általa meghívott fiiggvények is hívhatnak újabbakat, és így tovább.

'flden függvénynek egyedi neve van a programon belül. Ha a végrehajtás elérkezik


:: ilyen névhez, akkor a múve1el'iOfOn egy elágazás keletkezik: a végrehajtás a függ-
_~n leín e l ső művelette l folytatódik, majd innen egészen addig tart , amíg el nem
III I. rész· Bevezetés a C++ programozási nyelvbe

ér egy return pa rancshoz, vagy a függvény k(x1jának végéhez. Amikor a függvé ny


visszatér a hívóhoz, a végrehajtás a függvé nyhívást követő els6 parancson folytatódik.
Ezt a végrehajtási sémát az 5.1. ábrd szemlélteti. Egy jól megtervezett függvény egyetlen,
jól körülhatárolható feladatot hajt végre. Ez röviden aZl jelenti, hogya jó függvény
egyetlen dolgot tud, de azt nagyon jól, aztán ha ezzel végzett, akkor visszatér a hívóhoz.

Az összetett feladatokat előbb fel kell bontanunk egy-egy függvt:ny formájában megva-
lósílható alfelacbtokra, aZI,ín ezeket az elemi függvényeket keH cgyenkér1t a megfelelő
sorrendben meghívni. Ez a tervezési módszer a kódot könnyebben érthetővé és "igy
könnyebben k:lrbantanh,lIóvá teszi.

Ebhcn a könyvben nagyon gyakran fogom emlegetni a könnyű karbantanhatóságoL


Ennck az az oka, hogy - amint azt korábban is említeltem - rmmapság a progr::lmfej-
lesztésben az igazán nagy köIL<;{:get nem is ;mnyira az alkalmazás megírása, hanem an-
nak a karbantartása, folyamatos frissítés€: jelenti.

Main O
{ UtlsfIM; tunel
tunel ():
Utasités:
func2 O;
IJI.IsItH;
lune4 ():
} Utasitás:

5.1. ábra
AmiJ.'Or a program e,gy j/lggocnyhílXjs/lOZ érkezik, a végl"l!Ilajtás a jl1gg/Jélly kódjállak e{só
sorára ugrik, ml/jd a lli.sszatéTris utáll a függ/Jéll)'lIíuás Iltálli első so"al folytatodik.

Itt szeretném föl hívni a figyelmet arra az eg{:sz egyszen1 megállapításra is, mely szerint
a kódolással kapcsolatos problémák igen nagy hán}'"".Jda egyszerű stiláris probléma
(ilyen például a sorkezdetek megfelelő ménékű beljebb wirisa). Maga a fordító ugyan
nagyon hajlékony, de könnyen olvasni a szépen szerkt::sztett k6dot lehet.
5. óra • Függvények I SI

A függvények deklarálása és definiálása


:ld6u meghívharnánk egy függvényt, előbb deklaráln i majd definiálni kell azt.

függvénydekhuáci6ban megadjuk a fordítóprogramnak a kérdéses függvény nevét,


t:>SZatérési értékének, valamint bemenő paraméte reinek típusát. A függvény deklaráci-
~ szokás prototípusnak (prototype) is 1úvni, mivel ez csak egy formai leírás, ami
fUggvény törzsét ne m tartalmazza.

defuúció ezzel szemben azt í rja le, fiÚként kell il függvénynek működni e, milyen mú-
-eteteket kell végrehajtania . Ahhoz, hogy egy függvényt bármely más függvényb61
.eghívhassunk, legalább deklarálnunk kell azt va lahol az első hívás el őtt, ám m~. gá l
-.-égrehajtandó kódot csak a dcfinídó tartalmazZa.

Fiiggvények deklarálása

, rordít6progmmmal maga a gyártó is szállít számos e l őre megírt f'Üggvényt. Ezeknek


• o.1eklariiciÓi természetesen szintén készen vannak azokban a bizonyos fcjlécállomá-
T.'Okban, amelyeket az ti nc1ude paranccsal mdunk beemelni a saját k6dunkba.

Egy füru,>vény prototípus:! egy olyan pontosvessz6vel végz6d6 utasítás, amely t:lrtal-
1ll3zza a függvény nevét, valamint visszatérési érté ké ne k típusá t és bemenő par.:unéte-
reinek listáját. A paraméte rlista - elnevezésének megfel elően - ,I bemenő par<lmé terek
neveit és típusát tart'llmazza. Ez egyes elemekel iu vessző választja el egymástól. Egy
ruggvé ny prolotípusának szerkezetét szemIéIleli az 5.2. ábra.

paramétertrpus
/ "Y/éter neve

uoslgoed short iot FlndArea (lot length. int width) ;


~ '--VJ ~I
VIsszatérési érték típusa név paraméterek pontosvessz6

•.2. ábra
q:yfriggvimydeklanici6 részei

.\ függvény definíciójának és deklaráci6jának pontosan meg kell egyeznie a név, és


2 felsoroll paramétertlpusok te kintetében . Ha nem ez a helyzet, akkor fordításkor hibát
.bpunk. Érdemes ugyanakkor megjegyezni , hogy egy függvény prototípusának nem
82 11. rész· Bevezetés a C+ + programozási nyelvbe

föltérlen kell tanaimaznia a bemenő paraméterek neveit, csak a típusok megadása kö-
telező. Ennek meg felel ően a következő függvénydeklaráció teljesen helye~:

long FindArea{int, int) ;

Ez a protOTipus egy FindAread ( ) nevű függvényt deklarál, amely long t!pusú é rtékkel
tér vissza, bemenetként pedig két egész paraméter v;ír. Bár a függvény ilyetén megadá~
sa szintaktikailag he lyes, ne m valami jó öuet, hiszen ha megadjuk a paraméte rek neveit
is, az adott esetben sokkal világo~abbá teheti , hogy pontosan miről is van szó. Egy
szintén helyes de átgondoltabb de klaráció tehát a következ6 képpen nézhe t ki:
long FindArea(int lcnqth . int widthI ;

Így sokkal egyértelmabb, hogy mi is ez a függvény, és milyen paramétereket kell ne ki


megadnunk milyen sorrendben. Figyeljük meg, hogya függvé nyeknek a legtöbb eset-
ben van v is~zatl:rési énékü k is. Az 5.1 Listában bemutatunk e gy olyan programo t,
amely hivatkozik a Fi ndArea () függvény pro to típusára.

I
o : /1 Az 5 .1. Lista a függvények prototipusának használatát szemlélteti
1 : linclude <iostream>
2,
3 : inL FindArea (int length, int width) ; II prototipus deklarálása
4,
s : int main()
6,
7: int lengthOf\'ard ;
8: int wiclthOfYélrd ;
9: int areaOf'lard ;
10 :
11 : std : :cout « "\nHow wide is your yllrd? ".
12 : std : :cin » widthOfYard;
13 : std : :cout« '\nHow long is your yard? ";
14 : std : :cin » lengthOfYard ;
15 :
16: arcaOfYard: Find~rea(lengthOfYard,widthOfYard) ;
17 :
18 : std : :cout « '\nYour yard is ' ;
19 : std : : cout « areaOfYard :
20 : std :: cou t « " square feet\n\n" ;
21 : return O;
22 :
23 :
24: int FindArúa(int l, int w) 1/ function definition
25:
26 : roturn l .. WI
27 :
5. óra • Fúggvény.k 183

How wide is your yard? 100


How long is your yard? 200
Your yard is 20000 ~quere f eet

A FindArea () függvény prototípusát a 3. sor tartalmazza. Összehasonlítva ezt a 24.


sorban kezd6dő dcfinfci6valláthatjuk, hOh'Y mind a név, mind II paraméterek típusa
megegyezik a két helyen.

Ha különbözők lennének, fordítási hiba kelelkezne. Ami azt illeti, formai szempontból
nincs sok különbség a deklaráci6 és a definíció között. Az el6bbi nek pontosvessz6vcl
kell végződnie, és nem tartalmazhat ja a függvénytörzsct, cic amúgy mindenben egyezik
az utóbbival

FigyeljLik meg, hogy II prototípusban a length és a width neveket adtuk a bemen6


paramétereknek, a definíció azonban már az l és w nevekkel dolgozik. Ebből világo-
san látszik, hogy a fordító semmire sem használja a deklal"ációban megadott neveket.
Ezek csupán a programol6 számára jelentenek h<lsznos információt. Fontos ugyanak-
kor a sorrend. A függvény bívás<lkor az argumennlmokat ugyanolya sorrendben kell
megadunk, ahogya deklaráció és a definíció a par:.111létereket tartalmazza.

Azt is megtehetjük, hogy a függvény definícióját az els6 hívfi.~B el6tti progrmnrészben


helyezzük el. Ez eselÜnkben aZl jelenti, hogya 24-27 sorokb:m látható definíciót el-
mozgathatjuk valahova a 16. sor elé, ahol az els6 híV:IS történik. Ebben az esetben fö·
löslegessé válik a deklaráció. Bár ez a módszer kis méretű programok esetében kiváló-
an használható, azért követendőnek semmiképpen nem nevezhet6. Nagyohb progra-
moknál ugyanis komoly problémákba ütközhetünk, ha II végrehajtás és a lefordíthal6-
ság a függvények definíciójának sorrendjétől kezd függni. Összetett kódok esetében
ilyenkor kibogozhatatlan függőségek alakulhamak ki és soha nem lehetünk benne biz-
tosak, hogy minden a megfelelő helyen és a megfelelő sorrendben van. Sokkal haszno·
s.1 bb, ha mindent jó előre deklarálunk, hiszen a definiciók sorrendjétól val6 függést így
automatikusan kiküszöböljük.

Függvények definiálása

Egy függvény definíciója két részre osztható: tartalmaz egy fejlécet és egy törzseL A fej·
léc rulajdonképpen nem egyéb, mint a deklaráció elismétlése. Annyi különbség persze
azért van, hogy itt már kötelező megnevezni a paramétereket, illetve nincs a sor végén
lezáró pontosvessz6.
84 11.rász· Bevezetés a C++ programozási nyelvbe

A fü ggvény törzse vagy teste a végrehajtandó utasítások sorozata, amit kapcsos ~dróje­
lek között adunk meg, /\ :I fejléc és a függv (;nyrörzs szerkczetét szemlélteti az 5.3. ábra .

visszatérési érték típusa paraméterek


~ ~
unslgned short InI (inIlengIh, inI wId1h)

nyitó kapcsos zárójel

I/Utasftások
relum (length. wIdIh);

\kUICSSlÓ~
visszatérési érték
záró kapcsos zárójel

5.3. ábra
/igy fl188V1'}II), fcjlt."ociilwk és ItJrzséllck szeM'Zcl

FOggvények megadása: összegzés ...

Egy függvény prototípuSlI ami való, hogy által mt:gmondjuk II fordítóprogramnak , mi


a függvény neve, milyen típusú értéket ad vissza, és milyen líplISÜ bemenő adatoklH
vár milyen sorrendben. Ez fo rmaitag a köverkez6képpen fest:
visszat6réai_tipus faggvény_neve( [tipus [paraméter_nevelI ... ) :

A függvC:ny definki6ja ezzel szemhen azt tudatja a fordítóprograrnrnal, miként kell


az adOlt függvénynek működnie. A formai leírás a következőképpen fest:

visszatérés~típus ftlggvény_neve( [tipus [paraméter_nevcll ...


(
utasítások;

Bár a függvény prototípusa a visszatérési énéket, a nevet és a paraméterek listáját adja


meg, nem kell minden függvénynek paraméterekkel rendelkeznie, illetve ha vannak is
neki paraméterei, a deklarációban akkor sem kötelező felso rolni ezek neveit, csak a lí-
pusukat. A prototípus mt:gadása mindig pontosvessz6vel végz&iik.

A függvény ddiníciójának meg kell egyeznie a deklarációval a visszatérési l:nék és


a bemenő pamméterek típusában . Meg kell neveznie valamennyi para mélen, a függ-
vény törzsét pedig kapcsos zár6jelek közé kell zárni. A függvény törzsében szereplő
5. 85

valameflllyi utasítást pontosvessző követ, de magát a függvényt nem zárja ilyen jeL
A definíció végét a záró kapcsos zárójel jelenti.

Ha II függvény ad vissza valamilyen értéket, akkor a törzsében az utols6 végrehajtott


utasítás egy r e turn lesz. Ez az utasítás amúgy a törzsben bárhol felbukkanhat, vagyis
nem kell a sz6 .'i7.Oros értelmében az ulolsónak lennie. Minden függvénynek van
visszatérési típusa. Ha a függvény egyáltalán nem ad vissza értéket a hív6nak, akkor
visszatérési típusa v oid.

Nézzünk néhány példát függvények prolotipusának megadására:


long FindA rea (lo ng l ength, l ong width ) . II l ong értéke t ad vinn za és
II két bcmcn6 paramétare van
void PrintMessage (int messageNumber) ; / I nem ad Vi::Hi z a 6rtéket , egy
II bemen6 paramétere van
int GetC hoice ( ) ; II agész érték e t ad vi!;5za , ni n cs b e mc n 6 p u ramétere
BadFun ction(} ; II egés z értéke t ad vi ssza , n incs bemen 6 p a ramétere

:-;ézzünk né hány példát függvé nyek meghall'íro zásám is:


long Az:-ea(long l , long w}
(
return l • w:

void Pr intMessage(int whichMsg)


{
i f (whichMsg :: o)
atd : : cout « " Hello . \n ";
i f (whichMsg -- li
std : : cout « "Goodbye . \ n ";
i f (whichMsg > II
atd : : cout « "I ' m confuaed . \n " ;

Hogy hány és milyen típust] utasítás lehet egy függvény törzsében, arra gyakorlatilag
nincs semmiféle korlát.

Ugyanakkor a jól megtervezett függvényck álmlában rövidek. II legtöbb tulajdonkép-


pen nem hosszabb néhány sornál.

Változók hasmálata függvényeken belül


A változók függvényen belüli kezelésének több különböző módja is van. Amint már
többször említeuük, vannak a bemenő paraméterek, amelyeket a hívó ad át
a fü ggvénynek. Ezek mellett magának a függvénynek is lehet saját változókészlete,
amelynek a tagjai a függvényre nézve lokálisak, és megszlÍnnck létezni a törzsön kívül.
Ugyanakkor a függvények használhatnak globális, vagyis a program egészéból e1érhe-
tó változókat is.
86 11. rész • Bevezetés a C+ + programozási nyelvbe

Helyi vá~oz6k

Egy függvénynek nem csak kívül ről adhatunk át paramétereket, hanem a törzsén belül
is deklarálhaul11k újabb v;íltoz6kal. Ezek a lokális vagy helyi válLOzók, amelyek - ne·
viiknek megfelelően - csak a függvényen belül léteznek. H<1 a függvény visszatért a hí·
vóhoz, ezeknek a változóknak a tartalma elvész, azokhoz a továbbiakban semmilyen
módon nem lehet hozzáférni.

A lokális változókat pontosan úgy kell deklar:1lni, mint <lZ össze töbhil. A függvényn(!k
paraméterként át<1dott értékeket tároló változók szintén lokiílisak és ugyanúgy használ·
hatjuk 6ket, mintha deklarációjukat a függvény törzsében adtuk volna meg. A paramé-
terek és a helyi változók használatára mutat egy egyszenl példát az 5.2 Lista. A kimenet
a progmm három e~,.ymás utáni futtatásának eredményét mutatja úgy , hogy mindhá-

5.2 usta - ...,..."".... ás helyi


o : linclude <iostream>
ri_
romszor más és más bemen6 pammétcreket adtunk mcg ne ki.

""""'
=;..Iata
;..;...:;.
lloca
;..;..Ivo
_ ,_.cpp
= , _ _ _ _ _.....

"
2 : float Convert (float.) ;
3,
4 : inL main()
5: {
6, float 'I'empFer ;
7, flo a t TempCel ;
8,
9, std : : cou t « " Plea~e e n ter the ternp en~ tu re i n Fahre nhe i t : ";
10 : std : : ci n » TempFer :
IL TempCcl = Convert(TempFer) ;
12 : std : : cout « "'nHere's the temperature in Celsius : . ;
13 : std : : cout « 'I'empCel « std : : endl;
14 : return O;
15 :
16 :
11 : float Convert(rloat TempFer)
18 : {
19 : float 'I'empCel :
20 : 'I'empCel = «('I'cmpFer 32) * 5)/9 ;
21 : retur n TernpCe l;
22 :

Pl ease e nt er th, t emperature in Fahrenheit: 212


Here's the temp erdt.ure in Celsius : 100
Please enter <he t e mperature in Fahrenheit: 32
Her e 's the tempe ratu re in Celsius : O
Pl e<lse e nt er the t emper ature i n Fahrenheit: 85
Here ' s th e t crnpera ture i n Celsius: 29 . 4444
5.6ra • függvények I S7

..... kimenet a program három egymás utáni futtalásának eredményét mutatja. Ez egyes
fu natások során rendre a 121, 32 és a 85 értékeket adtuk meg neki bemenetként. A 6.
6 -. sorban egy·egy float IÍpusú változ6t deklar:álunk, melyek közül az egyik a Fah·
~eitben kifejezett h6mérséklelet tárolja, mig a másikban ennek a Celsius-skálán ki·
fc:,ezen értéke ta lálható. A felhasználótól a 9. sorban bekérünk egy f ahrenhcitbcn kire-
>ezen h6mérsékletet, majd ezt az értéket átadjuk a Convert () függvénynek.

.-' Yégrehajlib ekkor a Convert () első sorára, vagyis a 19. sorra ugrik, ahol ckkhm'í-
IUnk egy TempCel nevi! helyi v{lltoz61. Bár egy ugyanilyen n cvű vfl ltm:6 már a 7. sor-
'.ln is szerepelt, a kettő nem azonos, A most deklarált TempCel ugyanis :\ Convert ()
rugg\'ényre nézve 1ok{llis, v'lgyis csak eze n bclLillétezik. A param6lerk6nt lÍTndott
-.;.~pFer vá ltozó szinl(:n lokális, v;\gyis csupán m5solnt a main () függvényben hithaló
ruggvényhívásnál áwdotl (:rtéknek.

A függvény par.:lméterl:t ennek rnegfelel6en hívharruk volna akár FerTemp-nek is, :1 he-
nl . .iltoz6t pedig Cel Temp-nek, ugyanolyan tökéletesen ml1ködne. Aki nem hiszi, akár
-nódosíthatja is a kódot, és rncgnézheLi, hogy lefordítás után ugyanúgy ml1ködik-e .

.\ helyi TempCel változ6b;1 egy olyan énék kerül, ami úgy keletkezik, hogya pnr.lmé-
~dként átadott 'remFer i!nékl:b61Ievonunk 32-t, az eredményt megszorozzuk 5-tel,
"'I'U;d elosztjuk 9-cel. Ezt az énl:ket aztán vissz<l,\djuk a lúvó félnek , :unely a l l. sorban
-.:kerül a main () függvény változ6készlell:hez tartozó TempCel változóba. Végezetül
.u. il!rtéket kií~uk a képerny6re a 13. sorb:m .

." orogramol háromszor futtat juk le. Az első érték, amit átadunk neki a 212, ami nem
.....~. mint a víz forráspontj;\ Fahrenheitben kifejezve, így könnyen elle nőrizhetjük,
.- SY megkapju k-e a Celsi us skálán érvényes értéket (100). A második futtatásnál a víz
::";yáspontjáva l pr6bálkozunk, míg II harmadik egy találomra választott élték, amely át-
-.;.L{"'3 bizonyára nem lesz egész szám. Gy .. korlásképpen pr6báljuk meg lefordítani
~ 5.3 Ustában bemutatott programot, és az itt bemutatotthol. hasonlóan leszteini a mti-
'dését.

S.3 Lista - Holvlrilm6k 60 DORIIIIétorok haunélata 6ov. ho.. a malnll60 a Convertll


fGggrinyak rilloz6lt .... ugyanúgy hlvják (locaIvor2.cpp)
' include <ioSLream>

_ . float Convert (float) ;

~ : int main ()
(
float TempFer;
sa lI. rész · Bevezetés a C++ programozási nyelvbe
7: fl oat TempCel ;
8,
9: s td : : cout « • Pleas e ente r the t emperatur e in Fahrenheit : .;
10 : std : : cin » Temp Fe r ;
ll : Te mpCel = Convert( TempFe r) ;
12 : std : : c o ut « • \ n Here ' s t h e t empcra Lure i n eel sJ us : . ;
1] : s t d : : cou L « TempCe l « std :: end l ;
14: ret u rn O;
15 :
16 :
17 : float Convert (float Fer)
IS : {
19 : float Cel ;
20 : Cel" ({Fer - 32) • 5) / 9 ;
21 : r eturn Cel ;
22 :

l Ia minden iga z, ugyannt az ered ményt kell kapn unk , mint az iménL

Minden változónak van érvé nycsségi köre, ami meghatározza , hogy mikor és a prog-
ram mely része in hozzáf(:rhet6. Azok a változók, amelyekct egy programblokkon belí.il
ad unk meg, arra a blokkra nézve lokálisak. Csak ezen a k6drészleten belül hozzMérhc-
l6k, ha pedig a v~grehajtá s kikerült innen, akkor egyszen.1en megsz\1nnek létezni.
A globális változó k ezzel szembe n a program bármely ponljáról houáférhet6ek.

Apropó Ahol csak akarodl


Új változ6t bármely programblokk bármely pontján dsklarálhatunk. Ha úgy akarjuk,
akkor akár egy i f -es szerkezet kapcsos zár6jelei között is. Ugyanakkor ne felejtsük
el, hogy ezek a változók csak az adott blokkon belül léteznek.

Globális vá~ozók
Azok rt változók, amelyeket minden függvényen kivü l dekla rállunk globálisak lesznek,
v<lgyis bármely függvénybó l e l érhetőek , belcérlve ebbe természetesen <I main () függ-
vényt is.

A C++-ban ugyanakkor célszerű elkelülni a globális v{lltozók használatát, Illivel ezek-


kel meglehet6sen kusza k6dot h07.harunk létre, amit aztán igen nehéz lesz karbantarta-
ni. Ennek megfelel6en ebben a könyvben gyakorlatilag e~,'yetle n globális változó scm
fog előfordulni , l=s persze ugyanez igaz azokra II progmmokra is, amit a könyv szer.lói
nem oktatási céllal írnak.
5. óra • FQggvény.k 89

A függvények argumentumai
Egy függvény argumentuma inak természetesen nem kell azonos típusúnak lenniük,
Ha éppen arra van szükségünk egy probléma megoldásához, akkor teljesen ésszerű
az a függvény is, amelyik mondjuk egy egészet, két long típusú paramétert, meg egy
:-\;J.raktert vár !:>emenetként.

Fúggvényhívflskor argumcnlumké nt bármely a C++ nyelvben értelmes kifejezést meg-


.dharunk. Ebbe bcll:tartoznak :t konstansok, a matematikai és logikai kifejezések, illel-
\-e egy bármely mis függvény ;Utal visszaadott érték is. Tegyük fel például, hogy van
eg}' li1ggvényünk, melynek a deklarációja a következőképpen fest:
nt MyFunctioll(int thclntegerParam. bool theBoolean) ;

Ezt számos különbö7.6 módon hívhat juk meg, amely le het6sC:gckb61 i l l csupán hármat
mutatunk be:
_nt :;0: , X ,. 3 , y " 5:
II deklaráljuk az argumentun,ként ha9znál t
/I változ6kat
: " MyFunction(x,y); II egy int és egy bool változ6t adunk át
MyFunction (32, true) ; II két álland6t adunk át
;: " MyFunction(23.9, tOO>5); II a:;o: els6 érték 32, a másik igaz
\z utolsó hívás kirnenelelél lekinlve leljes<::n megegyezik a másodikka l. Végezetül n ~z­
T".Jk 3 kövctkez6 két függvényt:
_:lt MylntFunction(int x, int y):
001 MyBoolFunction(int x, int y):

:\ ~ Function- l ezek után akár a kövctkez6képpen is meghívhatjuk:


: " MyFunction(MyTntFunction13 , 5) , MyBoolFunction(2,4»:

I:t rulajdonképpen az történik, hogya MylntFunction(J,S) hívás által vissz:1:1doll


~ész . és a Myl3oolFunction (2 .4 ) hívás á ltal szolgáltatott logikai értékel adjuk át p3-
!""l.méterként a MyF'unct ion függvénynek.

függvényhfvás mint paraméter

B.:ír nyelvileg teljesen helyes, ha egy függvény paramétereként cgy másik függvény ál-
:3.l \·iSS7..aadott értéket használunk fel, a7.ért a 7. igazat megvallva ezz<::! a m6clszerrel elég
,rusza kódot lehet alkotni. Az ilyen progf"".J.mat aztán ig<::n nehéz nyomköveIni és kar-
:Unrartani. Példaként tegyilk fel, hogy a doubleJ: (), tripleJ: (), squaJ:e () és
J. :::ube () függvénye k valamennyien egy-egy értéket adnak vissza. Ebben az esetben
=\-elvileg teljesen helyes a következő függvényhívás:
.'_""C.5wer " (doubleJ:(tripler(square(cube(myValuc ) ) ) :
90 II. rész· Bevezetés a C++ programozási nyelvbe

Hanem hogy itt mi is történik pontosan, azon az ember kénytelen kissé elgondolkodni.
Vesszük ugyebár a myValue változó él1ékét és ezt paraméterkém átadjuk a eube ()
függvénynek. Ennek :l visszatérési értékét megkapja argu mentumként a square () ne-
vú függvény , amely szimén kisz.'lmol valamit. Ezt a vala mit kapja bemenetkém
a tripler () függvény, amelynek visszatérési értékél végül a doubler () dolgozza fel.
Az. Answer tehát a myValue köbe négyzetének megháromszorozott majd megkét5zere-
zett értékér kapja ért(:kül.

Mindezt nem csak leírni nehéz, hanem átgondolni is. EIs6 látásra például sokak nem
lesz triviális, hogyelabb szorozl\mk hárommal és csak aztán emeltünk négyzetre, vagy
éppen fordítva. Ha pedig hibás a végeredmény, ember legyen a ta lpán, aki megmond-
ja, hogy a négy függvény közül V'djon melyikben kell keresni a hibát.

Sokkal átl{lthatóbb, ha létrehozunk néhány átmeneti váltOZÓI, és a műveleteket lépésről


lépésre kljLjuk végre, valahogy így:
unsigned long myValue = 2;
unsigned long cubed = cube(myValue); II cubed • 8
unsigncd long squared = square(cubed) ; II squared ol 64
unsigned lon g tripled .. tripler (squured) ; II tdpled = 192
unsion~d long Answer = doublerltriplcd) ; II Answer : 384
Ezzel a módszerrel lehet6ségü "k van ci , hogy szükség esetén b5rmely köztes ered-
ményt mcgvizsgftljLlnk, és azo" sem kell gondolkodni, hogy milyen sorrendben hajtód-
tak végre <lZ egyes műveletek.

A paraméterek egyben helyi vá~oz6k is


A függvl:nynek átadOft argumentumok a továbbiakban lo kálisak a filggvény re nézve.
Ez azt jelemi, hogy :l rajtuk végzen változtatásoknak nincs semmiféle hatása a fliggvé-
nyen kívüli kódra. Ezt a szabályt a programozók érték szerinti átadásnak (passing by
value) nevezik, ami tehát szemlélctcsen azt jelenti, hogy a függvé ny saját argumencu-
mainak csak a másolatával dolgo:lik, nem az ,eredeti példánnya l". A helyi másolatokat
aztán a függvény k6djában már ugyanúgy kezeJhetjük, nlinl bármely más helyi válto·
zót. Az 5.4 List.-'lban bemutatoll k6d ezt a sz:tbályt hivatott bemutatni.

5.4 UIIII- Az _ _ 6Iodá -1,"'IIy".,."",


o: II 5 .4. Lista ~ Az érték szerinti átadás szeml éltetése
l : #include <iostream>
2.
3 : void swap(int x, int y);
4.
5: int main ()
6: {
7. int x = 5 , Y 10;
5. óra • Függvény.k 191

B. std : : cout « "Main . Before swap, x : « x


9. « y: <<: y « '\n' ;
~O : swap(x,y) ;
ll: std : : cout « 'Main. After swap, x: • « x
:2 : « • Y' • « y « '\n" ,
:3 : return O;
:4 :
:5:
:6 , void swap (int x, int y)
:"7:
:8 : int temp:
:9 , std : : cout « ·Swap . Before swap, x : « x
20 : « • y: « y « ' \n ';
21 : temp = x,
22 : x = y;
23 : y = temp ;
24 : std : : cout « ·Swap . After 9wap , x : " « x
25 : « • y: " « y « • \n' :

-
26 :

!{ain . Before swap. x: 5 y : 10


S;;ap . Before swap. x: 5 y : 10
:wap . After s wap. x : 10 y : 5
".ain . After s wap . x : 5 y : 10

Ez a program a main() filggvényen belüllérrehoz kél válloz6t, majd argumentumként


mdja azokat:! s wap () függvénynek. Ez - nevének megfe l e l ően - felcseréli a két érté-
..e{. Ami kor 3zo nban a main () függvt:nyhez visszatérve megvi7..sgáljuk a helyzetet, azt

tapasztaljuk, hogy semmi sem változott,

\-áltozók a 7. sorban kapnak értéket. Ezeket a 8. sorban meg is jelenítjük a képer-


.J,.
r:;-6n, aztán a 9. sorban meghívjuk II s wap () függvényt, amely megkapja ők et beme-
netkém .

•J,.program végrehajtása tehát a s wap () k6djával fo lytatódik. Itt a 19. és a 20 , sorban is-
~ kiíratjuk a kél értékct, amelyek cb'Yelőre ugyanabban a sorrendben vannak, mint
~etileg a ma in () függvényben, A 21-23 sorokban megtörténik a csere, amit a 24, és
:--. sorokban egy újabb kiíratással ellen6rzünk. Látható, hogy amíg a swap () kódját)
bdül . tartózkodunk", addig a két érték tényleg helyet cserél.

fu_!lem a füru"",ény végrehajtása után a program a 11. sorban folytatódik, ami már is-
r:.er. a main () része, és a két 6rték megint a régi . Semmi nem változott.
92 11. rész· Bevezetés 8 C++ programozási nyelvbe

Mindebból világosan látszik, hogya swap () függvénynek átadott két változó átadása
csak érték szerint törtém meg, vagyis a függvény két másolatot kapott, amelyek
a swap () ~m nézve lokálisak. A 2] -23 sorokban igazából ennek a két helyi változónak
az értékét l'seréltük meg, amelynek így a külvilágra semmiféle hatása nincs. A main ( )
továbbra is az eredeti áll apotol látja.

Egy későbbi fejezetben be fogunk mutatni egy o lyan megoldást is, amivel a ma i n ()
számára is fclcserélbet6 a két érték egy mégpedig egy függvényhívás segítségével.

Értékek visszaadása függvénvból


Egy függvény v.~gy egyetlen értéket ad vissza, vagy a visszatérési típUS<1 void. A vo i d
kulcsszó igazából csak egy jelzés a fordít6prograJtUlak, amely arra utasit ja, hogya kér-
déses függvényb61 nem kívánunk semmiféle értéket visszaadni.

lia egy függvényen belül értéket szeretnénk visszaadni , nincs más dolgunk, mint
a return kulcsszó után leírni a visszaadni kivánt változó nevét, egy kifejezést, vagy
cgy konkrét értéket. fme néhány példa:
return 5 ;
return ( x > 5) ;
retur n (MyFunction cl) ;

rdtéve hogy a MyFunct ion ( ) olyan függvény, amely maga is ad vissza értéket, mind~
három femi művelet helyes. A második esetben (x>s) a hfv6nak vissza,ldorr érték ha-
mis lesz, ha x értéke nem nagyobb mint S, eHenkező esetben pedig természetesen
igaz. Ügyeljünk rá, hogy ilyen esetekben a kifejezés értéke adódik vissza, nem pedig
az x változó tartalma.

Amikor a prognlm elérkezik egy r eturn utasításhoz, akkor a hívó fél visszakapja
az utána megadott kifejezés l:rtékét, a végrehajtás pedig a függvényhívás utáni soron
folytatódik. A r e t urn után szerepl6 műveletek ilyenkor egyáltalán nem futnak Ic, V;l-
gyis a r et urn ga ....,lntáltan a függvényből va ló kilépést jelenti. Ez egyben:lZt is jelenti,
hogy egy függvény kódja több r eturn utasítást is tartalmazhal különböz6 helyeken.
Csak azt nem szabad elfelejteni, hogy amint a program ;lZ els6 return utasítást eléri,
a rüggvénynek vége. A több return utasítás haszná lalát szemlélteti az 5.5 Listában be-
mutatou kód.

5.5 lista - T6bb lIlIUm ....Itú egyeUen fOggvHyben !manyqm..... ppl


o: II 5. 5 . Li s t a - Töb b r eturn utas i t ás egye t l en füqgvényben
1: Il s tatcments
2 : tinc 1ude <iostream>
3,
4 : i nt Double r (int Arno untToDouble ) ;
5. óra • Függvények 193

5:
6 : int main()
7: (
3: int r esult = O;
9: int input ;
:0 :
:1 : std : : cout « 'Enter a number betwee n O and '
:'2 : « " 10 , 000 to doub l e : ' 0

..
:3:
--' .
std : : cin » input ;

:'5 : std : : cout « ' \nBcfo r c doub l e r is c a lled . . ' ;


:6 , std : : cout « ' \ninput : • « inp u t
:,7 , « • doubled : • « resul t « ' \0 ";
:'8 ,
:'9 , result .. Doub ler(inpu t ) ;
20 :
21 : std : : cout« " \nBac k f rom Do ubl e r ... · ;
22 : std : : cout « " \n i np u t : • « input
23 : « doub l ed : ' « res ult « ' \0\0" ;
2~ :
25 : ret urn O;
26 :
27 :
2B : int Doubler (int original)
.;9 : {
-O : i f (original <= 10000)
~l : rcturn original ' 2 ;
32 : else
"] , return - 1.
~4 ; std :: cout « "'(ou can't get here ! \o' ;
- 5:

Enter a number bctwccn O and 10 , 000 to double : 9000

Before doubler is called .. .


• oput : 9000 doubl ed : O

Eack trom doubl er .. .


: nput : 90 00 doubled : 180 00

:::nter a number bc twccn O a nd 10 . 000 t o dou bl e : 1100 0

3efore doubler is ca l led . ,


:nput : 11000 doubled : O

3ack from doubler ...


: nput : 11000 doubled : -l
94 11. rész • Bevezetés a C++ programozási nyelvbe

A 11-13 sorokban bekérünk a felhasználó ró l egy számot, majd a 16. és 17. sorokban ki-
írjuk azt az eredmény tárolásárd használt helyi váltoroval együn. A Doubler () függ-
vényt a 19. sorban hívjuk meg, bemenetkém pedig a fel haszná lótól bekért értéket ad-
juk át neki. Az eredmény a már említe tt result neV1.1 helyi változ6ba kerül, majd az ér-
tékeket a 21-23 sorokban ismét kiíratjuk.

A 30. sorban - amely a Doubler () függvény k6djához tartozik - megvizsgál juk, hogy
a paraméterként átado tt érték nem nagyobb-e mint 10000. Ha nem, akkor a függvény
a szám kétszereséveltér vissza. I-Ia azonban a megadott szám tízezernél nagyobb, ak-
kor a visszatérési érték -1 lesz, amit jelen esetben hibak6dnak is felfoghalunk.

A 34 . sorba n l{uhat6 kódra a végrehajtás soha nem ke rü lhe t, mivel akár nagyobb ;j pa-
raméterkém átadott ért6k mint 10000, akár kisebb, a függvény a 3"1. vagy a 33 . sorban
mindenképpen visszatér a hívó fé lhez, vagyis a 34. sorig gar.mtá lt.an soha nem jut el.
Ez annyira így V' In , hogy egyes fordítóprogral1lok az ilyen helyzetre még Ogyelmeztet6
üzenetet (warning) is küldenek.

Apropó Fordft6i ilzenetek a manyretums.cpp fordftása közben


A manyreturns. cpp program fordftása során egyes a következő hibaüzenettel
találkozhatunk:
"manyreturns.cpp"; W8066 Unrcachable code in function
Ooubler(int) at line 34
"manyreturns. cpp ": w8070 Function should retum a value in function
Doubler(int) at linc 35
Az első üzenet arra figyelmeztet bennünket, hogy az s td : : cout művelet soha
nem hajtódik végre, mivel a Doubler () függvény mindenképpen visszatér a hfvó-
hoz. mielőtt elérné azt. A második üzenet szerint a fordítóprogramunk kicsit kényel-
metlenül érzi magát a kialakult zúrzavar mian, mivel az utolsó return utasítás
után de még a záró kapcsos zárójel előtt talált végrehajtható kódot, és ez határozot-
tan nyugtalanítja. Ezek csupán figyelemfelkeltő üzenetek (warning), amelyek felbuk·
kanása azt jelenti, hogya fordítás rendben lefutott, a gépi kódú program elöálIt, el-
lenben futtatáskor nem biztos, hogy azt fogjuk kapni, amit szerettünk volna. Ez ese·
tünkben természetesen nem probléma, mi ugyanis éppen azt kapjuk, amit szeret-
tünk volna: azt akartuk bemutatni, hogy Hügyes szelVezéssel~ eJhelyezhetünk egy
programban soha végre nem hajton részleteket.
5. 6ra • Függvények 195

Függvények alapértelmezett paraméterei


Ha egy függvény prolorípusában és definíciójában szerepel egy paraméter, akkor a hí-
,·6 fél köteles ennek a hívás soctin értéket adni. Ráadásul az átadott értékek olyan típu-
súnak kell lennie, mint ami a deklaráci6ban szerepd. Ha tehát van cgy a következő
módon deklarált függvényünk
long myFunction{int) ;

akkor annak meghíváskor tnindenképpcn át kell adnunk egy egész értéket, legyen
az egy váho7..6 vagy egy áUandÓ. Ha fl függvény definíciója eltér a deklarációtól, vagy
a hívás során nem a megfelelő értéket adjuk át, akkor fo rdítiÍsi hiba keletkezik. Ez al6l
egyet.len kivétel van , mégpedig ha a függvény prOlotípusában (dekla rki6jában) meg-
adunk egy p:mun(:ternek alapértelmezett értéket. Ez az érték akkor lép érvénybe, ha
:1 hívó nem rendelkezik a kérdéses paraméter éltékéról. Ennek ismereté ben az imént

bemut.nott cleklarációt a következ6képpen al:tkíthatjuk ál:


long myFunction (int x " 50) ;

Ez a dekl:lrádó azt mondj,l, hogy .1myPunction () neVI.Í függvé ny long értéket ad


vissza, és egy egész t:néket vár pamméterkénL Ha pedig a hívó ez ut6bbit nem adja
meg, akkor II p:lmméler alapértelmezett értéke legyen 50, Mivel :1 deklarációban nem
kötelező nevet adni a par<uuétereknek, a fenti sort akár a kövctkez6képpen is írhauuk
\'olna:
long myFunction (int = 50) ;

Atl61, hogy :t dekbrád6ban alapéltelmezett értéket adunk meg egy paraméterhez,


a függvény definíciója még semmiben nem változik. Esetünkben tehát a rnyFunction
konkrét megadása a következ6képpen kezd6dhet:
long myFunction ( int x)

Ha a hívó fél nem :tdja meg az x pa r-.J méler értékér, akkor a fordít6progr.un az alapér-
telmezett 50-es értéket rendeli hoz7.á. Ez továbbra sem jelenti azt, hogy II definicióban
és a deklaráci6ban a par.Jméternek azonos néven kell szerepelnie, az alllpénelmezctt
énék hozzá rendelése ugyanis pozíció é.~ nem név alapján történik.

.-\lapértelmezett értéket megadhatunk egy függvény egyes paramétereihez, vagy akár


3Z összeshez is. Ez egyetlen korlálO?ás ezzel kapcsolatban amit be kell tartanunk az,
hogy ha .1 paniméterlistában eb'}' elemnek nincs alapértelmezett értéke, akkor nem le-
het előtte olyan paraméter, aminek van,

Tegyük fe l, hogy egy függvény deklarációja a következőképpen fesl:


long myFunction ( int Paraml. int Param2, int Param3);


96 11. rész· Bevezetés a C++ programozási nyelvbe

Ebben az esetben a Param2 pamméLernek csak akkor lehel alapénelmezeu énéke, ha


Param3 -nak is adunk. Hasonlóan Par amI-hez csak úgy adhatunk meg ilyen értéket, ha
ugyanczt meglcsszűk a másik kettővel is. Az alapértelmezett pammélerértékek haszná-
latát az 5.6 listában mutatjuk be.

na.. I
o: II 5 . 6. Lista A~ alapértelme~ett paraméterértékek
1 : II használatának bemutatása
2 : .include <iostream>
J,
4: int AreaCube(int length, int width ~ 25 , int hcight • 1 ) ;
5,
6 : int main()
'7 :
8: int length ol100 ;
9: i nt width .. 50 :
10 : int height = 2 ;
11 : int area :
12 :
13 : area" AreaCube(length, width, height ) ;
14 : std :: cout« "First time area equals ' «arca« '\n ":
15 :
16 : area • AreaCube(length , width) ;
17 : std : : cout « 'Second time area equals' « area « '\n';
18 :
19 : arca" AreaCube(length):
20 : std : : cout « 'Third time area equals' « area « '\n';
21 : return O;
22 :
23 :
24 : int AreaCube(int length, int width. int height)
25 :
26 : reLurn (length ' width' height) :
27 :

First area equals : 10000


Second t ime e rea equals : 5000
Third time area equa ls : 250 0

A 4. sorban lálható az AreaCube () függvény prototípusa, mely szerint ez a függvény


három egész típusú paramétert vár a hívótól. Ezek közül a két utolsónak a lapértelme-
zett értéke is van.

Maga a függvény egyébként egy téglatest felszínét számolja ki , a paraméterek pedig


ennek az élhosszúságait tartalmazzák. Ha nem adunk meg szélességet (width), akkor
5, óra • Függvények 197

az automatikusan 25 Jesz, ha pedig a magasság (height) hiányzik, akkor a progr.lm


az 1 értékkel számol, Megtehetjük persze azt is, hogy csak a magasságot nem adjuk
meg. Ilyenkor természetesen csak erre fog érvényesülni az l-es alapértelmezen érték,
a 2S..öl a függvény nem használja. Ennek a fordítonja ugyanakkor nem lehelSégcs, va-
gyis nem adhatunk meg magasságot anélkül, hogy meg ne adnánk a szélességet is.

A 8-10 sorokban beáilítjuk:t magasságot, szélességet és hos,.o.;zúságot tároló változók


h.;idt.h, height, l ength) értékét , majd ezeket paraméterként átadva a 13. sorban meg-
hívjuk az AreaCube () függvényt. Ez kiszámít ja a felületet, amit a 14. sorban kiiratunk
a képerny6re.

A végrehajtás t!zlltán a 16. sorban folytatódik, ahol ism(:t meghívjuk 37. AreCube ( )
fü ggvényt, de ezúttal nem adunk meg In:lgasságot. A függvény ekkor az alapértelme-
zett értéket fogja hasznMni, ami akiíratott eredményben világosan lútszik.

A \9. sorban ismételt függvényhívás következik, de ekkor már sem II szélességet sem
a magasságot nem adjuk meg. A végrehajtás tehát immár harmadszor a 26. so rm ugrik,
3hol két alapértelmezett érték felhasználásával a függvény ismét kiszámítja a felszínt.
Ezt aztán megint kiírltjuk a képernyőre .

Függvénvek túlterhelése
:\ CH nye lv lehet6vé teszi , hogy ugyanolyan névvel akár több függvényt is létrehoz-
zunk. Ezt a:.! dj{jrást nevezik a függvény túlterheléséne k (overloading). Az azonos név-
vel rende lkező függv ényekne k azért a pamméterlistájukban különoozniük kell , vagyis
legalább egy paraméterüknek más lípusúnak kell lennie, vagy eltérő számú \xlrlméter-
rel kell rendelkezniük. Esetleg mindkettő. Lássunk rögtön egy példát:
::'nt myFunction (int , int) ;
_:lt myFuncti on (long , long) ;
:nt myFunction (long) ;

:\ :nyFunct i on () függvényt itt három különböző paraméterl ista megadúsával terheltük


rul. Az első és a második változat a para méterek típusában tér el egymástól, míg a har-
mad iknak eltérő SZ;lmú p:1ramétere van.

Hogy a C++ fordítónak melyik változatot kell használnia az egyes hívások :1lkalmával,
azt ezek az dtcrések, vagyis a hív,ís konkrét módja dönti cl.


Túlterhelt fuggvényeknél a visszatérési érték típusa lehet azonos de akár eltérő is, amíg
.i paraméterek listája különbözik. Ugyanakkor arra is lehetőség van, hogy a túlterhelés

...b.pja a visszatérési érték típusa legyell. A fuggvények túlterhelését swkás többalakú-


ságnak (polimorfizmus, polymophism) is nevezni. Az elnevezés magáért beszél, így ra-
;in nem igényel további magyarázatot.
98 11. rész· B8V8Z8tés a C+ + programozási nyelvbe

A függvények többalakúsága a nyelvnek al. a képessége, amely lehetővé teszi, hogy


egy adott nevet túllerhelve a függvénynek több jelentést lulajdonítsunk. Ha ugyanazzal
a névvel, de eltérő paraméterlistával hozunk létre két vagy több függvényt , maga a for-
dító akkori is képes lesz automatikusan eldönteni, hogy melyik helyzetben melyikről
van szó a kódban, hiszen a paraméterek száma és típusa alapján azonosítani tudja
a megfelelő változatot. Ha például az a feladat , hogy egy függvénnyel számítsuk ki sz;i-
mok átlagát, akkor fl túlterhelés segítségével nem kell külön nevet adnunk minden egy
adattípust kezelő változatnak, vagyis nem kel1lélrehoznunk külön Average I nts (),
AverageDoubles () és ehhez hasonló nevíI függvényeket. Hívhatjuk valamennyi egy-
szeruen Average () -nek, a fordító pedig mindig tudni fogja , hogy melyik az igazi.

Tegyük fe l, hogy van egy függvényünk , ami bármilyen bemenetet is kap, megslOrozza
azt kettővel. Csupán a numerikus típusoknál mamdva ennek a függvénynek nyilván
képesnek kell lennie az int , long, floa t és double típusok kezelésére. Ha nem len-
ne a C++-ban Többalakúság, eZl csak négy különbözi) ncv(j" Cüggvénnyel tudnánk meg-
oldani, valahogy így:
int DoubleI nt (int) ;
long DoubleLong(long) ;
float DoubleFloat(tloat) ;
double DoubleDouble(double) ;

Ugyanez a túlterhelés segíL'iégével így fest:


int Doubl e( i nt) ;
l ong Double (long) ;
fl oat Double (fl oat ) ;
double Doubl e (double) ;

Az ilyen kódot nyilv:'i.n sokkal egyszerűbb utólag mcgl:rteni v,lgy használni, hiszen
nem kell azzal foglalkoznunk , hogy mikor melyik függvénytípusl hívjuk meg, a fordító
ezt automatikusan elintézi helyettünk.

Inlino függvényok
Amikor megadunk egy függvényl, a fordítóprogram általában csak e~,'Yszer hozza létre
a memóriában az ennek megfelelő gépi kódot. Ha meghívjuk a függvényt, a program
végrehajtásában elágazás keletkezik, a processzor:t megfelelő memóriaterülefre ugrik,
és végrehajtja a függvény kódját. Amikor a függv{my visszatér, a végrehajtás is vissza-
ugrik a hívás helyére, és az azt követő utasítással folytatódik. Ha tehát egy függvény
tízszer meghívunk, az tíz ilyen ugrást fog jelenteni a végrehajtásban. A függvénynek
egyeden példánya van, de azt tíz.<;zer hajtjuk végre.

Az efféle ugrálásnak persze van némi számításigénye, vagyis általa némiképp csökken
progmmunk teljesítménye. Ugyanakkor vannak egészen kis függvények is, :tmelyek
5. óra • Függvények 199

akár egyetlen soroyi kóclból állnak. Nyilvánvalóan a tel jesítmény növekedését vonja
maga után, ha egy vagy kél utasítás végrehajtásáért nem kell ezt a bizonyos ugrási mű­
veletet elvégezni. Amikor a programozók teljesítménYfÓl beszélnek, általában a sebes-
ségre gondolnak, vagyis a függvényhívások kikeriilésével programunk futási sebessége
fog növekedni.

Ha egy függvény deklarációjában szerepeltet jük az inline kulcssZÓl, akkor a fordító-


program valójában nem hoz létre függvényt, hanem a megadott kódul bemásolja min-
den o lyan helyre, ahol eZI a látszólagos függvényt meghívjuk. Ez egyben azt is jelenti,
hogy a végrehajtásban semmiféle ugrás nem lesz, a progmm úgy viselkedik, mintha
a fOggvé nyben szerepl6 utasításokat a hívás helyére gépeltük volm. be.

Mindez igazár1 szépen hangzik, ám nem árt fölhívni a figyelmet az inline függvények
haszná latá nak leheL~~ges árnyoldalaira sem, El őször is ha egy inline függvényt tízszer
meghívunk egy másik függvényb61, akkor a k6dja tízszer bemásolódik a nnak a k6djá-
ba. Ez pedig azt jelenti, hogy bár a sebesség bizonyára növekedni fog, a kód mérete is
ugyanez teszi, mégpedig az inline függvény méretétő l függ6en esetleg olyan mérték-
ben, amiért már nem is éri meg ezt a megoldást, használni. Ráadásul előfordu lh at,
hogya sebessl:gnövekedés is csak szép álomnak bizonyul.

A manapság használatos fordítóprogr-.tmok igen okosak, rengeteg optimalizálást elvé-


geznek maguktól is, ami szinte minde n esetbe n szükségtelenné teszi az inline függ-
vények alkalmazását. Ami viszont még ennél is fontosabb, az a k6drnéret és a teljesít-
mény összefüggése: II nagyobb kód gyakran lassabban fut, mert nehezebben kezeli
az operációs rendszer.

Aprop6 Helycsere a #define direktfvával


Az inline függvények tulajdonképpen ugyanaz a szerepet töltik be, mint a C
nyelvben a IIdefine segrtségévellétrehozott makrók.

Ezek után joggal kérdezheti az olvasó, hogy - különösen kezd6ként - mégis milyen
amnyszabályokhoz tartsa magát. Ha van egy kifejezeuen kis fuggvényiink , amely
mindössze egy vagy két utasításból áll, az . potenciális jelöl[~ arra, hogy inline-kénc
deklaráljuk. Ha azonban bármilyen kétségünk merül fel ennek a hasznosság:lVal kap-
csolatban, egyszeníen ne éljünk a lehet6séggcl. Az inli ne függvl'!nyek haszná latára
mut:H példát az 5,7 Lista.

o: II 5 .7. Lista - Inline függvény használatának bemutatása


1: finclude <iostream>
2,
] : inline int Doubler(int);
4,
100 I. rész • Bevezetés a C+ +

5 : int main ()
6: (
7: int target ;
8,
9: std : : c ou t « 'Enter a number to work wi th : ' ;
10 : std: : cin » target ;
11 , std :: cout« '\n ';
12 :
13 , target " Doubler(target ) ;
14 : otd : : cou t « "Targ e t : • « t arget « std , , end l;
15 :
16 : target " Doublp.r(target) ;
17 : std :: cout« 'Target : • « target « s t d :: endl ;
lB :
19 : target " Doublcr(target) ;
20 : std : : cout « 'Target : • « ta eget « std : : cndl ;
21 : rcturn O;
22 :
23 :
24 : int DoubIer(int targe t )
25 : (
26 : return 2 *target ;
27 :

Enter a n u mber to work with : 20


Targel,. : 40
Target : BO
'I'arget : 160

A 3. sorban" Dou bler () függvénylolyan inline-kém deklaráljuk, amely egyetlen


eg(!sz értékel vár bemeneIként és szintén egész é rtékkel tér vissza. A deklaráci6 am(lgy
pontosan ú ~,.y néz ki , mim bármely másik, eltekintve persze a visszatérési típus előtt ál-
16 inline kulcsszótól.

Enől kezdve mindazokon a helyeken, ahol a


t arget = Doubler ( ta rgct} ;

kódsort leí~uk olyan gépi kód keletkezik, mintha a következő állna a forráskódban:
target " 2 * ta rget ;

Ez azt jelenti, hogy amikor a programol elindítjuk, már minden a helyén van, vagyis
nincs szükség ugrásokra ahhoz, hogya függvény lefusson. Az eredmény gyorsabb, de
nagyobb mérelú program.
5. óra • Függvények 1101

Apropó Csak agy tanács ...


A modern fordítóprogramok az inline kulcsszót valójában csak laza iránymuta-
tásként és nem parancsként kezelik. vagyis ha a fordító egyéb szempontok alapján
úgy dönt, akár figyelmen kivül is hagyhatja ezt az óhajunkat, és közönséges függ-
vényhívást hajthat végre helyette.

A függvények és a verem

Amikor meghíYl.Jnk egy függvényt, a progr.Hn végrehajtásában elágazás keletkezik.


A hívó fél átadj:l a paramétereket, majd a függvény törLSt!vel folytatódik a futCIs. Ami-
kor a függvény véget ér, egy értéket ad vissza a hív6nak (hacsak nem void li visszat(:-
rési típusa), a végrehajtás pedig visszatér a hívás utáni első utasításm. Na dc hogyan is
mű ködik ez az egész pontosan?

A fordítóprogram v:116j::íban minden egyes programhoz létrehoz egy úgynevezett ver-


met (stack) , Ez nem más, mint egy speciális célr'.1lefoglalt mem6riaterület, amelyen ke-
resztül a függvények képesek egymással kommunikálni. Veremnek azért nevezik, mert
érvényes rá <lZ "utolsónak be els6nek ki ~ (last-in-first-our) elv, vagyis körülbelül úgy
működik , mint ebédl6ben az egymásra halmozott tányérok (lásd az 5.4. :íbrfü).

5.4. ábra
A verem nem egy6b, mim rá/"JI(!~)'ek ByITjtc/luJI/)"c.

Az ~uto1s6nak be els6nek ki" elv röviden annyit tesz, hogy amit uLoljára tettünk a ve-
rem tetejére, azt kapjuk vissza el6ször a kiolvasás során. Vannak persze másféle kiszo l-
gálási sarok is. A legtöbb például úgy működik, mint a színházi sarok, vagyis aki dsó-
nek jön, az jUl be először, az hagyja el elsőnek a sort. A verem ezzel szemben olyan,
mint az egymásra ídkotl pénzérmék. Ha egymásra rakunk 10 darab érmét az asztalon,
majd párat leemelünk a rakásr61 , akkor az első három leemelt érme az utolsó három
fel helyezen pénzdarab lesz.
102 11. rész· Beveie1é. a C++ programozísi nyelvbe

Amikor adatot ~tolllnk" (push) a verem tetejére, akkor a rakás növekszik. Amikor lehú-
zunk (pop) onnan valamit, a verem csökken. Ha nem akarunk katasztrófát, akkor nem tu-
dunk úgy leemelni a veremról egy elemel, hogy le ne emelnénk az összes fölÖ(te lev6L is.

A programozással fog lalkozó könyvekben á ltalában a k1nyérhalmoLszokás a verem al-


leg6riájakénl em líteni, amivel nincs is különösebben nagy baj azon kívül , hogy alapve-
l6en hibás. Valójában sokkal helyesebb amolyan függ61egescn el rendezett Oóksornak
e lképzelni a vermet, amelynek a teleje ott van, ahova a veremmutat6 (stack pointer)
é ppen mutat.

Minden egyes t1óknak va n egy sorszáma, a vcrcmrnutató pedig mindig ezek közül
:1 sorszámok közül tárol egyel. Minden, ami ez alatt a fiók alan - va~,'yis a verem teteje
alatt - helyezkedik el, a veremhez tartozik, és éppen tá rol vala mit. Minden , ami fölötte
van, üres, nem tartozik a veremhez, a tartalma pedig érvénytelen. Ezt az elképzelést
szem lé lteti:az 5.5. ábm.

Verem
Veremmutató
100
A vermen kr- 11021
theVarlable 60 101
} vüli tartomány
l 02 <:0
My"," 50 l 03

l 04

105

YourAge 37 l 06
A verem tartalma
107
l 08
l 09
110

5.5. ábra
A veremm lllllfú

Alnikor adatot tolunk a verem tetejére, az a veremmutat6 fölötti első fiókba kenil, a vc-
remmutató pedig eggyel (vagy a szükséges hosszal) feljebb mozdul. Ha adatot veszünk
Ic a veremről , akkor ennek é ppen a fo rdítonja játszódik le, vagyis a veremmmató lej-
jebb tolódik. Ezt az egyszeru játékszabályt szemlélteti az 5.6. ábra .
5.6ra • Fuggvónyek 1103

Verem
100
Veremmutató
Ihe Varlab le eo 10 1 ~
102
MyA,. 50 103
Avermen kr-
10' vűli tartomány
105
YOI.IrAge 37 106
107
108Q

:~} Averem tartalma

5.6. ábra
A l'Crem ll/ultl/6 mozga/ása

Amikor progmmunk meghív egy függvényt, létrejön egy ~vcremkcrel~ (513Ck frame),
amely egy olyan, a veremb61lefoglalt megfelelő méret\! terület, amely elegendő a kér-
déses függvény par.:lmétereinek kezelésére. Ez egy viszonylag összetett folyamat , rá-
adásul a részletei különbözhetnek is a kü l önböző fe!építésCf gépeken, de a lényege
a következő:

1. A rendszer elhelyezi a verem tetején a függvény visszatérési dmét. Ez az a me-


m6riacírn, ahova a végrehajtásnak vissza kell ug'J.nia, amikor a függvény befe-
jezle mtIködését.
2. A rendszer lefoglal a vennen egy akkor'.! helyet, amely e l egendő a vissza(~r~si
érték tárolására.
3. A hív6 fél elhelyezi a verem tetején a függvény valamennyi paraméterét.
4. A végrehajtás a függvény k6djára ugrik.
5. A fiiggvény helyi változói deklarálásuk sorrendjében létrejönnek a vermen.

Visszméréskor ugyanennek a művd etsornak a fordítonja zajlik le.

Kérdések és válaszok
Kérdés: Miért nem használlInk kizárolag globális változókat?

Válasz: Volt idő, amikor a programokban valóban kizárólag glohális válLozókat hasz-
nálLak . Aztán ahogy növekedett a szoftverek összetettsége, egyre nehezebb volt megta-
lálni bennük a hibákat. Ennek pedig alapvet6en az volt az oka, hogy a globális adato-
104 11, rész· Bevezetés a C++ programozási nyetvbe

kat elvileg bármely függvényelronthaua, hiszen a program bármely pontján módosít-


h,ltó volt bármi. A hosszú évek gyakori ali tapasztalata azt.'Ín meggy6zte a fejleszt6ket,
hogy az adatokat ami lyen mértékig csak lehet, lokálissá kell tenni, vagyis a lehető leg-
kisebbre le keU szűkíten i azoknak a blokkoknak a számát, amelyek egy-egy változó-
hoz hozzáférhetnek.

Ké,.dé.~: Miél1 /Icm befolyásolja a liívó fCl adatait az, ha egy fiiggvél/Y módosítja a neki
árudoll fXo'améterel..'cr,?

Viilasz:'A filggvények paraméterei érték szerin{ adódnak át. El lIl( jelenti, hogya függ-
vény valójában csak az eredeti értékek másol:Hával dolgozik.

Kérdés: Mi t(JI1éllik akJ.'OI; ha dcklarálom a /.>6vet!':ező kél függvény:


int Area (int width , int length = l);
inL Arca (int size) ;

Tckilllhető ez Wlterhelésl/ck vagy sem? Eltérő ug)'an a pamméte1'ck száma, dc az első


esetbell az egyiknek vall alapél1clmczett él1éke is.

Válasz: A helyzet érdekes, ugyanis maga a deklaráció rendben van, de ha az Areél


függvényt bfirhol egyetlen par:ltnéterrel meghívjuk, fordítási hiba keletkezik, mivel
a rendelkezésre álló lIdatQk alapjfin nem dönthető el, hogy melyik formá"l gondoltunk
(:unbiguity between AreaCint, int) and Area(int».

Gyakorlatok
Most, hogy már tudunk mcgtanultunk néhány dolgot a fi.lggvényekr61 , álljon in néhá ny
megvfil:lszolandó k6rdés és megoldandó feladat , amelyekkel megszilárdíthatjuk a ed-
dig megszerzelt tudásunkat.

Kvfz
1. Mi az abpértelmeLetl paraméter-átadCI.<;i m6d függvényhivás esetén?
2. Honnan tudja II C++ fordító , hogy melyik változatát hívja meg egy túlterhelt
fOggvé nynek?
3. Mitől olyan hasznosak a függvények? Miért nem írunk helyettük egyetlen mono-
litikus programot?
4. Hány értéket lehet viSS7.aadni egy return paranccsal?
5. óra • Függvénvek 1105

Feladatok

1, Pr6báljuk meg kikommenlezni az 5.5, Listában bemutatott kód


(manyreturns.cpp) utolsó std : :cout hívását. Mi változik a fordítá:> során? Mi
történne, ha bete nnénk egy return pamncsot az std: : cout urán?
2. Hogyan kellene kiegészíteni az 5.2. listában bemutatott programot
(local var . cpp) úgy, hogy Celsius skálár61 is tudjon Fahrenheitre ál$zárnítani?
Pr6báljuk megva lúsltani az ötletet!
.,. Egészítsük ki úgy az 5.2. Listáb:m b(!mutalau programot (local var . cpp), hogy
egész és lebegoponlos értékeket is álludjon váltani Fahrenhcitrol Celsius skálá-
ra. Használjuk a függvények túlterhelésének lehctoségét!

Válaszok a kvfzkérdésekre

l. Az alapértelmez.en m6dsi!cr az énék szeri nti átadás ( p ;lS5 hy va lue). Ez azt jck:n-
ti, hogy.! függvény csupán a paraméterek másolatát knpja meg, vagyis az általa
rajtuk végzett módosít:1soknak a külvilágra senunifélc hatása nem lesz,
2. A fordít6program a filggvény meghívása kor átadott p:U"<unéterek listája a b pján
dönt a leheL'iéges változatok közl. A kapott listát összehasonlítja
a lchetoségekkel, és a megfelelot használja.
3. II rüggvények egyrészt le het6vé teszik :1 má r megírt kódok öjrahusznosílásál ÚI
saj{!lunkét és a m:1sok Mtal ínél egyar:"i nú, másrészt segítségilkkel kisebb ré.':izek-
re bonthat juk a nagyobb problémák megoldását. Ez utóbbi azért hasznos, mt!ct
jelcnt6sen könnyíti a hiba keresést és a k:lrbantartásl.
4. A return segítségével csak egyetlen értéket adhatunk vissza. Ez tcrmészetesen
lehel egy bonyolult számítás végeredménye is, de a hívóhoz csak és kizárolag
egy é rték juthat vissza ezzel a módszerrel. Arm is lehet6ségünk van, hogy
a konkrét szitlláci6tól függ6e n különböző értékeket adjunk vissza kül önböző
reLurn lItasításokkal, de a függvénynek ekkor is csak egyellen visszatérési 6né-
ke lehcl.
6. ÓRA

Vezérlési szerkezetek
Ebben az 6rában a következőkról lesz sz6:
• Milyen ciklusok vannak és hogy,ln érdemes őket használni
• Hogyan szervezzünk különféle ciklusokat
• Egy :t1Lernatíva a mélyen cgybeágyazolt i f ... else utasítások helyett

Ciklusszervezés
Múlt órá n :l vezérJóutasítások első változatáról vOll szó, vagyis az if ... else utasítá-
sokról. Sok programozási fe ladat úgy oldható meg, hogy ugyanazokkal az adatokkal
tevékenykeclünk újr.! meg újm. EZl az ismételt tevékenységet iterációnnk nevezzük.
Az iteráció megval6sitásának legalapvetőbb módszere a ciklusszervezés.

A ciklusok 6se: a goto utasftás

A swmításrudomány hőskorában a ciklus egy címkéből, néhány ulasításból és egy ug-


rásb61állt.

A C++ nyelvben (mint ahogy a C-ben is) a címke egy név, melyet: (kenósponc) köveL
El lehet helyezni címkét bármely utasítás elé (batoldaIÚ, és ugráskor erre a címkére le-
het hivatkozni a goto utasítás után.
1Da l l. rész • Bevezetés a C++ programozási nyelvbe

Miért ne használjuk a goto utasítást?


A C++ programokban riLkán látunk goto-t, és ez nem véletlen. A goto utasítással el lc-
het ugrani a forrásk(xl bármely részére, el5re vagy hátm. A g oto utasítás válogatás nél-
küli alkalmazása szörnyű gubancos, olvashatatlan programokat eredményezett: ezt hív-
juk spagetti k6dnak.

A go t o használatának elkerülésére kifinomult, szigorúan szabályozott cikluss7.ervcz6


utasítások születtek: f o r, while , do ... wh ile. Ezek használata sokkal érthetőbbé tC-
szi a prognuum, és általáb,lIl fe leslegessé teszi a goto használatát. A globális változó k
használatát is hason ló okokból kerüljük: nehéz 5kel S7iimon tartani és nyomkövctni.

A while ciklus
A 6.1. Listában bemutatott while ciklus hatásánl a program bizonyos utasítások soroza-
tát ismétli mindaddig, amíg a kezdeti fe ltétel ign marad.

o: 1/ 6 . 1 Lista
1 : II A wh i le ci kl us
2 : lI includc <ios t ream>
] ,
4 : int main()
5: (
6, int counter = O: /1 feltétel ben l év6 változó inicializációja
7,
8: whi l e(coun t er < 5) /1 ellcn6r zés : i ga z-e még a fe l tétel
9: {
10 : counte r ·H· ; II A ci klu s magja
l l: std :: cout« "S7.áml ál6 : " « counter« " \n ":
12 :
13 :
14 : std :: cout« "Kész . A számláló értéke , • « counter « " . \n" ;
15 : rctu r n O;
16 :

Számláló , l
Szám1á16 , 2
Számláló : 3
Szám1ál6 : 4
Számlá ló : 5
Kész. A számláló é rté ke : 5
6.6ra • Vezértési szerkezetek 1109

Ez az egyszeru program bemutatja a while ciklus tényegét: ha fe nnátt egy feltétel , vég-
reh'ljtódik a dklusmag. Esetünkb<:!n a program a 8. sorban található feltételt vizsgálja
meg: kisebb-e a számláló 5-nél. Ha igen, akkor végrehajtódik a ciklusrnag. A 10. sor-
ban növekszik egyet a számláló, melynek értékét a program a ll. sorban ki is írja.
Ha nem teljesül a 8. sor feltétele (azaz a s7.1Ímtál6 már nem kisebb 5-néD, a program
nem hajtja végre a 9- 12. sorban l evő ciklusmagot, hanem a 14. sorra kerül a vezérlés.

Bonyolultabb whil. utasftások


A while ciklus e lején vizsgált f<::ltételben bármilyen összetett C++ kifejezés áJthat.
Ez tartalmazhat logikai operátorokat is (és: &&, vagy: I I, tag:ldás: O. A 6.2 Lista egy
kissé összetettebb while utasítást mutat be.

6.2 lj... - Egy ÖlSZetottobb whilo ciki.. fcomploxwhilo.cppl


o: II 6.2 LiAtl.l.
1 : II Osszetettebb whi le ciklus

,.
2: ~includc <iostream>

4: int main ()
5: {
6. unsigned short small ;
7. unsignod loog large ;

,.
8. const unsigoed short MAXSMAIJIJ",65535 ;

10: std: : cout « 'írjon be egy kisebb számot: '.


ll: std : : cin » small;
12: std :: cout« 'írjon be egy nagyo bb számot : ' ;
13: std : : cin » l arge ;
14 :
15 : std: : cout « 'khIebb: • « small « ' .. ' .
16:
11 : II minden iteráci6s lépésben három feltételt Vizsgál
18 : while (!lmall < large && large> O && smull < Mi\XSMlILL)
19 :
20 :
21 : if (small 1; 5000 == O) II 5000 soronként kiír egy pOttyOt
22 : std : : cout « • . • .
23 :
24 : small+ +;
25 :
26 : large-:2 ;
21 :
28 :
110 II. rész· Bevezetés a C+ + programozási nyelvbe

29 : std: :cout « "\nKisebb : " « small


30: « " Nagyobb: " « large « std: : endl;
31 : return O;
32 :

írjon be egy kisebb számot: 2


írjon be egy nagyobb számot: 100000
Kioebb : 2 .....
Kisebb:33335 Nagyobb : 33334


EZ;J program egy egyszen1 játék. Beírand6 egy kisebb és egy nagyobb szám. A kisebb
egyesével növekszik, a nagyobb kettesével csökken. A játék célja annak kitalá lása,
hogy hol találkoznak. A 10-13. sorban írhatjuk be:il számokat. A 'IH. sor egy olyan cik-
lusfehételt tal'talmn, mcly esak az alábbi három feltéte l egyidejű teljesülése eserén en-
gedi meg a ciklusmag kfutásál:

• a sma11 nem n:!gyobb a large-nál


• a large pozitív
• a sma11 kisebb MAXSMALL-n{11 (ami a kis egészek m~rethal1lrd)

A 21. sorban II small SOQO-es maradékát sz.1mítja ki :il program. Ez csak akkor nulla, ha
a small egész számú többszöröse 5000-nek. Ne feledjük , hogy ez:! viz..<;gál:!t nem vál-
loztatj:! meg :il sma11 énékét. Amikor az 5000-es maradék nulla , egy pont (.) íródik ki
a képerny6rc, hogy lássuk a folyamat haladását. A 24. sorban n6 a sma11, a 26. sorban
pedig kettesével csökken ,I large énéke.

Ha a h:'irom fe ltétel valamelyike séIiiI, véget ér a ciklus, és a progmm futása a ciklus


végi z,h6jel utáni 29. sorban folytatódik .

Elképzelhető, hogy az ~előjeles nu1!ánk" miatt az <llábbi figyelmezlelést kapjuk:


"complexwhile.cpp·: WB012 Comparing signed and unsigned values in
functio n main() a t line 18
(E16jcles és e16jel nélküli értékek ósszehasonlitása a main () függvény
18. sorában)

Kényszeríteni lehet az előjel nélküli konstans használatát egy u bettl ooaillesztésével


(jelen esetben például Ou használatávaD.
6. óra • V_ési sze,bzetek 1111

A continue és a break
Bizonyos esetekben szeretnénk visszatérni a dklusfejhez (a ciklus kezdetéhez), még
mielőtt a további Ul,lsltások végrebajtódnának. A continue (jelentése: Jo(ylasd) utasí~
tás visszaugrik a ciklus elejére .

.\1á5 esetekben pedig még azelőtt szeretnénk kilépni a ciklusból , hOb'Y a kilépéshez szük-
séges feltételek fennállnának. A break (jelentése: kit6rés, megszakílás) utasítás azolmal
elhagyj3 a ciklust, és a cikjus végi wr6jel után folytatódik :l progr<lm végrehajtása.

A 6.3 Lista ezeket <I Z utasításokat mutatja-be. IH tovább bonyolódik az el6bbi játék
A játékosnak be kell írnia egy kisebb és egy nagyobb számot, egy szordlszámOl, vala-
mint egy célértéket. A kisebb szám egyesével emelkedik. A nagyobb sz,1m kellesével
csökken, kivéve, ha a kisebb szám a szorz6szám többszöröse. Akkor ér véget a játék,
ami kor a kisebb sz.'í m (small) meghaladja a(z eredetileg) nagyobbat (large-ot).
Ha a nab'Yobb szá m éppen eléri a célénéket, akkor megjelenik Cgy elismer6 üzcnet, és
a játék véget ér.

A játékos feladata, hogy már el6re megadja azt a célértéket, amiL a nagyobb ,~zá m a já-
ték utolsó lépésében fe lvesz.

o: II 6.3 Lista
1 : II A oreak és a continue bemutatása
2: hncIude <iostream>
3: using namespace std:11 E fájlban e16kerOl a !Jtd : : cout ,
4: II std :: cin, std :: endl, sto.
5,
6 : jnt muin()
7: {
8: unsigned short small ;
9: unsigncd long large;
10: unsigned long skip;
11 : unsigned long t arqet ;
12 : connt unsigned short MAXSMALL ~65535 ;
13 :
14 : cout«· írjon be egy kisebb számot : ";
15 : cin » small;
16: cout « "írjon be egy nagyobb számot : " ;
17 : cin » large ;
18 : cout « 'Adjon meg egy szorz6számot : " ;
19 : cin » s kip;
20 : cout « 'Adj a meg a tippelt célért6ke t : "I
21 : cin » target ;
22 :
23 : cout « "\n" ,
24 :
112 11. nlsz • Bevezetés 8 C+ + programozási ny.Ml.

25 : II a ciklus 3 fe l tételé nek megadása


26 : whil e (sma ll < l a rge && larg e> O && small < MAXSMALL)
27 : (
28 , small++ :
29 :
]0 : i f (smaH % skip """ 0 )11 ki mara dj o n a csökkentés ?
31 : {
32 : cout « 'kimarad : • « small c< endl ;
33 : continue ;
34 :
35 :
36 : if (la r ge == t arge t ) /1 Pon tos an egyezik acélér t ék k el ?
37 : {
38 : cout « 'Célba értiInk ! ' ;
39 : break;
40 :
41 :
42 : large =2 ;
<13 : II a while ciklus vége
44 :
45 : coul « " \nKiscbb : • « emaIl « • Nagyobb : " « large « endl ;
46 , return O;
47 :

írjon be egy kisebb számot : 2


í rjon be egy nagyobb sz ámot : 20
Adjon meg egy szorzószámot : 4
Adja meg a tippelt célértéket : 6
k i marad : ",
k imar"d : 8
Kisebb : 10 Nagyobb : 8

Ebben a játékban a jálékos veszített; a kisebb szám azel őtt haladla meg a nagyol, mie-
lőtt az elérte volna a 6-os célértéket.

A 26. sorban helyezkedik el az összetett ci klusfeltétcJ. Ha a sma ll továbbra is kisebb


ma.dd a large-nál, a l arge nagyobb mrlmd nullánál, valamint a sma ll nem lépi túl
a maximális előjel nélküli rövid egész számok méretét, akkor a program belép a ciklus-
magba.

A 30. sorban vizsgáljuk meg azt, hogya szorz6számnak többszöröse-e sma ll értéke.
Ha igen, akkor a con t i nue parancs hatására a vezérlés a 26. sorra kerül, ezzel elmarad
acélérték elérésének vizsgálata és a l arge csökkentése.
6. 6ra • Vezérlési szertezetek 1113

A 36. sorban vizsgáljuk meg azt, hogya nagyobbik szám egyenl6-e a céJé rtékkel.
Ha igen, győzött a játékos. Megjelenik az elis merő üzenet és elé rjük a b r eak utasítást.
Ezáltal azon nyomban ~kitö rünk ~ a whi l e ciklustJÓl, és a progmm futá sa a 45. sorban
folytatódik.

Óvatosa n használjuk a cont i nue és a brea k utasításokat. Nehéz megérteni .tzokat


a programo ka t, amelyek egy·cgy ciklus be lsejé ből hirtele n kilé pne k, és egy kicsiny kis
while ciklust is olvashatatlanná lehet tenni néhány szabadosan használt continue és
a break utasítással.

Aprop6 Névtarak használata


Ez a programpélda egy újabb kulcsszót is becsempészett a látóterünkbe: usi ng
narnespace, azaz: névtér használata. Ez arról értesíti a fordftóprogramot, hogy
amennyiben függvényhívást talál (mint amilyen például a cin vagy a cout ), akkor
képzelje oda a megadott elótagot (esetünkben ez a std :: ). Ez csökkenti a begépe-
lendő betük mennyiségét.
Mivel sok fordítóprogram nem ismeri még ezt a direktíváI, ebben a könyvben nem-
igen fogjuk használni. A Borland C+ + fordító ismeri.

while{ 1) - végtelen ciklusok

Egy while ciklusfejben vizsgált feltétel bármilyen összete u C++ kifejezés lehet. Amíg
é né ke igaz ( true) , fo lytatódik a ciklusmag fl,l(ra tása. így olya n ciklust is lelre le het hoz-
ni, mely soha nem é r véget, ha az 1 szá mot használjuk a vizsgá landó feltételben. Mivel
az 1 mindig "ig:lZ", a ciklusb61 soha nem lé pünk ki (hacsak nem éri.ink el egy b reak
utasítást). Ezt ~végt e len ciklusnak" hívjá k. A 6.4 Lista ezt a módsze rt használja, melybcn
tízig s71i molunk el.

6.4 Um - V6gte1en ciklus a while(1} legftsltg6vel (whilaforaver.cpp)


o: II 6 . 4 Li sta
1 : II A ' while iga z ' ciklus b emutat6ss
2 , 'incl ude <iost r e a m>
3,
4 : in t main ()
5, (
6: i n t coun t er o: O;
7,
8: while (1)
9: {
10 : count e r ~~;
11 : i f (c ount.er > 1 0)
12 : bre ak ;
13 :
14 : st.d : : cout « 'Számlá ló , • « count er « '\n ' ;
15 : re t u rn O;
16 :
114 11. rész • Bevezetés a C+ + programozási nyelvbe

Számláló: 11

A 8. sorban olyan rel tétel van megadva, amely sosem [esz hamis . A ciklusmag II 10.
sorban megnöveli a számláló (counter) változó értékét, majd a ll. sorban megvizsgál-
ja , túlhaladta-e már a tízet. Ha nem , akkor tovább folyik az iteráci6. Ha viszont II szám-
láló nagyobb tíznél, a 12. sorban található break véget vet a ciklus fuLásának. A prog-
ramvezé rlés .1 14. sorr.:l kerül , ahol II végeredmény kiíródik.

A program működik, de nem szép. Kitún6 példája annak, amikor alkalmatlan eszköz-
zel okjunk meg egy fei<lda tol. Ugyanez megv;:dósflhaló (Igy is, hogy a számláló érlék6-
ne k e lJe n6rzést:t oda tesszük, ahová való: a ciklusfej feltételébe.

Figyeleml lefagyások
A while (1) -hez hasonló végtelen ciklusok a számlt6gép lefagyását eredményez-
heti, ha a kilépési feltétel soha nem teljesül (vagy nincs is). Csak nagy körültekin-
téssel használjunk ilyet, és teszteljük alaposan!

A C++ szá mos utat tesz lehet6vé ugyanazon cél eléréséhez. Az igazi tudás azl jelemi,
hogy kell ő érzékkel megta láljuk az adott feladathoz leginkább ill őt

HoIyeo
Ila szn~ljunk while ciklust olyan ire- Ne tegyünk pontosvesszőt közvetle-
ráci6hoz, 1l1cly egy fe ltétel ign voltá- nül a while () után, mert ez ciklus-
tól függ. mag nélkül zárja le az utasítást (azaz
Gyakorolunk önmérsékletet nem fog működnO.
a continue és a break ut:lsít~sok
terén.
Győz6djünk meg arról, hogy ciklu-
sunk végü l valóban véget é r.

A dO...while ciklus
Lehetséges, hogy egy wh ile ciklus magja soha nem fut le. A while ciklusfejben a mag
futtatása előtt megtörténik az e ll e nőrzés , és ha hamis a feltétel értéke, a kkor az egész
ciklusmagot átugorja a program. A 6.5 Lista eZl illw>Zlrálja .
6. 6,. • Vezérlési szsrl.ezetek 1115

6. 5 Usta - A whilu ciklus magjának átugrása (bodyskip.cpp)


o: II 6.5 L is t a
1: II A whi l e cik lu s magj ának ki hagyá sa,
2: II ha él f~ lt é t e l ha mis
3: IIincludc <i ost r eam>
4,
5: int main()
6,
7: int counter;
8: std : : cout « "Hány hell o legyen? " ;
9: std : : cin » counter;
10 : while (counter:> O)
ll : {
12 : std :: cout« ' Hello!\n" ;
13 : counter~- ;
14 :
15 : std : : cout « 'A számlá l ó állállél : • « countcr ;
16 : return O;
17 :

Kimenet
Hány hello legyen? 2
Hello !
Hello !
A ~zámlá16 állása : O
FUU;lssuk lIjr.1 il prognulloc és adjunk meg nulWt. Jlyenkor ezt Játjllk:

Hány hello legyen? O


A sz&mlá16 állása : O

A fdhasználót ar,d szólílja fel a program a 8. sorban, hogy adjon meg egy kezdóérté-
ket, melyet a számláló (counter) neVtl egész változóban tároL Enne k érté ké r a 10. sor-
ban elle n6rzi, t:s a cikl u..,magb'l1l csökkenti (a 13. sorban) . Az el ső fultatíi sko r a számlá-
lót 2-re állítnlluk be, így kéL<;7.er le is fu tott;:l ciklusmag. A második alka lommal azon-
ban nullát adtunk meg. A 10. sorbeli vizsg:'i lat eredménye hamis volt; a szá mláló nem
volt pozitív. A ciklusmag tehát nem futot! le, és egyetlen Hello sem le tt kinyomtatva.

Mi a teendő akko r, ha legalább egy Hello-t mindenképp szeretné nk megjeleníteni?


A while ciklus nem képes erre, men a feltt:telvizsgálat még a zelő!! megröné nik, hogy
bármi is megjelenne. Kikényszerithetó a do log egy i f vizsgíilattal is, még a ciklustYd
való belépés el6tt:
i f (countcr < l ) II Egy mi nimális érték kénysze ri tése
couu Let:" = l ;
116 11. rész· Bevezetés a C+ + programozási nyelvbe

A programozók ezt ~belonlepkének~ Ckludge) hívják; csúnya és nem elegáns


megoldásnak.

A do ... whil e ciklus magja a feltételvizsgálat előlt fm le, így legalább egyszer minden-
k6ppen végrehajtódik. A 6.6 Lista az előző program újrafogalmazása a do ... whi le
öklussal.

6.6 usta - Pálda a do..wbile ciklus alkalmazására Idowhile.cpp)


o: II 6 . 6 Lista
1 : II A do ... whi1e bemutatása
2 : 'inc1ude <iostream>
3,
4 : int ma i n()
5: {
6: i nt cou nter ;
7: std : : cout « "Hány hallo legy en? ";
8: std : : c in » counLer ;
9: do
10 : {
11 : std : : cout « "Hello\n" ;
12 : counter - ;
13 : while (counter >0 ) ;
1 4: std : : cout « 'A számlálÓ állása : • « counter « std : : endl ;
15 : return O;
16 :

Kimenet
Há ny hello legyen? 2
Hello
Hello
fo. s zámláló á l lása : O

Fuuassuk újra a programm és adjunk meg nllllát. Most ezt látjuk:


Hány hello legyen? O
Hello
A s zá mláló állása : l

A felhasználót arrJ. szólítja fel a program a 7. sorban, hogy adjon meg egy kczdóérté-
ket, melyet a számláló (counter ) változóban tárol. A do ... wh ile ciklusban a ciklus-
mag a Feltételvizsgálat előtt lefut, ily módon garantált, hogy legalább eb'Yszer végrehaj-
tódik. A 11. sor kinyomtatja az üzenetet, és a 12. sorban csökkentett számláló értéke
a 13. sorban kenil e l lenőrzésre. Ha a Feltétclteljesül , akkor a vezérlés a ciklus eJej6re
kerül (a 11. sorra); különben pedig rácsorgunk a 14. sorra.
6, óra • Vezérlési szarbzetak. 117

A do .. . while ciklusban ugyanúgy használható a continue és a break, mint a while


ciklusban. A while és a do ... while ciklusok közü egyeden különbség;t fcJtérelvizs-
gálat helye.

A for ciklus
A while ciklusok írása közben gyakran azon találjuk magunkat, hogy beállínmk egy
számláló változót, vizsgálgatunk rá valamilyen feltételt, és fokozatosan növeljük (vagy
más módon változtat juk), val:lhányszor lefut a ciklusmag. A 6.7 Lista erre mutat példát.

6.7 ülla - M6tI OtIY piRa"".a wIllI. clkl.1nI (whil••uain.cppl


o : 116.7 LisLa
l: II while ciklus újra
2 : #lncludc <i oetrcam>
3.
4: int muin()
5: (

,.
6: int cou ntel:' " O;

8: whilc(countcr < 5)
9: (
10 : countcr++;
11 : std :: cout« 'POrgOk ! " ;
12 :
13 :
14 : std: : cout « • \nA számlálÓ : " « counter « • . \n";
15: rcturn O;
16 :

POrgOk! POrgOk! POrgOk! POrgOk ! POrgOk!


A számlálÓ : 5.

A számláló (counter) v{i!to:.:óértékét nu lbíra ;;!lítjuk a 6. sorban, majd a 8. sorban


megvizsgáljuk, kiscbb-e még 5-nél. A '10. sorban mcgnövcljük az ért.ékét. A 11. sor kiír
egy egyszerű üzenetc!; bizonyiíra cl tudja képzelni a kedves Olvasó, hogy ennél hasz-
nosabb feladatot is elláthat a program a számláló minden egyes növekedésekor.

Inicializáci6, vizsgálat, léptetés


A for ciklus egyctlen utasícásba gyűjti az inicializációt, a reltételviz,>gálalot és a lépte-
tést. A ciklusfejben a for kulcsszó után (egy záróje\páron belül) pomosvcssz6k áhal
határoltan három utasítás áll.
118 11. rész • Bevezetés a C++ programozási nyelvbe

Az els6 utasítás az inicializád6. Bármilyen C++ utasítás ideírható, de alapvet6en egy


számlál ó változó létrehozása, inicializálása szokott itt állni. A második utasítás a felté-
telvizsgálat - bármely helyes C++ kifejezés szerepelhet beIU1e. Ugyanarra szolgál, mint
a while ciklus feltételvizsgálata. /\ harmadik utasítás a léptetés. Á1t:llában egy számhíl6
növelése vagy csökkentése a feladata , de itt is elképzelhető bármilyen C++ Ul,lsítás.
Megjegyze ndő, hogy az első és a harmadik helyen tetsz6leges C++ utasítás állhat,
a második helyen viszont egy kifejezésnek kell állnia - egy olyan C++ utasításnak,
amely visszaad valamilyen f!rlékel. A 6.8 Lista bernUlat egy fo r ciklusl.

6.8 Lista - A for ciklus használata (fortoop.cpp)


o: II 6 . 8 Lista
1 : II A (or ciklus
2 : 8include <i ostream>
3,
4: i n t mai n ( )
5: (
6: int cou n ter l
7, for (counter., O; counter < 5 1 countcr ++)
8: std : : cout « ·POrgOk ! ' ;
9,
10 , std : :cout « ' \nA számláló : " « counter « •. \n" ;
11 : ret urn O;
12 :

menet
POrg:Ok! POrgO k! POrgOk l POrgOk ! PO r gOk !
A szám1616 : S .

• Ó

A f or ciklus a 7. sorban egyesíti a számlál6 iniciali7.áci6ját, aIlliak vizsg{,lalát, hogy ez


kisebb-e 5-nél , valami nl a számláló növelését. A ciklus magja a 8. sor. Természetesen
blokk is használható ciklusl11agkénl.

Figyeleml Gyakori for hibák


Gyakori hiba avesszó (,) használata pontosvesszó (;) helyett a for utasításban.
A fordítóprogram ezt jelzi is.
Egy másik gyakori hiba, hogya f or zárójele után pontosvesszővel (;) zárjuk a sort.
Ez egy ~ semmit sem csináló cikJust~ eredményez. Vannak esetek. amikor ennek ér-
telme lehet. így a fordítóprogram erre nem ad hibajelzést.
6. 6ra • Vezérlési szerl<02lI'.k 1119

Összetettebb for ciklusok


A for utasítások igen efÓS és rugalmas eszközt adnak a kezünkbe. A három független
utasíliis (az inidalizáci6, a vizsgálat és a léptetés) igen sok l ehetőséget hordoz. A for
ciklus az alábbi sorrendben hajlja végre a lépéseket:

L elvégzi az inidalizáció mGveleteit


2. kiértékeli a feltételt
3. ha a feltétel teljesül, végrehajtja a ciklusmagot és II léptetést.

A ciklus további lépéseiben a 2. és a 3. lépés ismétlődik.

Halmozott inicializáci6 és léptetés

Nem ritka, hogy több váilozót is inicializál unk, esetleg cgy összetettebb logikai kifeje-
zést vizsgálunk, valam int, hogy egyszerre több léptető ulasíl{,,~l is végrehajtunk.
Az inicializáci6 és a léptetés helyére több C++ ulasÍlás is odaírható, vesszővel clválaSZI-
\'3 . A 6.9 Lista két vállol.ó inidalil.áti6ját l!s ll!ptC::tésél mutatja.

6.9 Ulla - Halmozott utaltiaolc • for ciklulfejben lformulti.cpp)


o : II 6 . 9 Lista
l : II bemutatja, hogy hogyan lehet tObb utasitást elhelyezni
2 : II a for ciklusfejbon
3: 'includc <iostream>
4,
5 : int lMin()
6: {
7: for (int i=O, j=O ; i<) ; i·H, j·H)
8: s t d : : cout « " i : • « i « " j : • « j « std : : e ndl ;
9: ret u rn O;
10 :

Kimenet
i : O j: O
i : 1 j: 1
i: 2 j: 2

A 7. sorban kél változ6, az i és a j kap O kezdóértéket. A kiértékcIt feltétel (i<3) telje-


sül, így lefut a tiklusmag és kiírásm kerülnek az értékek. Végül a ciklusfej harmadik ré-
szében á1161éptetések is megtörténnek, megnövclve az i és a j értékét.
120 II. rész • Bevezetés a C+ + programozási nyelvbe
A 8. sor végrehajtása után újra és újra kiértókel6dik a feltétel , és amíg igaz marad, meg-
történnek a léptetések (az i és a j értéke tovább n6), valamint a ciklusmag is ismétel-
ten lefut. Ez mindaddig folytatódik , amíg meg nem bukik a feltétel. Ekkor a vezérlés ki-
lép a ciklusból, és a léptetés is elmar.!d.

Üres utasftások a for ciklusfejben

A for ciklusfej bármely (a kár az összes) lltasítása elmaradhat. Ezt úgy valósílhatjuk
meg, hogy nem írunk semmit a pontosvessz6k által kijelölt helyre. Ha o ly.m for cik-
lust szeretnénk írni, amely pontosan úgy mllködik, mint egy while ciklus, akkor hagy-
juk el az e1s6 és a harmadik lIIasHást. A 6.10 Lista eZl illusztrálja .

&.10 Lista - NIda a lor _fajben oIhoIyazIoet6 O............ (fomuII.cpp)


o: II 6 . 10 Lista
1 : II ürea utas itás a k El f or cikluöfejben
2 : #include <iostream>
] :
4: int main()
5: (
6: int eount cr = O:
7,
8: for( : eounter < 5: )
9: {
10 : counter++ :
11 : std :: cout« "POrgOk! ' :
12 :
13 :
14 : std : : eout « • \nA számláló : ' « eountcr « •. \n' ;
15 : return O:
16 :

POrgOk t POrgOkt POrgOk! POrgOkl POrgOkl


A számláló : 5 .

Bizonyár.! felismeri a kedves o lvasó, hogy ez ponlosan úb')' működik , mint egy koráb-
ban bemutatott while ciklus. A 6. sorban a számláló (counter) változó kezd6értéket
kap. A 8. sorbeli for utasítás nem inicializál semmit, ám megvizsgálja, hogy a számláló
kisebb-e ötnéL Nincs léptct6 utasítás sem, így ez a cik!usfej teljesen úgy viselkedik,
minlha ezt írtuk volna:
while (counter < 5)
6. ó... Vezériési szer1<ezetek 1121

Újra hangsúlyozzuk, hogy a C++ számos utat kínál egy-egy feladat megoldására. Ta-
pasztalt C.+ programozó nem írna ilyen for ciklust, de ez jól mutatja a for rugalmas-
ságál. Valójában olyan foz:- ciklusfej is megfogalmazható (néhány jól elhelyezett
continue és break révén), melyben mindhárom utasítás üres." 6.1J LiSla bemutatja,
hogy hogyan.

6. t 1 lista - Egy üres for ciklusfej (forempty.cpp)


o: /1 6 . 11 List<l
1: II üres utasítások a for ciklusfejben

,,
2 , 'include <iostream>

4: int main()
5, (
6: i nt.. counter",O : II initi1'llli7.t'ltion
7: int max:
8: std : : cout « 'Hány hello legyen? ';
9: std " cin » max ;
10 : (or ( ;; ) II a for loop thett doesn ' t cnd
11 : {
12 : if (counter < max) II test
13: {
14 1 std : : cout « 'Hello!\n';
15 : countcr++; /1 increment
16 :
17 : else
18 : break ;
19 :
20 : return O;
21 :

Kimenet
Hány hello legyen? 3
Hello!
Hello!
Hello!

A fo r ciklusfej a végsőkig ki leLL üresítve. Mind a ke:cdőérték megadása, mind a vizsgá-


lat és a léptetés át lett helyezve máshová. A 6. sorban történik meg :lZ inicializáci6, még
a for ciklus el6lt A feltételvizsgálat egy kOlön:í116 if-ben zajlik a 12. sorban, és ha ez
sikeres, akkor a lS. sorban eggyel megnő a számláló értéke. Ellenkező eselben a 18.
sorban l evő break hatására elhagyjuk a ciklusmagot.

Bár ez a program valahol elég bizarr, vannak esetek. amikor pontosan egy for ( ; ; )
vagy egy while (1) ciklusra van szükségünk A switch utasítás t.1rgyalásakor látunk
majd egy ésszen1 példát ezekre a furcsa ciklusokra .
122 11. rész · Bevezetés a C++ programozási nyelvbe

Üres for ciklus


Annyi minden elvégezhető a for ciklusfejben, hogy néha már semmi teendő nem marad
a ciklus mag számárA. Ilyen esetben tegyünk egy üres utasítást (;) a ciklusmag helyére.

A pontosvessző lehet egy sorban a for utasítással , de ezt könnyű szem elól tt:veszleni .
A 6.12 Lista rámutat, hogy hogyan érdemes ezt megírni.

6.1 2 Lista - for cikiul Orel 8 ciklusmaggal (fomullbody.cppt


o: II 6.12 Lista
1 : II Az Qrell uLasitá!'l
2 : II elhelyezése ciklusmagként
3 : 'incluae <ios t ream>
4,
5: i n t ITIuin()
6: {
7: tor (int i - O; i<5; sta : : cout « ' i: • « i ++ « std : : e nd l. )
8,
9: rcturn O;

-
10:

L o
"L 1
2
3
"
l , 4

A 7. sorb:m található for ciklus három utasítást tartalmaz. Az inicializációlétrehozza és


beállítja az i változ6c A feltételvizsgálat ellenőrzi, hogy i kisebb-e ötnél, végül a léptc-
t~si utasítás II kiírás után megnöveli iértékét.

Nincs semmi egyéb teendője a for ciklusmagnak, így al ün..!s utllSítást (;) használjuk.
Megjegyzendő, hogy ez nem valami csinosan megteJVezett for utasítás, mert a lépteté-
si rt:sz messze túllépi feladatkörét. Szebben nézne ki a cikllIs az :L!{lbbi formában:
8: for (int i = O; i<5 ; 1++)
9: std : : cout « "i: " « i « endl;

Bár mindkét progr.tm ugyanazt csinálja, ez utóbbi példát egyszenibb megérteni. Azt is
tartsuk szem e l őtt, hogy a ciklusfejben lélrehoZOll változók csak :I cikluson belül létez-
nek; a ciklus lefutása után megswnnek.
6. óra • Vezérlési 113

Egymásba ágyazott ciklusok


A ciklusok egymásba ágyazhatóak, azaz lehet ciklus egy másik ciklusmagban is. A bel-
ső ciklus mind;mnyiszor végigpörög, ahányszor csak a külső ciklus magja lefut. A 6.1 3
Lista bemutatja, hogy hogyan lehet egymásba ágyazott ciklusokkal egy mátrix elemeit
végigjárni és kiírni.

6.1 3 Usta - Egymásba ágyazott for ciklusok (fomested.cpp)


o: II 6.13 Lista
l: II E9ymásba ágyazott for dklusok
2: ~incluae <iostream>
] ,
4 : int m"in ()
S: {
6: int rows , column s ;
7: char theChar ;
8: std :: cout« "Sorok száma? " ;
9 : std : : cin » rO\ll9 ;
10 : std : : COUI: « 'Oszlopok száma? " ;
ll : sta: : cin » columns;
17. : Gtd :: coul:« 'Milyen karakter legyen? ' ;
13 : std: : cin » theChar;
14 : for (int i = O; 1<rows; i+t)
15 : {
16 : tor (int j = O; j < eolumns ; jH)
17 : std : : eout « theChar ;
18 : std :: cout « ' \ n " ;
19 :
20 : return O;
21 :

Sorok száma? 4
Oszlopok száma? 12
Milyen karakter legyen? x
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx

A progrn m felszólítja a felhasználóI, bogy adja meg a sorok és oszlopok kívánt számát,
valamint a nyomtatandó karaklert. AJ. első for ciklus a 14. sorban lélrehozza és érték-
kel látja el az i számlál6t, s már futhat is a külső ciklus.
aC++

A 16. sorban va n a külső ciklus' magjának első som. lu indít juk a másik for ciklust. Egy
másik ciklusváltozó (j) is inicializálódik, majd indulhat a bels6 ciklusmag. A 17. sorban
kiíródik a kiválasztott karakter, majd a vezérlés visszatér a belsO' for ciklusfejhez. Ész-
revehetjük, hogy a belső ciklus csak egyetlen utasítás (a kiválasztott karakter kiírása).
Megvi zsgá lj~lk a feltételt (j kisebb-e az oszlopok megadott számánál, columns-náD.
Ha ez igaz, akkor kiíródik a következő karakter, és n6 egyet j értéke. Ez mindaddig
folytlltódik. míg j értéke egyenlő nem lesz az oszlopok számával.

Anukor a belső ciklus vizsgálata sikertelenül zárul, akkor mjr 12 db x kinyollltaL'lSra


keruit; a vezérlés a 18. sarm kerul, ahol egy újsor kamkter íródik ki. A vezérlés ekkor
a külső ciklusfejhez tl:r vissza, ahol a megfelelő feltéteJvizsgáJat zajlik (i kisebb-e a sa-
rok megadott számánál, rows-ná1). Ha ez igaz-kén t értékeJ6dik ki , akkor újra lefut
a ciklusmag és megnő i l:n l:ke.

A küls6 ciklus második iterációs lépésében is elindul a belső ciklus. Itt j li jm Celveszi
a O kezd6értékel (t), és újra lefllt a teljes belső ciklus.

Az egymásba ágyazott ciklusoknak tehát az a lényege, hogya belső ciklus annyiszor


fm végig, ahány iter.ki6s lépése van a külső ciklusnak. igy a megadott kamktcr
~oszl o pszá m "-szor íródik ki minden sorban.

A switch utasftás
Az if és else ... if utasítások kombinációja igen zavaró lehet b izonyos mélységű'
egybeágyazáson túl. A CH ajánl is egy alternatívát. Az if-fe l szemben, amely csak
egyetlen vizsgálatot végez, a switch (jelentése: kapcso/6) Ichct6vé teszi, hogy egy vál-
tOZÓl tetsz6leges számú énékkel vizsgáljunk meg. A switch utasítás általános alakja
a következ6:
switch (kifejezés)
{
case elsoÉrték : utasítás ;
break;
case másodikÉrték : utasítás ;
brci.lk;

case utols6Érték : utasítás ;


break;
default : utasítás
break;

A kifejezés bármilyen helyes C++ kifejezés lehel, l!s az utasítás bármilyen C++
utasítás (vagy al.ok b\okk)a) lehet. J\. swit.ch kiértéke\i. a kife)C7J"!St, és al. eredményt
sorban összeveti a case értékekkel. Megjegyzendő azonban, hogy a kiértl!kell!s csak
egyenlO'séget ta rtalmazhat, relációs kifejezést vagy logikai mű've l eteket nem. Ha a case
6. óra • Vezérlési szel1<ezetek 1125

énékek valamelyike egyezik a kifejezéssel, akkor az ahhoz tartozó utasításhoz ugrik


:I vezérlés. A futás a soron következ6 utasításokkal folytatódik egészen a switch blokk
végéig, hacsak e16Ue break Ulasításba nem botlik. J la nincs olyan érték, amely egyez-
ne a kifejezésse l, akkor az opcio nálisa n megadható default ágra kerül a vezérlés .
I la nincs def ault ág, sem pedig a kiértékelt kifejezéssel egye".ő érték, akkor a prog-
ram kilép a switch utasításból.

Apropó A programozó hibamentesltése (de-faultozása)


Mindig érdemes default ágat használni a s witch utasrtásokban. Ha más miatt
nincs is rá szükség, a fe ltételezetten lehetetlen érték tesztelésére és egy hibaüzenet
kiírására mindig alkalmas; ez óriási segrtség lehet hibakereséskor.
Bár nem kötelező, a legtöbb programozó a default esethez is tesz egy break·et
a következetesség, hordozhatóság kedvéért.

Érdemes tudato.sítani, hogy .unennyiben egy case ág végén nim:s break ut:lsítás, ak-
kor a vezérlés r.'lcsorog a k övetkező sorra , a következő case ágra. Ez néha jól jön, de
általában hiba. Ha úgy dönt a programozó, hogy engedi továbbcsorogni a ve7,krJést, ér-
demes ezt egy megjegyzéssel is dokumentálni, hogy nem pusztán elfelejt6dött
a break .

A 6.14 Lista bemutatja a switch utasítás használatál.

6.14 LiI1a - Aswl1ch ...."'. hum"... (.witchor.cppl


o: II 6 .14 Lista
1 : II A switch bemutatása
2 : ~include <iostream>
3,
4 : int main ()
5: {
6: unsigned s ho rt :i nt number :
7: std: : cout « "Adjon meg egy számot l és 5 között:
8: std : : cin » number:
9: switch (number)
10: (
11 : case O:
12 : std :: cout« ' Sajnos ez túl kicsi!":
13 : broak ;
14 : case 5 :
15: std :: cout « " Remek dOnt és!\n" : II átcsorgunk
16 : case 4:
17 : std : : cout « ' J ó válasz tás ! \n' : / / átcsorgu nk
18 : case 3 :
19 : std : : cout « 'Kitun6 ! \ n"; II átcsorgunk
20 : case 2 :
21 : std :: cou t « 'Mesteri! \n" ; 1/ átcsorgunk
126 .rész ·

22 : case 1:
23 : std : : cout « "Hihetet.len! \n· ;
24 : break ;
25 : default :
26 : std : : <;:out « "Túl nagy! \n' ;
27 : break ;
28 :
29 : std : : cout « "\n\n" ;
30 : retllrn O;
31 :

-
Adjon meg
Kitun6 !
Mesteri !
Hihetetlen!
egy szá mot l és 5 között: 3

Adjon meg egy számot 1 és 5 között : 8


Túl nagy !

A progl1un felszólítja II felhaszn{1161, hogy adjon meg egy számol. EZL kapj:1 meg
:Iswitch utasítás. Ha a szám nulla, akkor a s .....itch 11. sora ilh:szkedik a kereseti ér-
tékre, éS:l ~Sajnos ez túl kicsi! ~ üzenet jelenik meg, majd egy break utasítás vet
véget a switch utasításnak. Ha a megadon énék 5, a program a 14. sorra . kapcsol ~,
ahol egy üzenet kiírása után rácsorgunk a 17. sorr-.. , ahol egy újabb üzenet megjeleníté-
se áll , és így tovább egészen a 24. sorig, ahol egy break utasíLásba botlik a vcz(:rlés.

Az a végs6 hatása ezeknek a programsoroknak, hogya 2, 3, 4, 5 szá mok esct(:n több


üzenet is megjelenik. lia a sz:1m nincs a 0 . .5 intervallumban, akkor fe lt C:te l ezhctőcn túl
nagy, (:s a 25. sor default utasítása hívódik meg.

Kérdések és válaszok
Kérdés: Mi alapjánlJálasz/ az if .. . else és II switch J...'Óz t"ll?

Viilt/sz: Ha egy-két else ágnál löbb szerepel, és mindben ugyanazt a változót vizsgál-
juk, akkor megfontolandó a switch használata. Ha nem egyenlőséget , hanem valami-
lyen reládót vizsgálunk (például a > b), akkor nem lehet a switch utasítást használni.
6. óra • Vezérlési szerkezetek 1127

Kérdés: Mi alapjáll választ a while és a d o ... while /Wzlll?

I "á/asz: Ha a ciklusmagnak legalább egyszer mindenképp le kell fULnia, akkor


a do ... wh ile az ajánlott, egyébként :1 whil e használandó.

Kérdés: Mi alapján /Jálaszt a while és a fo r Mzi/I?

la/asz: Ha inicializáini kell egy számláló változÓI, és minden alkalonmlal meg kell nö-
\"el ni, akkor a for ciklus használata javasolt. De ha már inicializálva van a változó, és
nem kell minden lépésben növelni, a while ciklus jobb választás lehet.

Kérdés: Melyik CI jobb, a while (l) vagy a for ( ; ; ) ?

I'á/asz: Nincs köztük sz:hnottev6 különbség.

Gyakorlatok
Ezen az ó rán megismertünk néhány összetett vezérlési szerkezetet. Tudásunk elmélyí-
tc.!sc végett válaszoljunk meg néhány kérdést és végezzünk el néhány gyakorlatot!

Kvfz
I. Milyen adattípust érdemes használni a for dklusban?
2. Mi a különbség a break és continue között?
3. A 6.13 Lista egymásba ágyazott for ciklusokaI mut.Holt be. Egymásba lchel-e
ágyazni whil e vagy do ... while ciklusokaI is?
4. Mit csinál a break parancs a s witch utasításon belül?

Feladalok
l. Módosítsa úgy a f orl oop. epp programot, hogy lei:>eg6pontos (floa t) számot
haszniiijon ciklusváltoz6kénl, és nyomtassa is ki az értékét a ciklusmagba nl
A léptett!skor 1 helyett növelje az értékét Ü,l-del.
2. Módosítsa úgy a fornest ed. cpp progmmot, hogy használjon for helyett whBe
dklusokat! Előszór csak az egyik f or-l cserélje ki while-ra, majd a másikat is1
Ebből az is látszani fog, hogy különböző lípusú ciklusok is egybeiigyazhatóak.
3. Módosítsa úgy a swi teher . epp programot, hogy az egyik ca se ág tartalmazzon
egy ciklust! Lefordíthat6-e és fut-e így a program?
128 1r. rész· Bevezetés a C+ + programozási nyelvbe

Válaszok a kvfzkérdésekre
1, A legtöbb progmmozó csak egész számot használ for ciklllsváltoz6ként, de ez
nem a nyelv korlátja ; lehet lebegőpontos számot, szöveget vagy más adattípust
is használ ni.
2. A break parancs hatására véget ér a ciklus, és a vezérlés a ciklust követő követ-
kező utasításra kerül. A continue parancs hatásárd a ciklusmag hátrdlevó részét
átugorja a program, de a ciklusból nem lép ki a vezérlés (hacsak a kilépési fe lté-
td nem teljesül).
3. Semmi akadálya! Maga a C++ nyelv bels6leg következetes. Lehel for <:iklusL
h'lsználni while ciklusan belül, ezt akár do ... while cikluson belül stb ...
a programozó szabad kezet kap e téren.
4. It. ~witch utasíthbeli break parancs hatására a progl"<un kil6p a switch utasí-
tásból. N(:lkcil e az történik, hogy az első megfelelő case ágtó! kezdődően min-
den p:mlnCS végrehajt6dik egészen a swi Leh végéig.
II. RÉSZ

Osztályok
7. 6ra AJapvetö osztályok
8. 6ra Az. osztályokról - mélyebben
7. ÓRA
Alapvető osztályok
Ebben az órában a következl5krollesz szó
• Mi a tíPllS?
• Mi az oszL!ily és :lZ objektum?
• Ilogyan hozhatunk létre új osztályokat és azokba lllltoz6 objektumok:J.t?

Mi a tfpus?
A típus egy kategória. Az egyik olyan léllyeges mlajdons:'Íg, amiben az t!mocr különbö-
zik az tillatvilágt61 az, hogy képes katcgorizálni a dolgokat. Nem formákat tálunk sz:íz-
számra a sZ:lVannán, hanem á llatokat és fákat. És nem csupán állatokat, hanem azon
belül is gazellákat, l'.si ráfokat, elefánlokat, vízibiva Jyokat és így lOvább. Az ember rend-
szertanok:lt :llkotott, különböző bcsorol::'isokat, csopoltosításokat, fe[oszLíÍsokat és osz-
tályoka\. Röviden: nü emberek természetünkb61 adódóan típusokban gondolkodunk.

A narancs egy citrusféle, a dlrus egy gyümölcs, a gyümölcs növény, a növény pedig
élőlény.

Hasonlóan besorolhatjuk az autókat, a házakat, a .személyeket vagy <IZ alakzatokat.


C++-ban a típus egy objektumféleség, amely mérettel, állapottal és jó néhány képes-
séggel rendelkezik,
13Z I II. rész' Osztályok

Egy CH programozó igény szerint bármilyen típust létrehozhat. Ezek a C++ beépítet(
típusaira épülnek - ilyen például az int, a long, vagy a double - és rendelkeznek
azok minden képességével és lchclőségéveL

Progf'.lmokat legtöbbször hétköznapi problémák megoldására írunk Ilyen például


a munkatársak nyilvánt:lft<ísa va~,'y egy fűtési rendszer szimulálása, Noha egyszeru adatti-
pusokkal - egész vagy kar:akter - is megoldhatunk összetett problémákat, igazán komp-
lex feladatokat sokk.11 cgyszelUob objekrumokkal megoldani. Más szavakkal: sokkal e~,'y­
s7.eruób a dolgunk egy fűtési rend'izer szi.mulálásakor, ha különböző egyedi adattipuso-
kal hozunk létre a szobára, a hőmér6kre, a termoszlátokra b; a fútőteslekre. Minél job-
ban modeUezzük a való életet típusokkal, annál könnyebben írhatjuk meg a programot

Új tfpus létrehozása
Kor~lbban már bemutatwk a C++ beépített típusait, mint például az int (egész) és
a cha r (karakter). Egy változó típusából sok minclCl1re kövctkeztethetünk. Például ha
a Heigh t (magasság) és wi d Lh (szélesség) változókat előjel nélküli rövid egészeknek
deklaráljuk, világos,m kiderül , hogy O és 65535 közötti értékeket vehetnek fel (feltéte-
lezzük, hogy az előjel nélküli rövid egész 2 bájtot foglal).

A mérete mellett a változ6 képességeire is kövctkeztethetünk a típusból. Például:1 rö-


vid egészeket összeadhatjuk. igy ha a HeighL és Width változókat előjel nélküli rövid
egészként deklaráljuk, akkor az összegüket értékül adhatjuk egy harmadik változ6nak.

A változó tiplls:ib61 tehát :1 k övetkezők derül nek ki:

• Amemóriaigénye
• Milyen jel1egll információt tárolhanmk benne
• Milyen mavclctcket végezhetünk rajta

A CH 1chetóv(: teszi s:ljáttípusok létrehozását is a problémák megoldására. Új típuso-


kat osztályok létrehozásával deklarálhatunk. Az osztály tehát nem más, mint egy (lj tí-
pus defmíciója.

Osztályok és tagok
Osztályok létrehozásával deklacilhatóak Llj típusok. Az osztály változók - gyakran kü-
lönböző típusúak - és velük kapcsolatos függvények összessége.

Gondoljunk például úgy egy aut6m, mint kerekek, ajtók, ülések, ablakok, stb. összes-
sége. Másképp is megközelíthetjük persze a dolgot: egy autó képes mozogni, lassítani,
gyorsítani, megállni és így tovább.
7. óra • Alapvetó osztályok 1133

Az egységbe7..árás (encapsulation) nem más, mint egy dologgal kapcsolatos inf0n11áció--


iknak, és a kérdéses dolog képességeinek egy objekrumba tön~n6 összegyűjtése.

Az egységbezárás - ha csak az autós példára gondolu nk - programozói szcmszögb6l


igen hasznos. Minden adat egy helyen van, így igen könnyen hivatkozhatu nk rájuk,
másolhatjuk vagy módosíthatjuk őket.

Egy osztály ügyrelei vagy kliensei olyan oszt:ílyok vagy függvények , melyek az adott
osztályt h:lsználják. Az e!,rységbe71írns lehet6vé teszi a kliensek számár::~ , ho!,'Y a műkö­
dési elv ismerete nélkül is használhassák az osztályt. Vezethetünk autót p61dául anél-
kül , hogy é rtenénk, hOb'Yan is mllködik egy bels6égésű motor. Ugyanígy a kliens osz-
tály is használhatja az adolt osztályt anélkül, hogy belelátna annak működésébe. Azt
kell csupán tudnia, hogy mit csinál , de azt nem, hogy hogyan.

Az oszt:íly különböző változ6típusok és más osztályok tetszőleges kombirlációjáb61 áll-


hat. Az osztály változóit tagv:í ltozóknak vagy adattagokn:lk hívjuk. Az Car osztály pél-
dául rendelkezhet változókkal, mely az ülések számára, a r.ídi6 upusára, vagy a kere-
kekre utal és í~y tovább.

A tagváltoz6k, vagy másképpen az adatlagok az osztályunk változói. A tagváltozók


éppúgy réSzei az osztálynak , mint a kerekek és a motor az autó nak.

Egy os zt~ly függvényei jellemz6cn a tagv:.'íltozók mód osítás{tr::t szolg!iln:lk. Rendszerint


tagfüggvénynek V:lgy mctódusnak r,evezzük 6kel. A Car osztályban lehet például
II Start () és a Br ak e () függvény , melyekkel a sebesség énékét állítlultjuk be. A Cat
osztályb:tn hasonlóan tárolhaljuk az :'illat éJclkorfh és súlyá[, a mct6dllsok között pedig
a Sleep(), II Meow () és a Ch aseMice () szerepelhet.

A tagfüggvények az osztály függvénye i. A tagmggvények éppannyira reszei az osztály-


nak, mint II tllgváltozók, meghatározzák , mire képes az adott osztály.

Osztályok deklarálása
Egy osztályt II cl a ss kulcssz6val deklarál link, nyitó és záró kapcsos zárójelek közön
soroljuk fel a tagváltozókat és II tagrüggvényeket. A záró kapcsos zárójel után pontos-
vessz6t rakunk. így néz ki például a Cat osztály deklar:.'ici6ja:
class Cat
(
public :
unsigned int i t sAg e;
uns i gncd int i tsWeight ;
Meow () ;
) ,
134 111. rész • Osztályok

A eat osztály deklarálásával még nem foglalunk le memóriát, csupán közöljük a fordí-
tóval, hogy milyen adatokatlárolunk (itsAge és az itsWeight vállOz6k) és aZl, hogy
az osztály tagja i mire képesek (Meow ()). Ez azt is megmondja a fordítón ak , hogya eat
osztály létrehOl.ásá hoz mennyi memóriára van szükség. Jelen esetben - ha az egész tí-
pus 4 bájlnt foglal - a eat osztály 8 bájt mérett1!esz: 4 bájtOl igényel az itsAge és 4
bájtot az itsWeight. A tagfüggvények - jelen esetben a Meow () - nem fog lalnak kü-
lö n tárhelyel.

Osztályok nevezéktan.
A programozónak természetesen valamennyi osztá lyt, tagfüggvényt és tagváJtoz6t cl
kell neveznie. A 3. fejezetbe n (Változók és konslansok) említettük, hogy ezeknek
könnyen érthelC1eknek kell lenniük é.'i utalniuk kell a vá ltoz6 céljaira is. A Cat ,
Rectangle (tégla lap) és az Employee (alkalmazott) például megfelelő oszlálynevek.
Meow ( ), H ChaseMicc () éS:l StopEngine () szintén megfelel6 függvénynevek, hiszen
ut3lnak a fü ggvér1yek rendeltetésére. Számos progra mozó its (övé) e I6t:lggall(llja el
a tagváltozóit, mir1t például: itsAge, i tsWeight, itsSpeed. Ez a megoldás segít meg-
különböztetni 6kel azoktól a vállozókt6 1, melyek nem tagváltoz6k.

A C++ kü lönbségel lesz .. kis- és n agybet űk közön , l=ppcn ezért minden oszt{llyt cél-
szem hasonlóan elnevezni. Ha így teszünk, nem kell azon törnünk a fejünket , vajon
Rectangle, rectangle vagy RECTl\NGLE néven dekla rá lnrk-e az oszt[llyt? Néhány
programoz6 például hizonyos el6taggal látja el az osztályok neveit ( példáu l eCat vagy
ePer son), megint mások csupa nagy vagy csupa kisberút használnak. A könyv példái-
ban minden oszt(tly nevét nagybetűvel fogjuk írni.

H:lsonlóképp bevett gyakorlat, hogy számos programoz6 a függvények neveit n:lgybe-


tűvel, a változók neveit kishel"űve l kezdi. Több sz6ból á1l6 változ6nevek esetén vagy
aláhúzásjelet használunk (például ChaseJlice) vagy Illinden sz6 kezd6betűjét nagy-
betü"vel írjllk (példálll ChaseMice vagy DrawCircle).

Fontos, hogy bármilyen konvenciót is követünk , az váljék szokásunkká 6s a program-


jainknál ragaslkodjunk hozzá. Id6vcl ki31aklll az egyéni stílusunk, nemcsak ~, nevekre,
de például a behúzásokra, a kapcsos zárójelek helyére és a megjegyzésekre vonalko-
zóan is. Sz,ímos cég és fejleszt6i csoport rendelkezik hivatalos vagy ajá nlott kódolás!
stílussal. Tanuljllk meg 6kel.

Objektum definiálása

Az új típusunk egy objektumát - példányát - hasonl6 képp definiáljuk, IllÍnt például egy
egész típusú változ6t;
unsigned int GrossWeight ; II definiálunk egy e16jel nélküli egészet
Cat Frisky ; II defini áljunk egy Cal objektumot
Ez a kódrészlet definiál egy előjel nélküli egészet GrossWeight (bruttó tömeg) né-
ven. Megad továbbá Frisky néven egy a eat osztá lyba tartozó, vagyis ilyen típusú
objektumot.

Osztályok és objektumok
A kedvencünket sosem hívjuk macskának, minden macska saját nevet kap. Ezzel emel-
jük a nappaliban htmyél6 kedvencünkct a macska általános iclcája fölé. C++-ban ha -
sonl6k6pp gondolkodunk. A eat osztály maga az idea, míg az cgyes példányok
az osztályba tartozó objektumok. Frisky tehát a Cat osztály egyik eleme v.tgy példá-
nYd, mely rendelkezik el6jel nélküli egész típusú Grossweight <Idattaggal.

Az objeknun egyszeruen az osztály egy tagja , amikor pedig egy osztály alapján lélreho-
zunk egy objektumot, :l zl az oszUíly pé ldányosításának hívjuk.

Osztályok tagjainak elérése


t1'lilltán definiáltunk egy a eat osztályba taltoz6 objeklumol- jelen esetben a Frisky-l
-a pont operátorral 5ivatkozhatunk az adott osztály tagfüggvényeire és tagváltoz6i ....J..
1la például Frisky itsweight változójá t be szeretnénk állítani 50-re, akkor ezt a kö-
ve tkezőképpen tchetjük meg:

Friaky .i tsWeight - 50 ;

i lasonl6képp kell eljárnunk, ha meg szeretnénk hívni a Mcow () függvényt:


Frisky .Meow();

C++-b:lll sosem típ~l s hoz rendelünk értéket, l1:lnem változ6hoz. Az alábbi sor pékMul
helytelen , amit a fordít6 jelezni is fog, hiszen :lZ egész típusho? nem rendelhetjük ér-
tékként :IZ 5-öt:
int . 5; II helytelen

lia azonban már definiáltunk egy egész típusú vá ltoz6t, ahhoz ahhoz hozzárendelhet-
jü k az 5-öt:
int X; II definiáljuk az x- et egészként
x = 5; II az x értéké t beá l litjuk 5-re

Röviden: tároljuk az 5-öt az x nevű változ6ban , mely egész típusÚ. Ugya nezért helyte-
len a következő értékadás is:
Cut.itsAge=5 ;
Elóbb létre kell hoznunk egy Cat típusú objeknunot, aminek a tagváltoz6jához aztán
miír hozzárendelhetjük ,IZ 5-öt:
Cat Frisky ; II akárcsak az x-nél;
Frisky . itsAge = 5; II akár csak x = 5;
Privát és publikus hozzáférés
További kulcsszavak is előfordulhatnak egy osztály deklaráci6jában. Ezek közül a két
,I
legfontosabb public és a private.

Minden adanag és tagfüggvény alapértelmezés szerint privát eléresú. A privállagok


csak és kizárolag az adott osztályból érhet6ek el. A publikus eléresú tagokat ezzel szem-
ben bármelyik osztályból e lérhe tjük. Ez a megkülö nböztetés famos, bár zavaró lehet.
l logy világosabbá tcgyük a helyzetet, nézzük meg ismét a fejezet egy korábbi példáját:
clafls Cat

unsigned int itsAge;


unsigncd int itsWeight;
Maow() ;

Ebben:] deklaráci6ban az i t sAge, az itsweight és :1 Meow () pr/vM láthatósággal bír-


nak, hiszen eZ :lz alapértelmezett. Ez azt jelenti, hogy ha másképp nem jclczzí.lk, min-
den lag privát.

Ha ennek ellenére megpr6báln5nk kívülrő l e lérni az itsAge adaltagol, a fordító hibát
fog jelezni. Az al:'ibbi pélcb tehát működésképtelen:
eat Boots;
Boots.itsAgc:S; II hiba! nem férOnk ho~~á a~ adattagho~

Az osztálydefiníci6val gyakorlatilag utasítouuk a fordítót, miszerint az itsAge,


itsWeight l!s a Meow() tagokat csakis a Cat osztály lagfilggvényei érhessék e l. Ennek
ellenére az elGzG példában mégis megpr6bálruk kívülről elérni. Az, hogy Boats egy
Cat típusú objektum, még nem jelenti azt, hogy Boats privát részeit is e lé rhetjük.

Hogya Cat osztály tagjait kívülről elérjük, deklaráljuk őket publikusként:


class Cat

public :
unsigned int
Iln!ligncd int
Meow() ;

Most már az i tsAge, az itsWeight és a Meow () is publikusak. Éppen ezért


a Baat. i tsAge=S sor is hiba nélkül le fog fo rdulni.

Alapvető tervezési s7..abály, hogy azt adattagak lehetőleg mindig privát Játhat6s.'íggal
rendelkezzenek. Éppen emian olyan publikus eljárásokat is létre kell hoznunk, melyek
elérik (kiolvassák és módosítják) a privát aclattagokal.
7. 6,. . 137

Ezek a függvények lehetóvé tt!szik, hogy különválasszuk az adattárolás módját és


az adatok felhasználását. Ennek köszönhetően ha bármi megváltozik az adattárolásban,
nem kell újrairnunk azokat a függvényeket , melyek ezekkel az adatokkal dolgoznak.

Az elérhetóséget public és pr i vat e ku1cSSZ<lVllkkal deklaráljuk. Minden blokk a kö~


vetkező eléréssel kapcsolatos kuks.'izóig vagy az osztálydeklaráció végéig lan. Az osz·
tá lydeklaráci6t záró kapcsos z.1 r6jdlel és ponlosvesszővel 7.1UjUk.

Tekintsük az alábbi példát:


e l so!> Cat

public :
unsigncd int Age :
unsigned int Wcight ;
vo i d Meow () ;
},
eat Frisky ;
Frisky . Age "'" 8 ;
t'risky .weight = 18 ;
Frisky . Meow{) ; •
Vagy nézzük ezt:
class Car
{
public : II a kOvatkez6 5 tag publikus
vo i d Start () ;
void Accelerat e( );
voi d Bukn() ;
void SetYear(int year);
int GetYear () ;
private , II az utolsó kett6 privát
int Year ;
Cha r Model [255] ;
} , II az ontá l ydeklarác i ó vége
Car OldFa ithfui ; II 8 Car osztály egy példánya
l nt bought; II lokális e16jelcs egész
OldFaithf ul . SetYear(84) ; II értékadás
bought = OldFaith fu l. GetYaar() ; II a bought változó 84 lesz
OldFaith f ul . St a rt() ; II meghi vjuk a !l t art me tódust

A private kulcssz6val tehát korlálOzhatjuk az adauagok elérését és azt, hogy mikép-


pen módosítharjuk 6 kel.
38 II. rész •

Osztálymetódusok létrehozása

Minden deklariUt osztálymetódust definiálni is kell.

Apropó Tagmetódusok
A tagmetódusok (vagy tagfüggvények) definfciója az osztály nevével kezdődik,
majd két darab kenósponttal, a függvény nevével, végül annak paramétere ivel
folytatódik.

A tagmetódusok (vagy tagfüggvények) deflníci6ja az osztály nevével kezdtXlik, majd


kéc darab kelli5sponttal, a függvl:ny nevévd, végül ann:1k parnml:lcrcivel folyt:1t6dik.
A 7. ] Listában be mutaljuk az egyszeninek mondható eat osztály teljes deklarációját,
:lZ adalwg-hozzMérési függvényeket, ilklve egy általános célú tagfüggvl:nyt.

1.1 Liata - Egy egysz8ni _ly oIjúáui llimpl........,pl


Q. 1/ Bomutntjuk egy osztály deklarációját
1, II és az oszLály eljárásaineok definiálását
2,
3, 'includc <iostream> II oz az std : : cout miatt szükséges
4,
5: class Cat II az osztály deklaráció kezdete
6, {
7: publi c : 1/ a publiku s rész
8: int GetAge() ; /1 adattagelér ési függvény
9: void SetAge (int agc); II adattagelérési függvény
10, void Meow() ; II általános célú függvény
11 : private : II privát 9zukasz
12 : int itsAge; II tagváltozó
13 : J;
14,
15: /1 GetAge , publikus adattagelérési függvény
16 : II visszaadja az itsAqe tag értékét
17: int Cat : , GetAgc()
18 , (
19 : return itsAge ;
20 :
21 :
22 : /1 a SetAge publi kus def i níciója
2] : /1 publikus adattagelérési függvény
24 : II mellyel az it sAge tagot módosíLhatjuk
25: void Cat : : Set;a.ge ( i nt age)
26 :
27 : II beáll í t juk az itsAge változót
28: II melyet az age vál to zó bó l kapunk
29 : itsAge : age ;
30 ,
31 :
7. óra • 139

]2 : / 1 a Meow met6dus de finici 6ja


J] : /1 nincs visszatérési értéke
]4: / I ni ncsen paramétere
35 : / 1 kiirja a képerny6re . hogy "mcow '
]6 : void Cat : :Mcow()
]7 : (
]8 : std : :cout « ·Meow. \n';
]9 :
40 :
4 1: /1 lét r ehoz za a macska egy példányát, beállítja az életkorát
42: II nyávog egyet , megadja az életkorát és újra nyávog
43 : i nt maln()
44 : {
45 : Cat Frisky;
46 : Frisky.SetAge(5);
47 : Frisky.Hcow{) ;
48 : std : : cout « 'Prisky i a a cat who is • ;
49 : std :: cout« Frisky. Get Age() « ' years old . \n' ;
50 : Frisky.Meow() ;
51 : return O;
52 :

Meow.
Fri s ky is a cat who is 5 years old .
Meow.

Az 5. sorról a 13. sorig taltiljuk a Cat osztá ly definícióját. A 7. sorba elhelyeztük


a public klllcsszót, mely jelzi a fordítónak, hogy publikus tagok következnek. A 8. sor
a Ge tAge () ada tcag-cl~ rési függvé ny deklar:lcióját tanalmazza. En nek segítségével é r-
hetjük el az itsAge tanaImát, melyct a 12. sorban deklaráltunk. A 9. sorban helyet ka-
pou a SetAge () . mely a megadon paraméterrc beállítja az itsAge értékér.

A 11. sorban kezd6clik a Cat oszlály privát e l érésű része, mcly csupán a 12. sorban
deklará lt itsAge tagváltozót tartalmazza. Az osztá lydeklarációt záró kapcsos z:hójellel
és ponrosvessz6vel zárjuk le.

A 17. és a 20. sorok között definiáljuk a GetAge () tagfüggvényt. A metóc!us nem ren-
del kezik pamméterrel és egész értéket ad vissza. Ne feledjük: a metóc!usok neve előtt
szerepel az osztály neve és kél darab ken6spom. Ez jelzi a fordító számám, hogy a Cat
oszlály függvé nyét definiáljuk. A fejléc kivételével a Get Age () függvé ny definiálása
megegyezik a többi függvényéveL

A GetAge () függvény csupán egy sarbói áll, és az itsAge é rtékét adja vissza. Ne fe-
k..ujük: a main{) függvényb61 nem érhetjük el a Cat osztály privát elérésű itsAge, vi-
140 III. rész • Osztályok

szont elérhetjük a publikus GetAge () metódust. Mimhogy ct GetAge () a Cat osztály


tagfüggvénye, így eléri az itsAge vá1toz6L Ezzel már a main () függvény számánl át-
adható az i t sAge értéke.

A 25. sor tartalmazza ct Set Ag e (l definícióját. Egész típusú paramétert Váf és ez alapján
beállítja az itsAge értékér a 29. sorban. A Se tAge () ct Ca t oszLály tagfüggvénye, (gy
tdjes joggal kezelheti az itsAge tagváltoz6t.

A Meow () metódus implementációja a 36. sorban kezd6dik. Ebben az egysoros függ-


'lényben csupán kiír.ujuk ct Meow szót egy soremeléssel a képerny6re. (Emlékszünk
ugye, hogy a \n új soremelést eredményez?)

A 44. sorban kezdődik a már jól ismert main () függvény. Ennek nincs bemenő paraméte-
re, és üres (void) a viSSZatérési értéke. A 45. sorban létrel10zunk a Cat osztályból egy pél-
dányt Fr i s ky néven. A -i6. sorban beállítjuk a Se t..Age () függv~ny segítségével
az it s Age v1illozóját 5-re. Az elj{u"ást a példányazonosltóval, egy pontral és a metódus ne-
vével hívjuk meg. Eh.hez hasonlóan bármely más met6clusl meghívhatunk az osztályb61.

A 47~ sorban meghívjuk a Meow () tagf"Üggvényt, :l 48. és 49. sorokban kiolvassuk ~s ki-
íratjuk az i t sAge értéké t a GetAge () adattag-elé rési függvé ny segílSégével. Az 50. sor-
ban újra meghívjuk a Meow () függvényl.

Konstruktorok ós destruktorok, avagy az objektumok létrehozása és törlóse

Kétféleképpen definiálhatunk egy egésllípUSLI vá ltozót. Definiálhatjuk elóbb magát


a vlÍltozót úgy, hogy é rtéket csak később adunk neki:
int Weight ; II változ6 definiá lása
II programk6d
Weight = 7 ; II értékadás

Ugyanakkor definiá lás után akár azonnal is inicializálhat juk:


int Weight - 7, II definiálunk és inici alizálunk egyszerre

Ez a m6dszer egyesíti a definíciót és a kezdeti értékadásl. A v:í!tozó később persze sza-


badon módosítható, az inicializálás azonba n garantá lja, hogya változónk nem fog ér-
telmetlen értéket tartalmazni.

Mi a helyzet egy osztá lyadattagjaival? Az osztályok példányosít.ásakor lefut egy speciá-


lis tagfüggv~ny , a konstruktor. Ennek a feladata az osztály egy érvényes példányának
létrehozása. A gyakorlatban ez azt jelenti, hogy az adattagokat induló értékekkel l:'ítja
el. A konstruktor eljárás neve megegyezik az oszrályéval , de nem rendelkezik visszaté-
rési értékkel. Bemenő pammétereket viszont kaphat, ahogyan bármely más metódus is
az osztályon belül.
7.6ra • 141

Ha van konstnLktorunk, ajánlott cleslnlklort is deklarálni. Ahogy a konstruktor létrehoz-


za és inicializálja a példányt, úgy a destruktor az objeb.w m megszűnésekor eltakaritja
az objektumot és az esetlegesen foglalt memóriát felszabadítja. A destruktor neve
u&'Yanaz mim az osztályé de - (tilde) karakterrel kezdődik. A destruktornak nincs be-
menő paramétere és visszmérési é rtéke sem. A Cat osztályhoz például így kell
destnlktort írnunk:
-Cat cl ;

Alapértelmezett konstruktorok
Ai': alábbi sorral:
Cat Frisky(5);

rulajdonképpen a Cst osztály konslr1lktorát hívjuk meg, amely jelen esetben egy para-
métert kapott (S). r la paraméter nélkül hívjuk meg, úgy clhagyhat6 a zárójelpár és
az alapértelmezett konstruktor fulle.
Cat Ft"i s ky;

Az alapértelmezett ktmstruktor nem kap paramétereket.

Afordlt6 által biztositott konstruktor


Ha egyáltalán nem deklaráltunk egy osztá lyhoz konstnLkton, úgy a fordító autom:uiku-
s.1 n létre fog hozni egy alapértelmezcner. (Ne feledjük, az alapértelmezett konstnLktor
nem fogad paramétert.)

A fordító által kínált alap&rtelmezett konSlnlktor nem csinál semmit. Mi is deklarálha-


nmk ilyent, hiszen nem kell mást tennünk m.im üresen hagyni :I konstnLktor törzsét.

Ehhez :lzonban [:utazik két fontos megjegyzés:

1. Az alapénelmezeu konstnLktor nem kap paramétereket. Definiálharjuk mi ma-


gunk is, vagy rábízhatjuk ennek létrehozását a fordítóra is.
2. Ha létrehozunk bá rl1li~)1C1I sajál kOnstnlktort (paraméterekkel vagy anélkül)
a fordító nem ad a lapértelmezett konstmktort. Ebben az esetben ezt is nekünk
kell definiálni.

A dCSTnlktor hiánya esetén hasonlóképpen jár el a fordító. Ha nem adunk meg


destruktort, akkor kapunk egy üres törzsűt, amely nem csinál semmit.

Ha azonban deflniáltunk bármilyen konslmktort, :Ikkor mindenképpen definiáljunk


destrukton is, még akkor is ha üres és valójában nem csinál semmit. Noha az alapértel-
mezen destruktor is tökéletesen teszi a dolgát, a saját destmklor átláthat6bbá teszi
a k6dunkat, egy üreset megírni pedig igazán nem nagy munka.
142 111, rósz' Osztályok

A 7.2 listában a Cat oszlály kapou egy konstruklon, mely a Cat példányokat hivalaU
inicializálni úgy, hogy beállítja az általunk megadotl életkort. Destruktor nélkül nem
lenne teljes a program, így bemutat juk azt is.

7,2 Lista - Ko..truktorok Ú destruktorok (_.Optl)


o: 1/ Bemutat juk konstruktor és destruktor
1: / I deklarációját il Cat osztályban
2: #inc!ude <iostream> II az std :, eout miatt
3: using std : : cout; 1/ mindig az std: :cout-ot használjuk e forrásban
4,
5: class Cat II az osztálydeklaráci6 kezdete
6: {
7, public: II publikus rész
8, Cat(int initialAge) ; 1/ konstruktor
9, -Cat () ; II destruktor
10 : int GelAge () ; 1/ adattagelérési függvény
11 : void SetAge(int age) ; II adattagelérési függvény
12 : void Meow ( ) ;
13 : private: 1/ privát rész
14 : int iteAge; II tagváltoz6
H: ):
16 :
11: II a Cat konstruktora
18 : Cat : :Cat( i nt initialAge)
19, {
20 : itsAge • initialAge ;
21 :
?2 :
23 : II destruktor , al1\ely nem cs inál senunit
24 : Cat : : ~Cat()
25 : {
26 :
27 :
28 :
29: II GetAge, publikus adattagelérési tüggvény
30 : II visszaadja az itsAgc tag értéké t
31 : int Cat : :GctAge()
32 :
33 : rúturn ltsAge;
34 :
35 ,
36 : II a publikus SetAge definíci6ja
37 : II a dattagelérési függvé ny
38 : void Cat : : SetAgc {int age)
39 : {
40 : /I beállítja az itsAge értékét
41 : /I a paraméterul kapott értékre
42 : its1'lge = age;
43 :
44 :
45: II a Mcow met6dus defin íc i 6ja
46 : II visszatérési értéke : üres (void)
7. óra • 143

47 : II paramét erek : n incsenek


48 : II mit cs i nál: kiirja a képel,"ny6re , hogy meow
49 : void Cat : :Mcow()
50 :
51 : cout « 'Meow.\n';
52 :
53 :
5<1 : 1/ létrehozzuk a macs kát, beáll!tjuk az életkorát
55 : 1/ nyá vog egyet, lekérdezzük a z é let korát , majd i.!; mét nyávog
56 : int main()
57 :
58 : Cat Frisky(5);
59 : Frisky.Meow() ;
60 : cout « 'Prisky is a cat who is • ;
61 : cout « Frisky . GetAge () « • years old . \n' ;
62 : Frisky . Mcow ( ) ;
6] : Frisky.SetAge(7) ;
64: cout « 'Now Frisky i s • ;
65 : cout « f'ri sky . Go tAge (J « • yoars old . \n " ;
66 : return O;
67 :

](j

Meow,
Frisky is a cat who i s 5 yeal,"s old .
MCQw.
Now Frisky is 7 yenrs old .

A 7.2. List:1 hasonlít a 7.1-esre, kivéve, hogya B-as sorban bÓvÍletrük egy konSlruk-
torml. mely egész értéket vár, illetve a 9. sorban egy destruktorml, mcly ncm fogad pa-
mméterekeL A destruktorok sosem kapnak paraméterekct, illetve se a konstruktor, se
a destruktor nem ad vissza értéket, még üreset (void) sem.

A 18. és 21. sor között megfigyelheljük a konstruktor implementádóját, mely nagyon


hasonló a Set Age () adaltag-elérési függvényhez.

A 24. és 27. sor kÖ7.ötl megfigyelhetjük a -Cat () destruktort. A függvény nem csinál
semmit , de muszáj megadni , amennyiben az osztálydeklaráci6nál megadtuk.

Az 58. sorban példányosítjuk a Cat osztályt Frisky néven. Frisky konstruktom 5-öt
kap meg paraméterként. Nem kell meghívnunk a SetAge () függvényt, hiszen Frisky-
t lmÍf eleve úgy példányosíljuk, hogy az itsAge változóba 5 kerül, bizonyíték erre
a 61. sor kimenete. A 63. sorban Frisky itsAge változóját m6<.losítjuk, melyet a 65.
sorban kérdezünk le újra.
144 , II. rész • OSZtálYaI<

Ausing kulcsszóról bővebben


Ebben a példában találkoztunk a us ing kulcsszóval. Ez (namespace módosító
nélkül. amelyet az előző órában mutattunk be) közli a fordítóval, hogya cout függ-
vényt mindig az std függvénykönyvtárban keresse. Ha más, szintén a szabványos
könyvtárból származó függvényeket is használunk (ilyen például c in), akkor ezt
vagy mindenütt std: : cin fonnában kell megadnunk. vagy hasonlóképpen kell el-
járnunk, mint a 3. sorban.

Kérdések és válaszok
Kérdés: MilyeJ/ Hagy egy osztálypéldány?

Vá lt/sz: Egy osztály egy péklányának helyfoglalása mindig az adanagok helyfoglalásá-


nak az összege. A t:lgfüggvények nem az objektuman belil! foglal ják a memÓri[tt.

Nl!h:íny rordító 2 bájlos változó esetén 2 bájtnál valamivel többet foglal Je. Err61 továb-
bi részletekkel <IZ :1lt,dunk használt fordítóprogram leírása szolgá lhat, cle most még fe-
lesleges ilyesmivel foglalkozni.

Kérdés: Mlc'!I1/1em szabad pub/ikllsnak definiálni az adaffagokal?

Válasz: Az adauagok privát elérése anélkül teszi lehet6vl: az osztá ly felhaszná l6ja szá-
mára !lZ adatok használatát, hogy ismerné az osztályon belüli ad:ntárolás és adaúeldol-
gozás mikéntjét. Például ha a e a t osztály rendelkezik egy GetAge () met6dussal, ak-
kor :I Z osztály kliensei lekérdczhetik az életkort anélkül, hogy nJdnák, miképp tárolja
:IZ az életkort, v:lgy hogyan számolja ki azL A publikus adat ezzel szemben olyan, mint
:I globális változó. Bármely parancs hozzáfér, ha pedig megváltozik, akkor hosszú és
kellemetlen hibakeresés következik.

Gyakorlatok
Most, hogy már lLldunk egyet-s-mást az objeklumokr61 és osztáJyokr61, válaszoljuk meg
a kvízkérclés€:!ket a gyakorlatok segítségéve! pedig mélyíL'iük cl a megszerzett tudást.

Kvfz
1. Mi a különbség az osztály és az objekmm között?
2. Mi a kü lönbség a public és a private elemek között egy osztályon belül?
3. Mit csinál egy konstruktor?
4. Mit csinál egy destruktor?
7. óra • 145

Feladatok
l . Módosítsuk a simpleclass . cpp-t úgy, hogy létrehozunk egy második macskát
Spot néven. Nyávogásm tudjuk bírni?
2. Mi történik a simpleclass . cpp-hen, ha megpróbáljuk megváltoztatni
az i t sAqe változót a main () függvényból? Működik az itsAqe++ vagy
a Frisky. itsAge ++ kifejezés? Milyen következtetéseket vonhatunk le
a publikus és privát elérésekkel kapcsolatban?
3. Módosítsuk II simpleclass . cpp-t úgy, bogy az it!}lIqe publikus változó le-
gyen. Most hajtsuk végre újr:... a 2. feladatot! Ezután a main fiiggv(:nyb61 is 110z-
óférünk Fri sky adataihoz, viszont így sokkal nehezebb megmondani, hogy
hol rontottuk el (:s miért kapta a változó ;IZ aktuális (;rtékét.

Válaszok a kvfzkérdésekre
1. Az osztály egyfajta leírás vagy definíció, mely nem foglal memóriát. Az objektum
ennek az osztálynak egy megvalósítása (példánya).
2. A privát adatok és metódusok (függvények) csupán az adott osztályból érhet6ek
el. A publikus adatok és metódusok (függvények) az oSltályon kívülr61 is elér-
het6ck. Gyakorlatilag érdemes az osztályadatokat privát cléréslívé, a metóduso-
kat pedig publikllss{\ tenni.
3. A konstruktor létrehozza az objektumot az osztálydefinki6 alapján. Konstruktor
létreho7ltsát rábízhatjuk a fordító"l is, amennyiben nem bonyolult a progm-
munk, dc összetett objC;! ktumn ~1 megírhatjuk mi is. A saj:ít konstmktorok készíté-
sével nemsokárn részletesen foglalkoZl.lnk.
4. A d/;!struktor végzi :1 takarítl'isl, ha egy objckl"umrn már nincs szűkségünk.
Konstruktor létr/;!hozásác rábízhmjuk a fordítóra is, amennyiben nem bonyolult
a programunk, de összetett objektumnál megírhatjuk mi is. A saját destnJkLorok
készítésével hamarosan részletesen is foglalkozunk.
8. ÓRA

Az osztályokról - mélyebben
Ebben az órában a következő kről lesz sző:

• Mik azok 3 konstans t.agfüggvények


• Hogyan különíL"ük el az osztá ly keze16 felülctét a megva16sítását61
• Hogyan kezeljük osztályainkat, és hogyan vegyük rá a fordít6program ot, hogy
segítsen megtalálni és elkerülni a progmmhibákal

Konstans tagfüggvények
Ha egy osztály tagfüggvényéL const -nak deklaráljuk, ezzel megígérjük, hogya metó-
dus nem fogja megváltozt:ttni semelyik osztály tag értékét Úgy deklarálhatunk egy osz-
tálymetódust konstansként, hogya const ku!csszót a zárójelek urán (a pontosvessző
elé) írjuk. Példaként deklaráljuk a SomeFunction () konstans tagfüggvényt úgy, hogy
ne legyen egyetlen argumentuma sem, és csak az üres típust adja vissza. Ez a követke-
zőképp néz ki:

void SomeFunction() const ;


148 1 ll, rész • Osztályok

A hozzáféfŐ függvényeket ("gel accessor" vagy "geffe!" metódusokat) h'Yakmn konstans


függvényekként de klar:ílják a const módosító segíL'iégével. A Ca t o5zt.álynak két hoz-
zá férő függvénye van:
vo i d SctAgc(int anAgel ;
int GetAge () ;

A SctAge () nem lehet konstans, me rt megváltozt3tja az iLslI.ge tagváltozót.


A GetA.ge () viszont már lehet konstans, és érdemes is annak választani, mert nem vál-
(oztat a7. osztályon ; egyszen1cn csak visszaadja az ItsAge tagváltoz6 aktuá lis értékél.
fgy ezen függvények deklarációja az alábbi módon tönénhet:
void SctAgc(int anAge) ;
int GetAge () const;

!la cgy függvényt k()nst~Hlsként deklarálunk, majd a függvény megval6sításába belekerül


valamely objektum (a;wz pékl[1U1 valamely mgválloz6 é rté ké nek) megváltoztatása, akkor
ezt a fordít6prognull hibiin:lk könyveli el. Ha példúul úgy írnánk meg a GetAge ( ) -et,
hogy tal1s.1 sZ:lmon, bogy il Cat húnyszor kérdezte le a korát ("' get age), akkor ez fordí-
t<'isi id6beli hibát gcner.i1na. Ez ugyanis változtaln:l a Co!It objektumon.

Ha csak lehet const


Erdem,,, konstanst használni, amikor csak lehet. Deklaráljuk konstansként a tagfügg-
vényeket, ha azok nem változtathatnak az objektumon. Ezáltal a fordftóprogram több
hibát szürhet ki, ami gyorsabb és olcsóbb, mint ha a programozó foglalkozna ezzel.

Jó programo:dsi gY:lko rl:lt, ha a le hc16 1egLÖhh met6dust ko nst:lnskénr clekl:lrfLljuk. Va-


la hányszor így teszünk, felh:l!:l lm:lzzuk a fordít6progra mol, hogy téveszt(:seinke l k or~n
fe lfedezze; még mielőtt azok futás i időbeli progmmhibá kká U:pnének elő. Ugyano lyan
okból érdemes konstans függvé nyeket használni, mint amil!rt konstans változ6k:lt.

Felület és megval6sftás
Ahogy korábban tanultuk, az ügyfelek a prognlm azon összetevői, amik létrehozzák és
használják osztályunk objektumait. Úgy is elkl!pzelhetjOk osztályunk felületét - azaz
:lZ osztály-deklar:'idót - mint egy s zerződést ezekke l az ügyfelekkel. A szerződés felso-
ro lj:l osztályunk felhasználható adatait, meghatárm:za osztá lyunk viselkedését.

A eat osztály uckla r:k i6jában például olyan ~ze rz6dést írtunk, amely szerint minden
macska (Cat) tudhatja és kinyerheti saját korát; valamint, hogy létrehozáskor iniciali-
zál ható ez a kor, és később beállítható vab'Y kinyerhető , és hogy minden macska tud
nyávogni: Meow () .

Ha a GetAge () -ct konstans függvényként hozzuk létre (ahob'Y azt érdemes), akkor
a sze rződés arm vonatkozóan is ígéretet tesz, hogya GetAge () nem fog változtatni
a macskán, ami meghívja
Tudta hogy... ? Miért használjuk hibakeresésre a fordítóprogramo!?
A programozás véres valósága azt mutatja, hogy egyikünk sem ír hibátlan kódot.
Nem az különbözteti meg a profi programozót a kezdőtől, hogy az előbbi nem vét hi-
bát; hanem, hogy ő még azelőtt megtalálja azokat, mielőtt tennékét vízre bocsátja.
A fordítás idejű hibák - melyek a programfordítás alatt deriilnek ki - sokkal jobbak
a futási idejű hibáknál, melyek a program futásakor ütköznek csak ki.
A fordítási idejű hibákat nem lehet nem megtalálni. Viszont számtalanszor le lehet
futtatni egy programot úgy, hogy annak vezérlési útja nem fedi le a teljes forráskó-
dot; így a futási idejű hibák meglehetősen sokáig rejtve maradhatnak. A fordítási
idejű hibák rögtön a fordításkor kiderülnek , így könnyű azonosítani és javítani őket.
Aminőségi programozásnak pontosan az a célja, hogy ne maradhassanak futási
idejű hibák a kódban. Ennek kipróbált és jól bevált módszere a fordítóprogram meg·
felelő kihasználása; segítségével a tévesztések még a fejlesztés korai fázisaiban fel·
térképezhetőek.

Természetesen elképzelhető, hogy programunk hibamentes, csak épp nem azt teszi,
amit várunk tőle. Ezért szükségünk van minöség-ellenőrző, tesztelő csapatra is.

Hová tegyük az osztály-deklarációt és a metódus-definfciókat?


Oszt:1lyunk minden egyes deklarált függvényéhez meg kell írnunk a megfelel6 definí-
ciót. Ezt más néven a függvény implementáci6jának, megvaI6sítás.in:lk hívjuk. Mint
minden más függvénynek , az Osztálymet6dus definíci6jának is van fej része és törzse.

A dcfiníci6n:lk olyan fájlhan kell lennie, melyet a fordítóprogmffi megtalál. A legtöbh


C++ fordít6 azt várja, hogy ennek a fáj lnak. c vagy. cpp kiterjesztése legyen. Köny-
vlinkben a . cpp-t fogjuk használni , de érdemes megnézni :I kedves olv:l."ó álL:!1 hasz-
núlt fordítóprogmm elvárásait.
Apropó A forráskód állományainak kiterjesztése
Sok fordítóprogram azt tételezi fel, hogya. c kiterjesztésű állományok C nyelvű prog·
ramot tartalmaznak, a . epp kiterjesztésűek pedig C+ + nyelvűt. Valójában használha·
tó bármelyik, de csökkenti a zűrzavart, ha . cpp·t használunk C++ programjainkhoz.

Ezekcll~ . cpp állományokat fel kell venni a projektfájlba v:lgy a make file-ba. Ennek
konkrét módja a használt fordítóprogramtól függ. H:I integrált fejlesztői környezetet hasz-
nál (TDE-ú, olyasféle menüpontot érdemes keresni, hogy jájlok hozzúadása a projekt-
hez". A progr..lmhoz tartozó minden .cpp állományt hozzá kell adni a projekthez, melyről
azt szeretnénk, hogy bekeriiijön (fordítás és linkelés után) a végső furratható fájlba.

Bár megvan a szabadságunk arra, hogy a deklarációt a . cpp forrásfájlba illesszük , ez


nem túl jó programozásbeli gyakorlat. A legtöbb progmmozó által elfogadott közmeg-
150 I II. rósz· Osztályok

egyezés szerint a deklarációkat fcjléc:íllományokba érdemes tenni, melyek hasonl6 ne-


vet viselne k, ("sak a kiterjesztésük . h j . hp vagy. hpp . Könyvünk ben. hpp-t fogunk e r-
re a etIm használni, de érdemes megnézni fordit6programunk beállításait.

A Cat osztály deklarációja például a cat . hpp állományba kerülhet, míg az osztályme-
tódusok deAnícióit a cat. hpp állományba. Úgy junathatom a fejlécillomány tartaimát
a cat . cpp állományba, hogy ennek el ső soraiba beírom:
llinclude · cat . hpp·

Ez arra utasítja a fordít6programot , hogy enn(;!k a sornak a hcly6re emelje be


a cat . hpp állo mányt; mintha csak erre a helyre le nne beírva annak tartalma. Na dc ak-
kor miért is kell elkülöníteni ('iket, ha a végén ögyis össze leszne k párosítva? Ügyfél-
progntmjaink:it a legtöbb cselben nem érdeklik a z egyes függvényck konkré t megval6-
sírás,ínak apró részletei. A fejlécállománybó l mindent megrudnak, amire szükségü k
van; felesk:ges lt:nne a megva lósítási fájl okkal foglalkozniuk.

Apropó Osztályok deklarálása


Egy osztály deklarációja mondja meg a fordítóprogramnak, hogy mi is ez az osztály,
milyen adatai és tagfüggvényei vannak. Az. osztály-deklarációt más néven az osz-
tály kezelófelületének (interfésll is hIvják, mivel ebból derül ki a felhasználó számá-
ra, hogy mit tehet az adott osztállyal. Ezt általában egy . hpp fájlban, azaz fejléc ál-
lományban tároljuk .
Egy függvény definíci6ja Irja le tészletesen, hogy hogyan muködik az adott függ-
vény. Ezt más néven az osztálymet6dus megvalósításának hívjuk, és egy . cpp fájl-
ban tároljuk. Az. osztály megvalósításának részletei csak az osztály megalkotójára
tartoznak. Az. osztály ügyfélprogramjainak - azaz a program azon részeinek, ame·
Iyek használják az adott osztályt - nem kell (és nem is érdemes) tudniuk, hogy ho-
gyan valósul meg egy-egy függvény.
Ennek a megközelltésnek az a fő előnye, hogy könnyen meg lehet osltani osztályo-
kat; azaz mások példányosíthat ják az általunk tervezett objektumokat, és mi is
használhatunk mások (munkatársaink vagy kereskedelmi eladók) állaliéirehozott
osztályokat.

Helyben kifejtett (inline) megval6sftás


Ahogy egy normál Függvény esetében is utasíthatjuk a fordít6progrnmor, ho1;,')' helyben
kifejtett fi.\ggvényként ford ícsa Ic azt, ugyanígy t e h ető helyben kifejtetté egy osztályme-
tódus is. Ehhez a visszatérési 6rték elé in inline kulcsszót haszn:'i lhatjuk.
A GetWeight () Függvény helyben kifejtett megva l6sítása például így n(:z ki:
inlinc int Cat : : GetWcight ()
{
rcturn itsWeight; II Vissza adj a az adattag súlyát (Wcight)
8. óra •

Úgy is helyben kifejlellé lehet6 egy függvény , hob'Y az osztály-dekJarációba írjuk a dc-
fmícióját. Például:
class Cat
{
publ i c :
int Get Weight ( ) const { return i t sWeight ; ) II i nline
void SetWeigh t(inL aWeight) ;

Figydjük meg a GetWeight () dd'lníciójának szintaxis:ít. A helyben kifejtett függvény


lörLSe rögtön az osztálymctódus de klarációja után kezd6dik, a7..az n incs pontosvessz6
a zárójelek után. Mint bármely más függvénynél, in is kapcsos zárojelek között szere-
pel a definíció. Ahogy már megszokhaltuk, a térköz karaklerek nem sz5mítanak, azaz
mind ez így is írhar6 :
class Cat
{
public :
int Getweight() const
{
return itsWeight ;
II inline
void Sctweight(int aWeight);

A 8 .1 és 8.2 Lista is létrehozza a eat osztályt; külön kerül a d eklarádó (cat . hpp) és
függvények megvalósítása (caL . cpp). A 8.1 Lisla II hozzáfér6 függvényeket és ti nyá-
:I
vogó (Meow (» függvényt is belyben kifejletté teszi.

8.1 Usta - A Ca. osztály daklaráció;a a cat.hpp fájlban


o: tinclude <iostrearn>

"2 ,
3,
class Cat

4: public ,
5: Cat (int initialAge) ;
6: -Ca t () ;
7, i nt Ge tAge() ( retu r n i t sAge ;) II inline !
s, void SetAge (int agc) { itsAge '=' age ; } II i nlinc !
9, void Meow() { std :: cout« "Meow. \n" ; } II inBne !
10, private :
11 : int itsAge;
12 , };

8.2 usta - A Cat osztály megvalósitása a cat.cpp fá~ban


o: /1 Helyben kifejtett (inIinc) faggvények bemutatása
l: /I és a fejlécállomány(ok) beemelése
2: hnc l ude 'caL hpp ' II Ne feledj ak a fejlécállomány beemel ését!
3,
152 111. rész' Osztályok

4: Cat : : Cat (int initialll.ge )


5.
6: it sAgc ~ initialAge;
7.
8.
9: Cal: : -Cal( )
10 : {
ll : }
12 :
13 : II Létrehoz egy [J\(Icskát (cat) ; beállítj a a korát ,
H : /I megnyávogtatja, lekérdczi a korát. majd új r a ny á vogtatja .
15 : int main{)
16 :
17 : Cat Frisky(S) ;
18 : Frisky . Meow() ;
19 : std : : cout « " Frisk y egy • ;
20 : std :: cout« Fris ky. Gct Age() « • é v es macs ka . \ n ' ;
2l: Fris ky .Meow();
22 : Frisky . sc t Agn (7);
23 : s t d : : OOIJl. « ' Frisky most. már'
24 : std : , cout « Frisky . GetAge() « éves . \n ";
25 : return O;
26 :

m
Mcow .
Frisky egy 5 éves ma c9ka .
Mcow.
Frisky most már 7 éves .

A GetAge () -el a R 1 Lisu 7. sorában dcklaráljukj in nonna! gondoskodunk a helyben


kifejtett megvalósltásról is. A 8. és 9. sorok további helyben kifejtett függvényeket ad-
nak meg; ezek megvalósítása ugyanaz marad, mint a korábbi .távolabb kifejtett" meg-
valósításokban .

A 8.2 Lista 2. soráhan a #i nclude "cat . hpp· beemeli a caL . hpp tartaImát (am i vi-
szont a cout számára szükséges iost r e am-et emelte be :lZ ;. sorban).

Osztályok beillesztése más osztályadattagjaiként


Nem ritka, hogy egy bonyolultabb osztályt úgy építünk fel , hogy egyszerűbb osztályo-
kat használunk fel h07.711; azaz deklarációikat beilleszrjük a bonyolultabb osztály dekla-
rációjába. Egy Autó osztály felépülhet például Kerék , Motor , Sebességvá l tó stb. osz-
tályoklxll. Ezeknek a deklarációknak rész jellegük van, azaz az autó részei a kerekek,
a motor és a sehességváltó.
8.

Vab'Y tekintsük a következő példát: minden téglalap szakaszokb61 áll. A szakaszokat két
\'égpontjával jellemezhetjük. A rOnloka! x és y koordinátáik halározz.'ik meg. A 8.3 Lsta
a Rectanglc ( Téglalap) osztály teljes deklarki6ját mutatja ( rect . hpp). A téglalapot
a négy csúcsát összekötő négy szakasz jeleníti meg; minden pont egy koordinátapárml
van megadva, ezért először a Point ( Polll) oSltályt kell megadnunk (mc1y :mnak x és y
koordinátájál tartalmazza). A 8.4 Lista mindkét osztály deklarficióját bemutatja.

8.3 Ulta - A rect.hpp egy teljes osztály deklarációját mutatja (fejléc).


o: II rect. hpp kezd6dik

,.
1: lincludc <iostrcam>

3: clöss Point /1 Tartalmazza az x,y koordinátákat


4: {
5: II nincs konstruktorl az alapértelmezés has~náland6
6: public :
7: void Setx ( int x) ( itsx '" x ; )
8: void SctY(int y) ( i t sY '" y ; )
9: int GetX()const { return itsX ; }
10: int GetY()const { return itsY;}
11: privllte:
12: int itsX;
13: int itsY;
14, I; II II • Point· osztály deklaráci6jának véoc
15,
16 :
17 : cll'HI~ Rectangl e
18 : {
19 : oubl ic :
7.0 : Rectanglc (int top . int left, int hottom . int right) ;
21 : -Rectungle (l (l
22 :
23 : int GetTop() const { rcturn itsTop; l
24 , int GetLeft() const ( return itsLeft;
25: int GetBottom() const ( return itsBottom;
26: int GetRight() const { return itsRight; }
27 :
28 : Point GetUpperLeft{) const ( return itsUppcrLcft ;
29 : Point GetLowerLe[t() const ( rcturn itsLowerLeft ;
30 : Po i nt GetUpperRi.ght () const ( r eturn itsUpperRight ;
31: Point GetLowerRight () const { return itsLowcrRight;
32 ,
33 : void SeLUpperLeft (Point Location) ;
34 : void SetLowerLeft(Point Location) ;
35: void SetUpperRighL(Point Location) ;
36 : void SetLowerRight(Point Location) ;
37 :
38 : void SetTüp{int top) ;
39 : void SetLeft (int left);
40 : void SetBo t tom (int hottom) ;
41: void SetRi gh t (int right) ;
42 :
154 111. rész • OSllályok

43 : int GetAn:!il () cons t;


44,
45 : private :
46: Point itsUpperLeft;
47: Point itsUpperRight;
48: Point itsLowerLeft;
49: Point itsLowerRight ;
50: int itsTop;
51 : int itsLeft ;
52 : int itsBottorn;
53 : int itsRight;
54 : J,
55: /I rect . hpp vége

8.4 Usta - A rect.epp egy bonyolultabb osztály megvalóslUidt mutatja.


o: II rect . epp kczd6dik

,.
1: #inc l udc Hrect .hpp·

3: Rectangle : : Rec tanglc(int Lop, int left, int bottom , int.. righ t)
4: (
5: itsTop .. top;
6: itsLeft - left ;
7: itsBottom z bottom ;
,.
8: itoRight '" right ;

10: itsUpperLeft , SetX(lcft) ;


ll : itsUpperLeft . SetY(top);
12 :
13: itsUpperRight.SetX(right) ;
14: itsUpperRight.SetY(top) ;
15 :
16 : itsLowerLeft , SetX(left) ;
17 : itsLowcrLeft , SeLY(bottom) ;
18 :
19: itsLowerRight.SctX(right) ;
20 : itsLowerRlght . SetY(bottom) ;
21 :
22 :
23 : void Rectangle : : SctUpperLeft(Poi nt Location)
24. : (
25 : itsUpperLeft = Locdtion;
26 : jtsUpper Right . Se tY (Location .Ge t Y() ;
27 : i tsLowerLcft .SetX(Location GetX( ») ;
29 : itsTop = Location.GetY( ) ;
29 : itsLeft = Location.GetX();
30 :
31 :
32 : void Rectangle : : SetLowerLe ft (Point Location)
33 :
34 : itsLowerLeft =' Location ;
35 : itsLowerRight . SetY(Location . GetY());
36 : itsUpperLeft . SetX(Locati on . GetX()) ;
8. óra • Az

37 : it.sBott.om = Location . GetY () ;


38 : itsLeft = Location . Gf'!t.X () ;
39 :
40 :
41 : void Rectangle : : SetLowerRi ght(Point Loca tion)
42 : {
43 : itsLowcrRight = Location ;
44 : itsLowerLeft . Set.Y(Loc~tion . GetY(» ;
45 : itsUpperRight.SelX{Locat ion .Ge t X(» ;
46: itsBottom::. Location . Ge t Y() ;
47 : itsRight • Location .GetX() ;
48 :
49 :
50: void Recta ngle : : SctUppcrRight{Point Loca t lon)
51 : (
52 : itsUppecRight = Loca t. ion;
53 : i t~Upper Lef t. SetY(Loca t ion . GetY{» ;
54 : i toLowe r Right . SctX(Location .GetX (»;
55 : i ts Top '" LOCtlt.ton . GetY() ;
56 : itsRight _ Location.Cc t X() ;
57 :
58 :
59 : void Rectnngle: : SetTop(int. LOp)
60: (
61 : itsTop • top ;
62: itsUppcrLeft.SetY{top);
63 : itsUpperRight.Set.Y(top) ;
64 :
65 :
66 : void Rectangle: : SetLeft(int 1eftl
67 : (
68 : itsLeft '"' left.;
69 : itsUpperLe[t.SetX(1eft);
70 : itsLowerLeft . SetX(left.) ;
71 :
12 :
7) : void Rectang1e :: Sct Bot tom (int bottom)
74 : (
75 : it~Bot t om m bottom ;

76 : itA Lowerl.eft . SetY(bottom) ;


77 : i ts LowcrRight . SetY(bot t om) ;
78 :
79 :
80 : void Rectü ny l e :: SetRi ght (in t rig h tl
81 :
82 : itsRight " right ;
83 : itsUppcrRight . SctX(right) ;
84 : itsLowerRlght.SetXCrlght) ;
85 :
86 :
87 : int Rectangle : : Get.Area () const
88 :
156 111. rész • OSZ1ályok

89: int Width


~ i tsRight - it sLeft;
90 : int Height '" itsTop - itsBottorn;
91 , return (Width · Height);
92 :
93 :
94 : II kiszámitjuk a téglalap teruletét a sarkok koordinátái alapján úgy,
95 : II hogy meghatározzuk a szélességét, magasságát , és Osszeszorozzuk
96: int main()
97 : (
98 : / I A helyi Rectangle változ6 inicializálása
99 : Rectangle MyRcctangle (100 , 20, 50, 80 ) ;
100 ,
101 : int Arca", MyRectangle.GetArea{);
102 :
1 03 : std :: cout« "Terület: • « Arca« "\n\";
104: std : : cout « "Bal fels6 pont x koordinátája: ";
1 05 : std : : cout « MyRectanglc.GetUppcrLeft() . Ge tX{);
106: retur n O;
107,

Tero.let: 3000
Bal fa!s6 pon t x koordinátája 20

A 8.3 Lista 3-14. sorában deklarálruk a Point osztályt, amely egy sokszög valamely
pontján:lk x és y koordinátáit hivatO!{ tárolni. A programban nemigen kerül el6
a Point osztály, de a mjzoló metódusok használják.

A Point osztály-deklaráci612. és 13. sorában kél tagvá ltoz6t deklarálunk (itsx-et és


itsy-t). Ezek hordozzák a koordináta-információkal. Az x koordináta növekedésekor
jobbra ll10zdulunk a koordinálasíkon, az y növekedésekor pedig felfelé. Más ábrázolá-
sok csetén ez másként is lehet, például egyes ablakkeze\6 programok esetén az y növe-
lésével lefelé mozdulunk a képernyőn.

A Point osztály deklaráci6jában (a 7-10 sorban) helyben kifejlett hozz,Mér6 függvények-


kel é~ük el és állítjuk be az x és y koordinátákat. A Point-ok az alapértelmezett konstruk-
tort és destmktort használják, azaz kénytelenek vagyunk beállítani a koordinátáikat.

A 17. sorban kezd6dik a Rectangle osztály deklarációja. A téglalapol a négy csúcsa


(négy pom) határozza meg.

A Rectangle konstruktora (20. sor) négy egész számot vár, melyek a következők:
top. left, bottornés right (azaz/ent, bal, lent és jobb). A konstruktornak átadott
négy paraméter négy tagváltozóba kerül, majd létrejön a négy Point.
A hozzáCér6 füg&'\'ényeke n kívül a Rectangle-nak van eb')' GetArea () nevű függvé-
nye is, melyet a 43. sorban deklarálunk. A terület változóban való tárolása helyett
a GetArea () kiszámít ja a területet a 8.4 Lista 89-91. sorában. Ehhez a téglalap szélessé-
gét és magasságát számolja ki, majd ezeket összeszorozza

A téglalap bal felső pontja x koordinátájának kinyeréséhez el kell érnünk az Upper -


Left pontot és ki kell nyernünk ennek x koordinátáját. Mivel a GetUpperLeft ()
a Rectangle saját metódusa, közvetlen hozzáférése van annak s."lját adataihoz, köztük
az itsUpperLeft-hez is. De mivel a itsUpperLeft egy Point , és a Point-ok itsX
értéke saját (plivát) változ6, a GetUpperLeft () nem tudja ezt közvetlenül elérni. Ehe-
lyett a nyilvános GetX () hozzáfer6 függvényt kell használnia az adat kinyeréséhez.

A 8.4 Lista 96. sorában kezdődik :t tenyleges programtörzs. A 99. sorig nem foglalunk
le helyet a mem6riában, és voltaképpen semJ11i nern történik. Pusztán aZI közöljük
a fordít6 prog~ m m a l , hogy hogya n kell létrehozni Point-ot és Rectangle-t, ha valaha
szükség lesz rá.

A 99. sorban viszont megadunk egy (vízszimes/függ6Ieges oldahlkkal rendelkező)


Rectangle-t annak felső, baloldali, alsó és jobboldali szélének koordináta-értékével
(top, left, bottom és right).

A 101. sorban létrehozunk egy Area ( /en"llel) new helyi egész változ61. Ez t.1rolja
imént létrehozott Rectangle területének értékét, mely a Rectangl e saját
:IZ
GetArea () függvényével inicializíi l6dik .

A Rectangle klie nsprogramja tehát anélkO! t\Jd létrehozni Rectangle objektumot (és
tudja lekérdezni a területét), hogy bármit is tudna a GetArea () megval6sításár61.

Puszdn a fejlédlllomá nyt látva (mely tartal mazza a Rectang1e osztály deklaráci6ját)
a programozó tudja, hogy a GetArea () egy egész számot ad vissza. Hogy ez konkré-
tan milyen bO"vészmutatvánnyal történ ik, az nem a Rectang1e oszt:'i ly fel használójának
a dolga. A Rectangle osztály progr-<lmozója anélkül is megváltozt.1 thatja a GetArea ()
múködését, hogy ez hacássallenne a Rectangle osztályt használ6 programokra .

Kérdések és válaszok
Kérdés: Ha a 1..'Ollslansfiiggvények osztiilymódosittisi kísérle/e hibajelzést adfordittisi
időbeli, m iért 'Icm érdemes egysze1l7en elhagyni a const módos;rószóf a hibajelzések
elkell"ilésére?

Válasz: Ha a szóban forgó tagfüggvé.nynek elvileg nem kell módosítania az osztályt,


akkor a const kulcsszó használata jó módszer a buta hibák felderítésére a fordítóprog-
158 111. rész ' Osztályok

mm rl!vén. A GetAge () -nek például semmi oka nincs arra, hogy változtasson a Cat
osztályadatain, ha azonban a megvalósítás programk6dja az alábbi sort tartalmazza:
if (it~Age _ 100) std::cout « "Hé! Már 100 éves vagy?!\n";

akkor a konstansként deklarált GetAge () hibát dob a fordításkor. A prog.... moz6 felté-
telezhet6cn azt szerette volna ellenőrizn i , hogy az itsAge egyen16-e százzal, de ehe-
lyett véletlenül értékül adta a százat az itsAge-nek. Mivel ez az é rtékadás módosítani
szerelnI! az osztály adatait, hololt korábban azt ígértük, hogy nincs ilyen szándékunk,
a fordít6progr-.tm hibát jelez.

Ezt a fajta hibát e lég nehéz pusztán végignézéssel megtaJálni - a szem gyakran azt lát-
ja, amit szeretne. A U:nycg: a program látszólag normálisan fog futni , de az it sAge ér-
téke egy hibás szfl mértékrc lesz átállítva. Ez előbb-utóbb gondot fog okozni.

Kérdés: VCI II -C! él1C!l lIIc struct slmM/I.í rá/ hasz /lálni C+ + progl"alllban?

VálCIsz: Több C++ programozó csak olyan osztályok esetében használja a struct
kulcsszót, amelynek nincsenek függvényei. Ezzel a régi idők C sLnlktúráihoz nyúl nak
vissza, amelyek nem tartalmnzhattak függvényeket. 6szint(:l' szólva czt nem tanom jó
programozási gyakorlatnak. Ami ma egy metódus nélküli struklÚI'".!, az a jövőben igé-
nyelhet metódusokat. Emiau áL kell majd írni a struktúrát osztályl'".! , v!l.gy metódusokat
is tartalmazó struct struktlmlt használva fel kelllÚgni a követeU szabályt.

Gyakorlatok
Ebben 32 6r~iban további ismereteket szereztünk az osztályokr61. Tudásu nk elmélyíté-
$éhez válaszoljunk meg néhány kérdést és végezzünk el néhány gyakorlatot!
Kviz
1. Miért szokás az osztály-deklaráci6kat külön fájlban e lhelyezni?
2. Mi a szerepe a ca t . c pp-bcn szereplő konstruktornak?
3. Mi történne a rect . cpp-ban és a rect . hpp-ban, ha a Point osztályt nem def!-
nHllnánk?
4. Honn:ln tudja a fordítóprogram, hogy hol keresse a beemc!end6 állományokat?

FeladatoK
1. M6dosítsuk úgy a rect. hpp programot, hogya Point osztály deklarációja ke-
rüljön át egy másik állományba, és ezt az új állom.1nyt emelje be a rect . hpp-be.
Változik az eredmény?
2. M6closícsuk úgy a cat . hpp progmmol, hogy a GetAge () legyen konstans metó-
dus. Kell még valami máson is változtalni?
3. Módosírsuk úgy a cat. cpp progr<lmot, hogy jöjjön létre egy másik macska is,
(eat; például legyen a neve Szerénl. .e). Rá tudja venni SzemI/MI, hogy legyen
idősebb és nyávogjon egyet?

Válaszok a kvflk-érdésekre
1. Hogy könnyen megoszthatóak legyenek másokkal (és mi is használhassuk má-
sok objektumait).
2. Inicializálja az itsAge változót, arra az értékre állítva be, amit az objektum pél-
dányosításakor megadnak. Ez.í ltal mcgspóroljuk, hogy az objektum deklarálása
után még külön meg kelljen hívni a SetAge () -el az életkor bcállításám.
3. Ha ól Point oszt.ályt sehol máshol sem adjuk meg, akkor a fordítóprogram hibát
jelez meghatározatlan hivatkozás miatt. Ha valahol máshol került deflni:ílásrn,
akkor a megfelelő fájlt be kell emelni (pl. po i nt . hpp-ként). Gyakori megoldás,
hogy egy osztály egy mCtsikra t{11l1aszkodik, abból építkezik.
4. Ha abeemelendő állom{UlY neve idézőjelek (") közt áll, akkor a fordítóp rogram
ugyanabban a könyvt:1rban (ogj;t keresni, mint ahol az aktuális forr.'Ísfájl is van.
Azt is közölni lehet a fordítóprogrd1llmal, ha más könyvtárakban kell keresnie
az állományt: idézőjelek közt megadva az elérési utat, csak oU keresi a fájlt a for-
dítóprogram. Ha azonban az állomány neve kacsacsőrök (o) között szerepel,
akkor a szabványos rendszer-fejléckönyvtárban tönénik a keresés.
III. RÉSZ

Memóriakezelés
9. óra Mutatók
10. óra A mutatók kifinomult használata
11 . óra Hivatkozások
12. óra A hivatkozásokról és mufatókról - mélyebben
9. ÓRA

Mutatók
Mirőll.sz sz6 ebben az 6rában:
• Mik :'Izok a mutatók
• Hogyan vezethetjük be és hogyan használhat juk a mutató kat
• Mi a dinamikus memória (heap), és hogyan kell műve l eteket végezni ezen
a mem6riaterOlctcn

A mutatók és használatuk megértése


Egy C++ programozó számám a létez6 1eghatékonyabb eszköz a mutató, amdy a me-
mória közvetlen clérésének lehel6ségét biztosítja. Ugyanakkor a mutat6kr6\ azt is tart~
ják, hogy a programot leginkább összekuszá16 elemek. Épp ezért mlgyon fontos , hogy
megért,-;ük . mik is valójában a mutatók.

Ebben :lZ órában a mutatók működéséróllesz szó. Ám ne feledjük, hogy csak a könyv-
ben továbbhaladva fogjuk teljesen megéneni a mutatók szükségességét.

A lilII/alÓ egy olyan váltózó, amely memóriacímet tartalmaz.


164 1111. rész· Mem6riakezelés

Álljunk is meg, és olvassuk el újra. A mutatá egy változó. Azt már tudjuk, hogy mi
az a válLozó: olyan nyelvi elem, amelyben értékeket tárolhatunk. Egy egész változó
számokat tárol, egy karakter lípusú változó em' belŰL tanalmazhat A mutató pedig
olyan vállozó, amely memóriacímettárol.

Rendben , de mi is az a memóriacím? Hogy eZl megértsük, ismernünk kell valamennyi-


re a sz..ímítógép mem6riájának múködését. Ne ijedjünk meg, annyira nem bonyolult.

A számítógép mem6riája az a hely, ahol - többek közötl- a változók tárolódnak.


A mem6ria sorszámozott mem6riacellákra van bontva , a memóriacímek pedig ezek
a bizonyos sorszámok.

Mindegyik változó, legyen az bármilyen típusú, egy egyedi címen tárol6dik a memóriá-
b<1!l . A 9.1. ábrán a theAge nevU előjel nélküli hosszú egész vállOZÓ tárolásának módja
láth<1!ó vázlat.os ábrázolásban.

Memória

A theAge változó
, I
I 10110101 01110110 11110110 11101110

I I I I I
100 101 102 103 104 105 106 107 108 109 110 111 112 113

Minden memóriacella 1 bájt hosszú


A theAge nevű el6jel nélküli hosszú egész 4 bájt, azaz 32 bit hosszú
A theAge változónév az elsó bájtra mutat.
A theAge memóriaclme: 102

9.1. lb..
" tf/eAge uáltoz6 flbrt1zolt'i:;a /láz/a/osall

A"L egyes számítógépek e l térő de általában elég összetett sémák S"Lcrint címezik a me-
móriár. A programozóknak általában nem kell ismerniük a ponlos címét minden egyes
változómik, mivel ezeket a részleteket a fordítóprogram kezeli. Ha szükségünk van er-
re az :.datra, :tkkor használhat juk a címe (address of) operátorl (&), amelynek múködé-
sél a 9.1 Usta mul:ttja bc.
9.1 Usta - A vátlozók memóriacimének lekérdezése (addressdemo.cpp)
o: II A 9 . 1 . k6drészlet az address of (cim) operá t orL
1 : II lokális változók esctóbcn
2 : # i n c lude <iostre am>
3,
<I : int main()
5: {
6, unsigned short shortVar=5 ;
7, unsigncd long longVar~65535 ;
8, long sVar = -65535;
9,
10 , std , : cout « "shortVar : \t " « shortVar ;
11 : std : : cou t « "\tA shoctVar memóriacime : \t" « &shortVac « " \n" ;
12 : !;td : : c:out « "longVar : \t" « longVar ;
13 : std , , cou t « "\tA lo ngVar memóriacíme : \t' « &longVar « '\n ' ;
1<1 : ctd : : c:out « 'sVar : \t\t' « sVar ;
15 : sto : : cout « ' \tA sVar mcmóriacíme : \ t " « &sVar « " \n ";
16 :
17 : return O;
18 :

Apropó Különleges karakterek a C++ nyelvben


A \ t a 9.1. kódrészletben egy TAB karaktert illeszt a kimenetbe. Ily módon egysze-
rűen készfthetünk oszlopokat (egyszeru ugyan, de messze nem tökéletes).
A \n új sorra viszi a kurzort (ugvanazt eredményezi. mint az std : : endl függ-
vény)
A \ \-t akkor használhatjuk, ha \ jelet szeretnénk kifrni (karaktersorozatokban)
A \ " karakterekkel id ézőjelet [rhatunk ki (karaktersorozatokban)
A \ ' karakterekkel egyes idézőjeleket frhatunk ki (karaktersorozatokban, vagy ka-
rakterként)
A fenti összefüggésben használva a \ (backslash) karaktert escape karaktemek ne-
vezzük, mivel megváltoztatja az öt követö karakter jelentését, s nem nyomtatható
karakterként viselkedik (a \n új sort eredményez. és nem egy n betűt).

menet
short Var : 5 A shortVar memóriacimc : 12 45066
longVar : 65535 A longVar mcffiÓrlacíme : 12 4506 0
~Var : -65535 A sVar mem6riacíme : 1245 056

Saját számítógépünkön másként nézhet ki a kimenet, lIlivel minden futás során más cí-
meken jönnek létre a változók, au61 függ6en , hogy mi egyéb található éppen a memó-
riában, és mennyi szabad hely áll rendelkezésre. A kimenet így is kinézhet:
shortVar : 5 ll. shortVar mcmóriacime : Ox8fc9 : fff4
lo ngVar: 65535 A longVar mem6riacíme : Ox8fc9 : ff:f2
sVar: -6553 5 A sVar memóriacimc : Ox8fc9: ff 00
166 1111. rész • Memóriakezelés

Három változót vezettünk be, majd kezdóértéket adtunk nekik. Egy short típusüt a 6.
sorban, egy unsigned long típusút a 7.-ben, és egy long típusút a 8.-ban. Az értéküket
és a címüket a 10. l!s 15. sorok között íraljuk ki, utóbbit a cím ( &) operálor segítségéve!.

A shortVar változó értéke a vártnak megfelel6en 5, mem6riacíme 1245066 volt


az Intel x86 alapú sz.-'inútógépcmen futtatva , Borland fordítóval készített biná riS5."l!.
Ez a nem túl egyszeru cím számítógépfügg6, és minden futás során változhat kicsit.
Ugyanakkor soha nem vállozik az, hob'Y a két első változó címe közötti különbség
négy bájt, amennyiben a számítógép néh'Y bájt hosszú cgészckct használ. A 9.2. ábra
szemlélteti, hogya fenti program váltOl:ói hogyan tárolódnak il memóriában. (Ne feled-
jük, hogy néhány számítógépen mindkét vállozó között négy bájt lesz a kü lönbség, at-
l6l függ6en, hogy il fordít6 nkal hogyan MJítottuk be.)

Egyel6re a dolognak nem !'iok értelme látszik, elvégre minek nekünk tudni :~ z egyes
változók memóriacímének aktuális értékét. Ebben a pillanatban tehát elég annyit meg-
jegyezn i, hogy mindegyiknek van címe, és hogy mindegyikhez a megfelelő mem6ria-
mennyiség van hozzárendelve.

T ".
I

00000"10'

~ 000000000000_ " "1111'"'"11'ooo_ 00000000" ""1111111111

9.2. ábra
A Ilálfozók fáro/ásának lJcnluwfásC/

Honnan rudja a fordító, hogy mennyi memóriát kell rendelni az egyes válloz6khoz?
Nos, ezt mi magunk adjuk meg neki, amikor deklaráljuk a típusukal.

l Ia például unsigned long lípusú változ6t vezetünk be, a fordító rudja , hogy négy bájt
memóriát kelllefoglalnia, mivel minden egyes unsigned long négy bájton tárolódik.
A fordító dolga a megfelel 6 teliilet hozzárende1ése.
9. 6ra • Mutatók 167

Amikor egy mulató létrejön, :l fordító a hardverfelépítéstól és operációs rendszertől füg-


g6en annyi memóriát rendel hOZ7..á, hogy dférjen benne egy változó mem6riacíme. Egy
mutató mérete nem feltétlenül akkora, mim egy egész tipusú változóé. Semmiképp se
vonjunk le következtetéseket a vá ltozótípus alapján a muml6 méretére vonatkozóan.

A memónacfm tárolása a mutatókban


Minden változónak van címe. És még ha nem is ismerjük czl pontosan, akkor is elhe-
lyezheljük egy mut:Cltóban.

Vegyünk például egy howOld nevú egész típusú változ6t. Ennek a cim(:t eltárolhatjuk
a pA'iJe nevú mutatóban, amit a következőképpen deklarálhatunk
i nt *DAge = NULL

Ez egy pAge néva int típustl változ6t címz6 mutOlt61 vezet be, azaz a pAge meghatáro-
zás.'l szerint egy int eimét fogja t.írolni.

Ne feledjük, hogya pAge ugyar'olyan v[lltoz6, mint a többi. Amikor bevezetünk egy
t::gész (int típusú) v5 Jtozót, akkor:lZ egy egész érték tároJásiira lesz [elkészítve. Ami-
kor egy, a pAge-hez hasonló mut;lIót készítünk, az egy mem6riacím tárolására lesz fel-
készítve. A mutató egy különleges típusú változó, amely a memóriában talá lható ele-
mek címének tárolására alk<l lmas. Jele n esetben a pAge egy egC:sz típusú változó me-
móriacímét tárolja .

Fontos, hogy lehct6ség szerint olyan típusú mutatót kell létrehoznunk, <Imilyen
az a vá ltozó, amelyet címez. Ez hmározza meg ugyanis a fordító számára, hogy hogyan
kezdje azt memóri:llerületcl, amire a mUlató murat. A mutató maga csak a mcmóriací-
mer tartalmazz<I, semmi egyebet.

A fenti példában a pAge változónak NULL kezd6értékel admnk. A NULL érlC:kkel ren-
delkcz6 muutót 1I/lllmulalóll(lk (null pointer) hívjuk. Minden mlltatónak, amit létreho-
zunk, kezd6értéket kell adnunk. Ha nem nldjuk mit akarunk hozzárendel ni, akkor
NULL é rtékel adjunk neki. A kezdóértékkel nem rendelkcz6 mutatókat szoklls {)C/d IIIU -
la/ólmak (wild pointer) nevezni. Ezek igen veszélyes jószágok.

íme II mulatók kezd6értékkel egyOn történ6 bevezetésének egy másik módja:


int ·pAge " O

Ennek eredménye ugyanaz kelj legyen, mint ha NULL ércékel adtunk volna neki, de
technikailag a O egy egész típusú állandó, a NULL pedig ennek az állandónak a memó-
riacímél lartalmazz3.
168 1111. rész · Mem6riakezelés

Apropó Kezdóértéket a mutatóknak!


Programozzunk biztonságosan, adjuk kezdóértéket a mutatóknak!

Ha a mutat6t O vagy NULL kezdőértékkellátjuk el, külön hozz,'! kell rendelni a howOld
eimét a pAge mutatóhoz. A következő példa ennek mikéntjét mutatja.
int hoWOld = 50 ; II változó létrehozása
int .. pAge", O; II mutató létrehozása
pAge ol &howOld; II a howOld címének h07.zárendelése a pAge-hez

Az els6 sor létrehozza a howOld nevG változ6t, ::unelynek típusa unsigned short int,
és az 50 kezd6értékel rendeli hozzá. A második SOr hozza létre a pAge nevű mUlatót,
;l!ncly szintén unsigned short lnt típusú , (:s előszö r O kczd6értékkel rendelkezik.
A pAge mutató voltát a v,lItoz6 típusa és a változó neve közé irt· k,lraktcr jelzi.

A harmadik és egyben utols6 sorban hozzá rendeljük a howOld memóriacímét a pAge


neVli rnut<1tÓhoz. A hozzá rendelést az address of (&) operátor teszi l e hetővé.
H<1 az address of open~tort kife!ejtenénk, a howOld mcmóriacímc helyctt annak érté-
két rendelnénk hozzá a nmtat6hoz. Ez az érték vagy valós, vagy egy nem lét ező me-
m6riacím, de még ha valós is, az teljesen a véletlen mGve, és biztos, hogy nem
az a hely, ahol b,1rmifélc műveletet szerelnénk végezni azzal a mUl,Hóval.

Apropó Mutatók és nem mutatók


Nem mutató értékét rendelni egy mutatóhoz viszonylag gyakori hiba. Annyira az,
hogya fordítónak ezt elvileg jeleznie is kell fo rdítás közben.

A pAge vá ltoz6 értéke jelenleg a hoWOld változó memóriadme, magának howOld-nak


az értéke pedig 50. Ezt az állapotot az alábbi pár lépésben is megvalósíthattuk volna:
unsigned short int howOld 50; /1 változó készitése
unsigned shor.t int • pAge = &howOld ; 1/ a howOld ra irányitott mutató
II bevezetése

A pAge tehát egy olyan mutató, amely a hoWOld változó mcm6riacímét tarta lmazza.
A pAge ha sznábHával kiolvashatjuk a howOld értékt:L, vagyis azt a bizonyos 50-ct.
A howOld változ6nak a pAge mutatón keresztültörtt:nó elért:s(!t k6ZveICII elérésnek
(indirection) nevezzük, mivel nem közvetlenül (a nevén át) f(!rünk hozzá a howOld-
hOL, hanem a pAge-en vagyis a memóriacímén keresztül. K ésőbb, de még ebben a lec+
kében látni fogjuk , hogyan alkalmazzuk a közvetett elérést egy változó értékének kiol-
vasásához.

A közvetett elérés azt jelenti, hogy a mutatóban található mem6riacímen e l é rhető érté-
ket olvassu k ki. A mutató tehát röviden e.gy közvetett módsLert ad az általa táro lt ci-
men található érték megszer.lésére.
9. óra • Mutatók 169

Mutatónevek
A mutató kkal kapcsolatban ugyanazok az elnevezések érvényesek, mint a változók
esetében. Könyvünk azl a konvendót követi, hogy minden egyes mutat6t p octave!
kezdünk, mint például a pAge és a pNumher mu lalókal ta rtalmazó példá kban.

A közvetett elérés operátor, avagy mutatók közvetett használata


A közvetett elérés open'iton ( *) másként v;ssztlh iulItkozús(dereference) operálornak is
nevezzük. Amikor egy ffiutatóval visszahivalkozunk, az általa hiva tkozott rnern6rbd-
men található énéket olvassuk ki.
unsigned short int howOld '" 50 ;
unsigncd short int yourAge;
yourl\ge '" h(')wo l d ;

A muta16 közvetett dér(:st b iztosít aLOn vá ltozó é rté kéhez, a rndynek mem6riacimére
mut:lt. A hoWOld változó é nl:kének {ttadása a yourAge változónak a pAge mu lató köz-
bei ktatásával például az alábbi módon tön énhet:
unsigned short int howOld 50;
II howOld bevezetése
unsigned short int ~ pAge &howOld ; II a howOld-ra mutató pAge
K

I I bevezetése
unsigned short int yourAge; II egy újabb változó bevezetése
yourA",e = ~pAge; II a pAge által mutatott érték (50)
II hozzárendelése u yourAge v61tozÓhoz .

A közvetett elérés oper.'itor (*) a pAge neV\l mUlató előtt valami ilyesmit jelem: .:I Z
ezen a címen tá rolt éJték ~. A hozz.árcndclés tehát a követ kezőt mondja: .VegYÜk
a pAge mutatóban találhat6 mem6riacímcn tárolt értéket, és rendeljük hOZ2.1
:1 yourAge n evű váhozóhoz. ~ Egy mfLsik megközelítésben a dolog a következ6képp
néz ki : ~Ne foglalkozzunk a mutatóval, fogla lkozzu nk a mUlató áltHl hiv:Hkozott címen
la lálható e l em mel. ~

Apropó Közvetett eléréssel kapcsolatos műveletek


A közvetett elérés operátort (*) két esetben használjuk: változók deklarálásánál és
hivatkozásnál. Amikor egy mutatót készítünk, a csillag jelzi, hogy ez egy mutató
lesz, és nem közönséges változó. Például:
unsigned short * pAge = NU LL ; II mutat6 unsigned short típusú
I I változ6hoz
Amikor a mutatón keresztül visszahivatkozunk, a közvetett elérés operátora azt jelzi,
hogy a mutatóban tárolt memóriacímen található értékhez szeretnénk hozzáférni, és
nem a memóriacímhez magához.
·pAge = 5 ; II az 5 érték hozzárendelése a pAge
II memóriaterületóhez.
Ne feledjük továbbá, hogy ugyanezt a karaktert (*) hasmáljuk a szorzáshoz is. Afor-
dító a kódkömyezetb61 állapítja meg, hogy éppen melyik felhasmálási esetről van szó.
170 1111. IÓSZ • Memón.koz.lés

A közvetett elérést a mindennapi életben is használjuk. l ia a helyi pizzériát hívjuk,


hogy vacsorát rendeljünk, de nem tudjuk a telefonszámát, megnézzük a telefonkönyv-
ben, vagy a wehen. Az információforrás nem a pizzérifira magára vonatkozik, de tartal-
mazza 3IU1ak "eimét" Üelt:fonszámáO. Amikor ezt tesszük, valÓjában közvetett elérést
val6sítunk meg,

Mutatók, címek és változók


Nagyon fontos, hogy különbséget tegyünk a mUlatÓ, és a mUlató által tárolt mem6ria-
cím között. Ez a forrása a legtöbb összcvisszaságnak a mutat6kkal kapcsolatban.

Nézzük a következő kódtőredéket:

int theVariable : 5;
int * pPointer = &theVariable

A theVariable nevű váltOZÓI egész típusúkénl határoztuk meg, és az 5 kezd6értéket


rendeltük hozzá. A pPoinler-t egy egész vMlozóra mulató hivatkozásként dekbrál-
tuk, kezdóértt:!kként a thevariable változó Illernóriadmét kapta. Tehát a pPointer-
ben tá rolt melllóriadm megegyezik a theVariable változó címével. A pPointer által
hivatkozott Illelllóriacílllt::n az 5 értéket tároljuk. A 9.3. ábra a theVariable és
a pPointer változókat szemlélteti.

A.datok manipulálása a mutatók használatával


Miután a mulatóhoz hozzárendeltük egy változó mern6riadmér, a mutal6n keresztül
hozzáférhetünk a változó értékéhez. A 9.2. Lista bemutatja, hogyan rendelhet jük eh'Y
lokális változó eimét egy mUl3t6hoz, és hogyan válloltathatjuk meg a változó é rtékét
a mulatón keresztül.

IheVariable pPointer

Változó név l, j , ,
0000 0000 0000 0000 0000 0110
0000 0101 0000 0000 0000 0101
'------v----' '-----y-----'
; I 1~1
H"
'" '02 'Ol 104 105
". '" '08 '09

Címkiosztás

9.3. ábra
A mell/ória l!áz(atos áhrázolása
9. óra • Mutatók 171

9,2 Um - Adatváltottatál mutat6k használatával {pointeruse.cpp)


o: II 9 . 2 . Li s ta - Mutatók h as ználata
1 : linclude <lostream>

,.
2 : using s t d: : cout ; /1 a z std : : cout k önyvtár haszná lata

4 : int main()
5: (
6: i nt myAge ; II e gy v á l to zó

,.
7:

,.
i n t " pAge .. NULL ; / I e gy mu tató

myAge ~ 5,
10 : pAge ~ &myAge; /I a myAge címének hozzá rendelése a pAge-hez
11 : cout « 'myAge : « myAge « ' \n " ;
12 : cout « • ~pA9'c : « "pAge « ' \o\n" ;
1) :
14 : cout « "'pi\(Je • 7 \ 0" ;
15 : " p Age = 7 ; II JI. 7 ért ék h02zá rcn d e l ése a myAge- hc z
16 : cout « '''pAge : « " pAge« " \n" ,
17 : cou t « ' myAge : «myAgC« ' \n\n ";
18 :
19 , cout« 'myAge = 9\n ';
20 , myAge = 9;
21 : cout « 'myAge : « myAge « " \n ';
22 : CDu t « • · pAge : « " pAge « ' \n ";
23 :
24 : ret urn O;
25 :

myAge : 5
" pAge : 5

· pAge = 7
. pAge : 7
myAge : 7

myA<;le : 9
myAgc : 9
" pAge : 9

A fcnti program két vállOZ6l vezet be: egy myAge nevú egészet és egy pAge nevú muta-
t6t, amely egy int típusú változóra hivatkozhat, és amely a myAge címét tarta lmazza.
A myAge a 9. sorban az 5 értéket kapja, ezt ellenőrizzük a ll. sorban történ ő kiíratt\ssal.

A 10. sorban a pAge-hez rendeljük a myAge címét. A 12. sorban visszahivutk07.unk


a pAge-re, és kiíratjuk, bemutatva, hogy a címén található érték val6lr.m 5, amelyet
'72 1111. rész' Mtmóriakezelés
<lmyAge változóban tárolunk. A 15. sorban il 7 é rtéket rendeljük a pAge memóriacímén
található memóriaterülethez. Ez a myAge változ6 é rtékér is 7-re változtatja, amelyet
a 16. és 17. sorban található kiír6 művelctekkel igazolunk.

A 20 sorban, a 9 értéket re ndeljük a myAge változ6hoz. Ezt az énéket aztán kiolvassuk


közvetlenül a 21. sorl:Ydn, és közverYc - visszahivatkozva a pAge mutat6ra - a 22. sorban.

A mutatókban tárolt cfmek vizsgálata


A mutatók lehctővé teszik a mem6riacímekkel történő mlíveleteket anélkül , hogy pon-
tosan mdnánk mi is van a mutatókban. Ha végigolvasluk ezt a leckét, életük hátralev6
részében vakon bízhalunk abban, hogy amikor egy változó mem6riacímét hozzáren-
deljük egy mut.1t6hoz, a kkor a mutatóban valóban annak a vált.oz6nak a címe szere-
pd. Egyszer az életben :Limnban bizonyosodjunk meg róla, hogy ez tényleg így van,
A 9.3 Lista éppen ebben segít ne künk.

9.3 Lista - Mi il van egy 1IMIlIt6ban... (pointemore.


o: / / 9.3 Lista: Mi ls van egy mutatóban . .
1: ftinclude <iostream>
2,
3: int main ()
4: {
5, unsigned short int myAgc : 5, yourAge la;
6, unsigned short int * pAge • 'myAge; / / a mutató
7.
8: std : : cout « 'myAge : \t" « myAge;
9: std :: cout« '\t\tyourAge : \t' «yourAge« " \n ";
10 : std : : cout « '&myAge:\t ' « &myAge ;
ll : std : : cout « " \t&you r Agc : \t' « &yourAgc «'\n ',
12 ;
13 : std :; cout« 'pAge:\t' «pAge « ' \n';
14: std :: cout« '''pAge : \t"« "pAge« '\n\n',
15:
16: pAge = &yourAge;
17 :
18 : std : : cout « 'myAge : \ t ' « myAge ;
19 : std : : cout « '\ t\tyourAqe : \t ' « yourAge « "\n" ;
20 : std : : cout « 'ócmyAge ; \t' « &myAge ;
21: std : : cout « '\t&yourAge: \t' « ócyourAgc «'\n';
22 :
23 : std :: cout « 'pAge : \t' « pAge « '\n';
24 : std " cout « ""pAge : \t " « "pAge « " \n\n';
25:
26 : std :: cout« 'ócpAgc : \t ' «ócpAge« ' \n' ;
27: re t urn O;
28 :
9. 6,. • Mutat6k 173

Kimenet
myAge: 5 you rAge : 10
&myAge : 12 45066 &yourAge : 124506 4
p Age : 12(\5066
*pAgc : 5
myAge: 5 yourAge : 10
&myAge : 12 45066 &yourAgc : 124506 4
pAge : 1245064
· pAge: 10

&pAge : 1215060

Ne feledjük, hogy saját rendo;zerükön 'L kimenet ismét eltf:rhet az itt látható eredmény-
t61, mivel minden számítógép killö!1böz6 címeket tárolja a különböz6 változókat, attól
függ6en, hogy mi van még a memóriában, és mennyi memória áll rendelkezésre. A ki-
menet a következ6képpcn is kinézhet:
myAge : 5 you r Age : 10
&myAgc : Ox355C &yourAge : OxJ55 E
pAge : Ox 355C
~ pAge : 5

myAgc : 5 yourAge : 10
&myAge : Ox ]S5C &yourAge : Ox355E
pAge : Ox 355E
· pAge : 10

&pAge : Ox355A

Az 5. sorban a myAge és yourAge változókat unsigned short egész típusúkénl vezel-


tük be. A 6. sorban 3. pAge mUlat6t egy unsigned short egész típusü cím tárolásához
deklarálruk, és kezd6értékként a rnyAge változó címét adtuk meg neki.

A 8.-11. sorokban a myAge és your Age változók értékét és dmét lt.1ltuk ki. A 13. sor-
ban íratruk ki a pAge tart:l lmát, amely a myAge címe. A 14. sorban aztán a pAge-re tör-
ténő vissza hivatkozás eredményét ki.i1dtük a kimenetre, amely a pAge címén ral:'i1hat6
értéket, a myAge értékét, azaz 5-öt adott eredményül.

Ez a mutatók lényege. A 13. sor mutatja, hogy a pAge a rnyAge változó címét tart·ahnaz-
za , majd a 14. sor mutatja , hogy hogyan érhetjük el a myAge értékér a pAge mut.116m
történő visszahivatkozással. Fontos, hogy megénsük eZl, mielőlllovább folYlalnánk.
Tanulmányozzuk a kódot, és nézzük át újra a kimenetet.
174 1111. rész • Memóriakemlés

A 16. sorban a pAge-bez ezúttal a yourAge változó címét rendeltük, majd újra kiíratnlk
az értékeket és a címeket. A kimenet azt mutatja, hogya pAge most a yaurAge változó
címét tartalmazza, és a visszahivatkozás során a yourAge értékét kaptuk.

A 26. sor a pAge sa ját címét írja ki. Mint az összes többi változónak, ennek is van címe,
amely szintén e1t,í ra lható egy másik mutatÓhan . (A mutató címének másik mutat6hoz
törté n ő hozzá rendelésére hama rosan részletesen is kitérilnk.)

Használjuk a közvetett elérés operá- Ne pr6bálkozzunk memóriadm táro-


lort (*) a mutat6ban lévő címen tárolt lásával olyan változókban, amelyek
6rték eléréséhez. nem lllutatÓk.
Minden mutat6nak adjunk val6s kez-
dóértékct, vagy null-t (O).
Ne feledjük II különbséget a mutató-
ban lévő cím, és :lZ azon a címen lé-
vő érték között.

Miért jó mutatókat használni?


Most tehát már pontosan tudjuk, hogy hozh:Hunk létre muta tót, és hogyan helyezhet-
jük cl benne egy változ6 címét. Most már csak az H gond , hogy magunktó l soha n ~m
jut.na eszünkben ilyesmit csiná lni. Végül is mi a csud5nak bajlódjunk mUlalókkai , ami-
kor megvan a változónk, amelyen keresztül hozzáférhetünk a memóriában tároli érték-
hez? Nos, az eddig bemutatott ~mutat6s ada tváltozt;ltások~ basználau{nak egyetlen ér-
telme tényleg cS:lk a mutatók működésének a bemumtása.

llaneIllmOSl, hogy már ismeros számunkra a mutatók használata , valami hasznosat is


kezdhetünk velük. A mutatókat le&!,'}'akr.lbban lZ alábbi három feladatr" használjuk:

• A dinamikus mem6ria (heap) kezelésére


• Osztálypéldányokban Cobjektumokban) tárolt vá ltozók és tagfi.i8bTV~nycik c1ért!sére
• Vá ltozók fü ggvényeknek történ6 refercnciakénti átadásához

A fejezet hátralévő részében a dinamikus memóriában ( heap) tönénő adatkezelést és


az oszt.álypéldányok adatainak és tagfüggvényeinek elérését mutatjuk be. A 11. fejezet-
ben foglalkozunk m:ljd a változók hivatkozással törté nő átadásával.
9, óra • Mutatók 175

A verem (stack) és a dinamikus memória (heap)


A programozók áiLa[ában az alábbi öt mcm6riatcruleten végeznek ml1veleteket:

• Glob{ilLo; névtér
• A dimmikus memória (heap)
• Regiszterek
• Kódlér
• A verem

A lokális változók él függvényparaméterekkel egyült veremteriiklen vannak, A kód rer~


mészetesen a k6dtérben található, a glob,ílis változók pedig n globális névtérben. A re~
gisztereket használjuk az olyan bels6 álbpotkczeló funkciókhoz mint például a verem~
kezelés, de ill ta lálható például az ulasí1.ásszáml:íl6 is. Majdnem az összes fen nmaradó
memória a dinamikus:1n kezcIt mem6riaterület (heap), amil néha egyswrúcn .szabad
tárterű [elnek" is hívunk.

A lokális változókkal az a problém,l, hogy nem maradandók. Atnikor a függvény vissza-


tér, a lokális változó megsemmisül. A globális változ6k megoldják ezt a prob[émát, csak
épp a programból bárhonnan hozzáférhet6ck, amil:>61 aztán a Icgkülönfélébb problé-
mák eredhetnek. Az ilyen kódol állalában neMz megérteni és karbantartani. Az adatok-
nak a dinamikus memóriáb:m történ6 elhelyezése mindkét nehézségeI megoldja.

E[só közelrtésben a dinamikus rnem6ria egy j6 migy memóriatcn]lcl, ahol ezrével sora-
kozmlk a sorszámOZOlt mem6riacellák, csak arm várva, hogy adatokkal töltsük meg.
Azonban ezeket a cellákat nem címkézhet jük fel egyszeruen változ6bcvezctéssel, aho-
gyan a veremtefÜleten lévőket. Itt először kérnünk kell egy mem6riadmct, amelyen le-
foglalhatunk egy területet, aztán czt a dmet egy OlutatólYdn kell biztons.4gba helyeznünk.

Lássunk talán egy példát, amin kereszrül jobban megérthetjük a dolgot. Egy barátunk
megadja nekünk azt a 800-as tclefonszámot, amellyel a Mindentgyflrtó Amerikai Válla-
!attól (ACME) rendelhctünk poSlai utánvéttcl. Miután hazamegyünk, felvesszük ezt
a számot a telefonunkba, majd eldobjuk azt a darab papírt, amire a tclcfonszárnot e l ő­
zőleg felírtuk, Atllikor később tárcsázunk, a telefon kicsöng valahol , és az ACME cso-
magküldő szolgálat válaszol a hívásra. Nem emlékszünk már a tclefonszámra, és azt
sem tudjuk, hogy a hívOll telefonkészülék hol van, de a gyorshív6 gomb segÍlségével
akflrhányszor újra elérhetjük az ACME csomagküldó szolgálatot. Az ACME csomagkül-
dó ellől kczdve olyan, mint egy adat a dinamikusan kezelt memóriában (heap). Nem
rudjuk merre van, de azt igen, hogyan érhetjük el: a címén keresztül , jelen esetben a a
telefonszám segítségéve!. Még csak nem is kell ismernünk eZl a számot, elég ha el-
tesszük egy muratóba, eseTÜnkben egy gyorshívó gombhoz társítjuk A mutató hozzáfé-
rést biztosít az adatainkhoz anélkül, hogy a részletekkel kellene bajlódnunk.
176 1111. rész • Mem6riakezelés

A veremterület automatikusan törlődik, amikor a függvény visszatér a hívóhoz. Ezzel


az összcs a függvényre nézve lokális változó látókörön kivülrc kerül, és eltűnik a ve-
rembőL 1\ dinamikus memóriát azonban nem takarítja ilyen automatikusan a rendszer.
Az ott tárolt dolgok megmamdnak, amig a progr.lmunk be nem fejezi a futását. Ez per-
sze egyben azt is jelenti, hogy ami felelősségünk, hogy a használat vt!geztt!vcl felsza-
badítsunk minden olyan memóriaterületet, amclyet előzőleg lefoglaltunk. I la ezt elfe-
lejtjük, és otthagyjuk magunk után a már nem használt elemeket, akkor úgynevezett
memóriaszivárgás (leak) keletkezik. Erről a tl!már61 később még szót ejtünk ebhen
a leckében.

A dinamikus mernória legnagyobb előnye, hogy a lefoglalt tenHet egészen addig hoz-
záférhető mamd, amíg célzottan fel nem szabadít juk. Ez az állíuís feltétel nl:lkül igaz,
vagyis h" egy függvl:nyből fogblunk le dinamikus memóriát, az i.~ elérhető marad azu-
tán is, hogy II Cüggvl:ny visszatért a lúvóhoz.

A mem6ria ilyen módon tö rténő használata azzal :IZ clőnnye l jrlf a globális válrozókhoz
képest, hogy az adatokat csak :Izok a függvények érik el, amelyek ismerik a hozzájuk
vezető mutat6t.

Ez a módszer tehát egy szigorúan ellen6rtötl fdületet nyújt az adatok eléréséhez, és


megszüntcti azt :l problém5t, hogy az egyik függvény vámtlan ~S el6re nem láthat6
módon változtatja meg az adatainkat.

Ehhez azonoon musz. 'ij mUL'Itókat létrehoznunk és kezelnünk. A következ6 részben


megmUl:.lljuk, hogy:ln tehetjük ezt meg.

A new kulcsszó használata


A C++ programnyelvben a new kulcsszó segítségével foglalhatunk dinamikusan memó-
riát. A new kulcsszót annak a változónak/objektumnak a típusa követi, lItllcly számára
a memÓri:HefÜletellefoglalni szeretnénk. Ebb61 tudja a fordít6progflun , hogy mennyi
memóri{1l kcll biztosítani:l. A fentiek éneimében a new unsigned short int deklarnci(
példáu l két bfijtot fogbl a dinamikus memó riában, míg II new long négyct.

A new visszatérési értéke egy memóriacím, amelyet cgy mutat6hoz kell llozzárendeJ-
nünk. Egy unigned sort típusú dinamikusan kezelt változ6 létrehozás:1 .1 követke ző­
képpen történik:
unsigned short int * pPointer;
pPointer ~ new unsigned short int;

Természctesen a mutatónak a bevezetés során is adhatunk kezd6énékct:


unsigned short int * pPointer = new unsigned short int;
A mutató mindkét esetben a dinamikusan kezelt memóriában található unsigned
short int típusú adatra Illlltat , amelyhez a mulatón keresztül a következőképp ren-
delhetünk értéket:
*pPointer = 72;

Ez II következőt jelenti : ..tedd a 72 értéket a mutató által címzett teriiletre vagy


H
, ~ tedd
a 72 értéket a dinamikus memória azon pontjára, ahová a pPointer nmtat ".

Ha a new utasítás nem tudja lefoglalni a szükséges menny iségű memóriát (a memória is
véges erófortiÍs), akkor egy kivételt dob.

Apropó Kivétel generálása


A kivételek hibakezeló objektumok, amelyekkel a 24. leckében foglalkozunk majd
bövebben.

Nélliiny régebbi fordító ilyenkor nul lmut:ltóval tér vissza. Ha ilyent 11asználunk, föl -
tétlen e ll e nőri zzük. hogy null-c a 1llutatónk értéke a new végrehajtása után. Minden
újabb fordító esetében kiv(:tclck dob!lsára számíthatunk.

A delete kulcssz6 használata


:\liután végeztünk a memóriaterületünkön l évő adatokkal, ki kell adnunk a delete
utasítást a memóriadmellartalmaz6 1l111latÓra. A delete . viss7.llcsatolja" a memóriát
a szabad tetillethez. Ne feledjük, hogy maga a mutaLó - ellentétben lIzL11 a mem6riate-
rüleltel, ahová mutat - lokális változ6. Miután a függvény, amelyben bevczcuünk,
ViSSl.:ltér a főprogra mhoz, a mutató látókörön kívülre kerül, és elveszik. A new kulcs-
szóval lefoglalt memóriareriller, nem szabadul fel automatikusan, te hát ilyenkor az a LC-
rület c1érhetetlenné válik, ezt a helyzetet nevezzük memóriClszivárgásnak (memory
leak). Azért hívják szivárgásna k, mivel a program futásának végéig nem férhetünk hoz-
zá, azaz o l y~m, mintha a szabad memória elszivárogna a számítógépünkból

Hogy visszaadjuk a memóriatcriilctet a rendszernek, a delete utasítást mindig ki kel t


adnunk. Nézzünk cgy példflt:
delete pPointer ;

Amikor töröljük a mUlaL6t, valójában azt a memóriatcriiletet szabadít juk fel, amelyre
a mutató hivatkozott, valami ilyesmit kétilnk: ~Csatold vissza a a szabad területhez azt
a memóriadarabkát , amire a mUlató hivatkozik." A mulató még ezután is mutató ma·
rad, azaz szükség esetén újra mcmóriadmet rendelhetünk hozzá. A dinamikus lárban
történő mcmóriafoglalásr.! a 9.4. LiSla mutat példát. Láthatjuk a létrejött változó haszná-
latát, majd törlését is.
178 1111. rész • M.móriakezelés

Figyeleml Mutatók törlése


Amikor meghívjuk a dele t e utasítást egy mutatóval, a hivatkozott terület felsza-
badul. Ha újra törölni próbáljuk ezt a mutatót, a programunk összeomlik. Ennek ki-
védésére ha törjünk egy mutatót, állítsuk az egyben null értékre, a delete meg-
hívása egy null mutat6ra ugyanis garantáltan biztonságos. lássunk egy példát:
AnimaI * pDog .. new AnimaI ;
de l e t e pDo g ; Ilmem6~i a fels?abaditás3
pDog '" O: IImutat6 nu ll-ra állítása
II . ..
delet e pDog; r/veszélytelen

Ne aggódjunk, ha a fenti kódot nem értjük teljesen. Itz objektumok dinamikus keze-
léséról számára majd a következő leckében lesz szó. A módszer természetese mű ­
ködik az egyszeru adattípusokra, például az int-re is:
int *pNurnbcr = new in t;
dele t e pNumberl //mem6ria fels~ftbaditása
pNumhcr = Ol //mutat6 null-ra állitása
/ I . ..
deletc pNumberl /lvesz61ytelen

9.4 Us" - Mem6riafogl.lás60 mutat6k tö~60. (poIntarhHp.Cpp)


O: II 9. ~ Lista:
1: 1/ Mem6riafoglalás és mutat6k t6rlése
2: .include <iostreaffi>
3.
4: int main ()
5: {
6: int localV"riable :: 5;
7: int * pLocal= &localVariable;
8: int * pHeap = new int ;
9: if (pHe,lp "".' NULL)
10 : {
11 : std :: cout« "Error! No memory for pHcap!!";
12: return l;
13 :
14 : '*pHcap::: 7;
15 : std :: cout« "localVariable : • « localVariable « "\n ";
16 : std :: cout « · ·pLoca l : " « " pLocal « "\n ';
17 : std :: cout« ' ''pHcap : " « "pHaap« ' \ n ";
18 : delet a pHeapl
19 : pHeap " new int ;
20 : i f (pHeap == NULL)
21 : {
22 : std :: cout« ' Error! No memory fo r pHeap!!" ;
23 : r eturn l :
24 :
25 : "pHeap = 9 ;
26 : std ::cout« '''pHea p: '« *pHeap « '\n' ;
27 : delet e pHeap;
28 : rcturn O:
29 :
9. 6ra • Mutat6k 179

localVariable: 5
*pLocal: 5
*pHe ap: 7
*pHcap: 9

A 6. sorban kezd6értékkellátjuk el a bevezetett lokális vá1tozól. A 7. sorban egy muta·


tónak adjuk kezd6értékkéOl a lokális változó címét. A 8. sorban egy újabb mutalól ve-
zelünk be, amelynek énékéül eh'Y new int utasítás ViSS7..atérési értékét adjuk, amely di·
namik~lsan foglal memóriát egy int számára. A 14. sorban a 7 értéket tesszük az újon-
nan foglalt mem6riatelÜleLre. A 15. sorban kiimtjuk a lokális változó értékét, mlljd
a 16. sorba n a pLocal {tll::l ] hivatkozott értékeL A vánnak megfelel6en a k é l érték
megegyezik. A 17. sorban a pHeap által hivatkozott értéket íratjuk ki. A kimenet:lZ1
a tényt igazolja, hogya 14. sorban áradott érték hozzáférhet6.

A 18. sorban a 8. sorban fogla lt memóriát felszaood ítjuk a delet e lltasítás meghívfisfi-
val. Ennek hatás.1ra a felszab:lduló memóriaterület címe is törl6dik a mutatóból.
A pHeap ennek következtében szabadon újra felh:'lsználható. A 19.-25. sorokban újr:.!
mem6riacímet rendelünk hozzá, majd a 26. sorban kiír:.lljuk az eredményl. A 27. sor-
ban újr-.t visszacsatolju k a szab;ld területhez a lefoglalt memóriát.

Ugyan a 27. sor redundáns (mivel a program végén a lefoglalt memóriaterülelek úgyis
fe\.szabadulnak), mégis ajánlatos a memóriateriiletet a progmmoz6nak felszabadítania.
Ha a program megváltozik, vagy hozzáínmk még valamit, el6nyös lehel, ha már eI6z(5-
leg gondoskodtunk az ilyenkor szükségessé vá ló lépésekr61.

A mem6riaszivárgás elkerülése
A másik gyakori oka a mem6riaszivárgás jelenségének, ha m6g azcl6tt új címet rende-
Iünk a mutatóhoz, hogy felszabadítoltuk volna III általa hivatkozott memÓriaterületel.
Nézzük a következő k6drészletet:
unsigncd uhort int * pPointer = new unsigncd short int;
*pPointer '" 72;
pPointer '" new unsigned short int ;
*pPointer '" 84;
A fenti kódrészlet cls6 sorában egy pPointer nevú mulat6l hozu nk létre, majd hozzá-
rendelünk egy a dinamikus memóriához tartozó címer. A második sorban erre a terü-
letre a 72 értéket helyezzü k. Eddig a dolog rendben is volna. A 3. sorban azonban
ugyanehhez a mutal6hoz immár egy másik dmet rendel ünk, a 4. sorban pedig a &1 ér-
téket helyezzük el ezen az új területen. Az erede[Í memóriaterület, ahová cl6z61eg a 72
180 Im. rész • M.m6riak.,.~s
értéket írtuk, most már nem hozzáférhető , mivel a mut:lt6t, amely az oda vezet6 utat
mutatta, időközben fdülínuk. Mivel pedig nincs semmilyen mód az eredeti terülel el-
érésére, felszabadílani sem mdjuk azt a progmm futásának vége előtt.

A fenti kódrészlet tehát helyesen a következ6képpcn rest:


unsigned short i nt * pPoint er '" new unsigneo short int;
" pPoi n t er = 72 ;
delete pPointer :
pPointer '" new unslg ned short i n t ;
*pPointcr - 84 ;

Az újabb k6drés zletbe n .. zl a memóriaterületet, amire a pPointer hivatkozik, immár


fels7Abadítjuk a 3. sorban , vagyis kiküszöböltük a fenl vázolt hibál.

Tudta hogy... 1 Kövessük a mutatókat


Minden egyes alkalommal. amikor egy new utasftást kiadunk, ki kell adnunk hozzá
később egy delete utasítást is. Fontos követnünk, hogy melyik mutat6hoz van
memóriaterület hozzá rendelve, és mindig győződjünk meg róla, hogy a lefoglalt me-
móriaterületet visszaadjuk a rendszemek, miután már nem használjuk.

A malloc és free
Bizonyára találkoztunk már régebbi programokban malloc () és free () függvény-
hivásokkal. Ezek a függvények hosszú ideje a C nyelv részei, és ugyanúgy a dinami-
kus memóriakezelésre szolgálnak, mint a newés a dele t e utasítások. A leglénye-
gesebb különbség a régi C és az új C++ módszer között az, hogyamal loe szá-
mára pontosan meg kell mondanunk, hogy mennyi memóriát foglaljon. Nézzük meg
a fordít6prog ramunk súgóját vagy kézikönyvét, ha többet szeretnénk tudni róluk. Mi
természetesen a C+ + nyelvi lehetóségeit fogjuk használni a továbbiakban is.

Kérdések és válaszok
Kérdes: Miél1 olyan jOllfosak a mulalób?

t'á/asz: Amin t ez ebb61 a leckéb61 is kiderült, a 1l11.1t:l!6k azért olyan fOnlosak, mert
a segíL<;égükkel a dinamikusan kezelt memóriában is tudunk objektumokat elhelyezni,
illetve lehetővé teszik a refe rcnciakénti paraméterátad:.íst is. Ezen kívül a 13. órában
majd látni foguk, hogy hogyan használhatjuk a mul,Hókal a töhb szerepkörben felhasz-
nálható osztályok esetében .
Kérdés: MiiJT1 J•.'e/l egyáltaláll bajlódllllllk a db/ali/ikm memóliakeze/essel?

Válasz: Egyrészt a dinamikusan kezelt memóriában létrehozoll objektumok azután is


megmamdnak, hogyafüggvénylink visszatért a hívóhoz. Ezen kívül az objektumok tá-
rolásának ez a módja lehetővé teszi, hogy futásidőben dönLo;ük el, mennyi objekrumra
lesz szükségünk, ahelyett, hogy előre lefoglalnánk valamennyinek a helyet. Ez utóbbi
lehetőséget majd a 10. órában ismerhetjük meg mélyebben.

Gyakorlatok
Most, hogy megismerhettük a mutatók használatát, válas7.0ljunk néhány k6rd6src és
oldjunk mcg pM felad:not, hogy meger6síLSük eddig megszerzett ismereteinket.

Kvfz
']. Mi a különbség a O és NULL mulató kezcl6értékek között?
2. Mennyi memóriát foglal egy egészhez létrehozott mutató? Mennyi memóriát fog-
lal egy lebegé5pontos változól címző mulató?
3. Mi az. a mem6riaszivárgás?
4. Hogy:1Il tudjuk felszabadítani a new kulcsszóval lefoglalt memóriát?

Feladatok
l. MódosítSlIk a pointeruse. cpp (9.2. Lista) programot úgy, hogy a pAge mlltat6t
O kezd6értékkel lássuk el NULL helyet!. Változott bármit is a futási eredmény?
2. M6dosítsuk a pointergtore. cpp (9.3. Lista) programot lll. alábbiak szerinl:
szorozzuk össze a yourAge és pAge változókat, majd tároljuk ezt az értékeket
egy új vált07.óban. fmssuk ki az új változó értékét. Gondolkodjunk el, vajon
honnan tudja a forditó, hogy II " karakter mikor jelent szorLást és mikor a pAge
mutatóra történő hivatkozást.
3. Végezzünk újabb módosítást a pointerstore . cpp (9.3. Lista) progmtnon: old-
juk meg, hogya "pAge mutatót használva módosíLSa a myAge vagy yourAge v;,\l-
tozők tartaimát. Megváltozik ett61 a pAge-ben tárolt mcm6riacírn?
182 1111. resz • M,m6ri.k,zelés

Válaszok a kvízkérdésekre
l. Mindkettő a nulla címel rendeli a l11utat6hoz. A NULL nyilvánvalóan egy mulató,
míg a O inkább úgy néz ki , mint egy egész, de a jelentésük végeredményben
azonO$. KódoláSt stílus kt!fdése, hogy ki m<::1yiket használja.
2. A legtöbb rendszeren a válasz az, hogy ugyanakkorák. Amúgy viszont progr..!-
moz6ként egyáltahin nincs szükségük erre az információra. Elég annyit tudnunk,
hogya mutatók mérete biztosan elegendő a hhoz, hogy dférjenek bennük a gé-
pünkön használatos memóriacímek.
3. A mem6riaszivárgiís akko r lép fel , amikor dinamikusan foglalunk Ic memóriát,
de nem szabadítjuk fel azt, antikor már nem használjuk. A program ilyenkor to-
vább fogalja a memóriát, így egyre kevesebb szabad hellyel gazdálkodhat
4. Használjuk a delete kulcssz6l. A legjobb :lZ, ha azonnal töröljük azt II mutal6t,
:lmely áhal dmzett adatra már nincs szükségünk.
10. ÓRA

A mutatók kifinomult használata


Ebben az órában a következőkr61 lesz szó:

• Hogyan hozzunk létre objektumokat a dinamikusan kezdt memóriában


• Hogyan haszn:'ilhatjuk igazán hatéko nyan a mutatókat
• Hogyan cl6zhctjük meg :il memó riagondokat a mutatók használata közben

Objektumok létrehozása a memóriában


A CH programozó egyik leghatékonyabb eszköze a mutatók használata: segítségükkel
közvetlenül hozzáfé1'11Clóv(: v{l]ik a számítógép memóriája.

Ahogy létre lehet hozni egész szflmra hivatkozó mutatót, ugyanúgy bármi lyen objek·
[umra rámutathatunk Ha deklarál unk egy Cat típusú objektumot, akkor deklarálható
erre az osztályra hivatkozó mutató, és máris példányosítható egy Cat objektum a dína·
mikusan kezelt me m6riában, mint ahogy az a veremben is meglehető. A szintaxis meg-
egyezik az egészeknél használnal:
Cat *pCat = new Cat ;
184 1111. rész • Memóriak8lBlés

Ez az alapértelmezett konstruktort ruvia meg, amely nem kér paramétert. A dinamiku s


memóriában vagy a vereremben konstruktorrallehet objektumot létrehozni.

Objektumok törláse
Ha egy mutatón keresztül törlünk (delete) egy objektumot a memóriából, akkor
az objektum destruktora hívódik meg a memóriaterület felszabadítása el őtt. Ez lehető­
séget ad arra, hogy az osztályunk után kitakarítsunk, hasonlóan ahhoz, mint ahogy ez
a verem esetén is megtön6nik. A 10. 1 Lista bemutatja, hogyan hozhatók létre és ho-
gyan törölhetók objeknllnok a mcmÓriáb6l.

10.1 lma - ObioktumDk I_ozáu 60 tö~.... di....lkuIa. kezolt mom6ri6llan


(h•• pcreate....)
o: II 10 . 1 Lista
1 : II Objektumok létrAhozása a mem6riáb;:ln
2: #include <ios t rcam>
] ,
4: class SimpleCat
5: (
6: public:
7: SimpleCat() ;
8: -SimpleCat() ;
9: privata:
10: int itsAge;
11: };
12 :
13 : SimpleCat : : S ilnpleCat ()
14 : (
15: std: : cout « ' Constructor called ." « std : : endl ;
16 : itsl\ge ol 1;
17 :
18 :
19 : SimplozCi!lt: : -SimpleCa t ()
20 : {
21 : std: : cout « "Dcstructor called." « std: : endI;
22 :
23 :
24 : int main ()
25 : {
26 : std: : COIJt « ' SimpleCat Fr isky . « std : : end.1 :
27 : SimpleCat Frisky ;
28 :
29 : std : : cout « "SimpleCat *pRi!lgs z new Simplecat ... " « std : : endl;
30 : SimpleCat * pRags ; new SimpleCat;
31 :
32 : std: : cout « "delete pR3gS ... • « std :, endl ;
33 : delete pRags;
34 :
35 : std: : cout « " Exiting, watch Frisky go .. . " « std : :endl :
36 : return O;
37 :
10. óra· A mutatók kifinomult használata 185

Kimenet
SimpleCa t Frisky . . .
Constructor called .
SimpleCat * pRags = new simpleCat ...
Constructor called.
de1ete pRags ...
Dcstructor ca1led.
Exiting, watch Fdl;ky go .
Destructor ca1led.

A 4-ll. sorban deklaráljuk a lecsupaszított simpleCat osztályt. A 27. sorban Frisky


létrejön a vermen, melynek hatására lefut a konstruktor. A 30. sorban pRags lllutatóv;l l
létrejön egy másik simp l eCat, de ez már a dinamikus memóriában (,I konstll.1ktor itt is
meghív6dik). A 33. sorban delet e-lel töröljük a pRags mutat6t, ezzel a dcslruklorl is
meghívjuk. Amikor a f6program függvénye véget ér, Frisky a hatókörén kívül kerül ,
így a destruktor munkába 16p.

Adattagok elérése mutatókon keresztül


A helyileg (veremben) 16trdlozott Cat objekl'umok esetén <IZ adauagokat és tagfüggvé-
nyekel a pont (.) operátorr:.11 érheuük eJ. A dinamikus mem6riában létrehozott Cat ob-
jektumok eléréséhez fel kelloldanunk a mulatót , és a pont oper:.'lIort kell meghívnunk
a mutatott objekrumra. így például a GetAge () tagfüggvényt így érhetjük el:

(·pRaQ's) .GetAgc () ;

A zárójel biztosítja, hogy a pRags felold:'ísa még <1 GetAge () függvény meghívása előtt
t.örténjen.

Mivel ez igy elég kényelmetlen, a C++ nyelv rendelkezésre bocsát egy rövidített operá-
tort a közveten eJéreshez, a ~ mlllar opecltort (->, kÖl6jel + ~ nagyobbn jel). A C++ eZl
egyeden szimbólumként kezeli. A 10.2 Lista mutal egy példát a memóriában létreho-
zott objektumok adattagjainak és lagfüggvényeinek elér~sére.

10.2 Lista - Dinamikusan létrehozott obisktumok adanaaiainak az elérése


(heapaccess.cpp)
o: II 10.2 Lista
1 : II Objektumtagok elérése a dinamikus memóriában
2: linclude <iostream>
3 : using std::endl;
4: class Simp1cCat
S: (
6: public :
7: simp1ecat() {itsAge - 2; }
III. rész · MemóriakeZ8lés

8, -Simp1eCat{l II
9, int GetAge(l const return itsAge ; }
10 : void SetAge{int age l { itsAge = age ; }
ll : privatc :
12 : int it sAge :
13 : };
14 :
15 : int main(J
16 :
17 : SimpleCat • Frisky : new SimpleCat :
18 : std : :cout « "Frisky • « Frisky->GetAge(l
19 : « • éves' « endl;
20 :
21 : Frisky->SetAge{5l :
22 : std : :cout « 'Frisky • « Frisky >GetAge()
23 : « • éves ' « endl :
24 :
25 : dc1cte Fr i s ky;
26 : return O;
27 :

Kimenet
Frisky 2 éves
Frisky 5 éves

A 17. sorban egy SimpleCat o bjektumpé ld{my jön lé tre a me móriában, me lynek é le t-
konn az alapénelmezett ko nstruktor 2-re á llítja be. A lR. sorban a GetAge () tagfügg-
vé nyt hívjuk meg. Ez most egy mutató; ilyenkor a • lilii/a f operátorral tudjuk elérni
:1 hivatkozon tagfüggvC:nyt. A 21. sorban a SetAge () metódus, a 22. sorban pedig újra
a GetAge (l hívódik meg.

Adattagok a dinamikus memóriában


Az osztályo k egy vagy több adatlagja rámutathat a mc m6ria egy-egy objekllllnára .
A szükséges memóriate rü let lefoglalása tönénhc t az osztály konstruktorában vagy vala-
mely metódusában, a fe lszabadítása pedig a dcstruktorában, ahogy az a 10.3 Listában
is látható .

10.3 Lista - Adattal sl8r.pét játszó mutatók (datamember.cppl


o : /I 10.3 Lista
1 : II Adattag-mutat6k
2 : iinc lude <iostrcam>
3,
4: class SimpleCat
5,
10. óra · A mutatók kifinomult használata 187

6, public :
7, S i mp leCat ( ) ;
8, -SimpleCat{ }:
9, int GetAge~() cons t return ~ it sAge ; l
10 : v o i d SetAge(int a g e ) ( *itsAge " a ge ; )
11 :
12: int GetWci9ht() con st { return *it sWcight ;
13 : v oid setWeight ( i nt weight) ( *i t sWeight = weight ;
14 :
15 : p riva t e :
16 : int ~ itsAge ;
17 : int * itswci9ht ;
18 : );
19 :
20: SimpleCat : : Simp1cCat (J
21 : (
22 : i t sAge = new 1n t(2) ;
23 : i t s Weight • new i nt ( 5) ;
24 :
25 :
26: SimpleCat , : -SimpleCat ()
27 , (
28, delete itsAge :
29: delete itsWeight;
30 :
31 :
32 : int main()
33 : (
34 : SimpleCat *Frisky = new SimpleCat ;
35: std : : cout « "Frisky " « Frisky->GetAge()
36 : « • éves\n' ;
37 :
3B : Frisky->SotAge(S) ;
39 : std : : cout « " Fr i s ky , « Frisky->GetAge ( )
40 : « • éves\ n';
41:
42: d elete Frisky ;
43 : return O;
44 :

Frisky 2 éves
Frisky 5 é ves

A Simp1eCat osztálynak két tagváltoz6ja van, mindkettő egészeket címez. A konstruk-


tor (a 20-24. sorban) inicializálja ezeket a mutat6kat úgy, hogy a dinamikus mem6 riate-
rületre mutassanak, és a változ6khoz hozzá rendeli a megadou kezd6érrékekcl.
188 1111. rész • Mem6riakezelés

A destnIktor (a 26-30. sorban) felszabadítja a lefoglalt memóriaterOletekcl. Minthogy ez


a destruktor, nincs értelme nullázni a mutatókat, hiszen ezek a későbbiekben már nem
lesznek elérhetőek. Ez egy bizlonságosan vállalható kivétel azon szabály alól, hogy
a törö lt mutatókhoz érdemes null értéket rendelni ; persze nem okoz gondor a szabály
követése se m.

A hívó függvény - jelen esetben a main () - mit sem rud arról , hogy az itsll.ge és
az itsWeight a dinamikus memóriára irányuló mutatók. Egyszenle n meghívja
a GetAge () és a GetWeighL () függvényeket, mint eddig is; a mem6 riake zelés részle-
t.ei el vannak rejtve az osztály megvalósításában - ahogy annak lennie kell .

Amikor F'risky törlődik a 42. sorban, egyből meghív6dik a destruktOJ:l. A deSlmklor


minden adattag-mutatót töröl. Ha ezek kimutatnak egyéb objektumokra is, melyek a fel-
Il:lszn{116 {tltal definiált oszt51yok példányai, akkor azok destruklorai is meghfv6dnak.

Ezen a p6kl{tn jólláthat6, hogy miért érdemes saját destruktort írni (a fordít6 progr.:tn1
állal fclaj{tnlotl alapértelmezés helyett). Alapértelmezetten a 28. és 29. sorban láthat6
törl(:sek n~m tört(:nnének meg; a programozómik kell ezeket ll1<:!gírnia. Ezek híjáll
a 42. sor törl(:se csak magám a F'ris ky objektumm vonatkozna (és a hozzátartozó mu-
tatók .....,i), de a dinamikus memóriában létrehozott bejegyzésekre nem. A destHlktor nél-
kü l memóriaelszivárg:'isunk lenne.

A this mutat6
Az osztályo k minden tagfuggvénye tartalmaz egy rejtett paJ:lmé tert: a this ( ez) muta-
tót. Ez muta! magára az obje ktumpéldányra. Így minde n GelAge (J vagy Set.i\ge () hl-
vásnál az obje ktum this mutatója rejtett paraméterké nt jele n van.

A Lh is mutató fe ladata rámutatni arra az objektumm, melynek metódusát meghívtuk.


Általában nem használjuk; egyszeruen meghívjuk a tagfüggvl!nyckel l!s beál1ítglltjuk
a tagválLOZókal. Alkalmanként azonban szükségünk leheL arm, hogy magát az objektu-
mot is elérjük (például a rá hivatkozó mutató vissza3dása miau). Ezen a ponton válik
igaz5n hasznossá II this mutató.

Általftban nem kell a this mutatót haszná lni ahhoz, hogy egy objektum tagváltozóit el-
érjük ugyHnazon objektum tagfüggvényein belül. Azonban meghívható kifejezenen
a t hi s mutató is, hH a programozónak úgy tartja kedve. A ] OA Lista bemutatja, hogy
ez miként használható.

10.4 Usta - A this mutató használata lusingthis.cpp)


o: II 10 . 4 Lista
l : II .i\ th i s mut at6 használata
2: .include <iostre am >
3 : using namespa ce std;
10.6ra • A mutatók kifinomult

4: class Rectangle
5: {
6: pu blic ,
7: Rec t a ngle () ;
8: -Rectangle() ;
9: void SetLength(int length) ( this - >itsLcngth = length ;
10 : int GetLength() const ( return this->itsLength;
ll : void SetWidth(int width) ( itsWidth = widt h ; )
12 : int GetWidth() const { return itswidth ; }
13 :
14: private:
15 : int itsLength;
16 : int itsWidth ;
17 : );
18 :
19: Rectang1e : : Rectang1e ()
20 : (
21 : i t s Wid th = 5 ;
22 : itsLength;; 10 ;
23 :
2 4:
25: Rectangle : : -Rectang1e()
26 : ()
27 :
2 8: int main()
2 9: (
30: Ractangle t haRect ;
31 : cout « "Tógla 1apom " « theRect . GetLength()
32: « • láb hosszú ." « end1;
33: cout « "Téglalapom • « theRect . GetWidth()
34: « " láb széles . " « endl;
35 :
36 : theRect . $et Length(20) ;
37 : theRect . SctWidth(lO);
38: cout « "Tégla!apom • « thcRect . GetLcngth(l
39 : « " láb hosszú . " « endl ;
40 : cout « "Téglal a pom " « t h cRect . GetWidth()
41 : « • láb széles . " « e ndl ;
42 :
43: return O,
44 :

Téglalapom 10 láb hosszú .


Tégl alapom 5 láb széles .
Téglalapom 20 láb hosszú .
Tégl alapom 10 láb széle s .
190 III. rész • Mem6riakezelés

-
A SetLength () és GetLength() hozzáfér6 fü&!,,,,ények kifejezetten a this mutatót
használják a Rectangle objektum tagváltozóinak elérésére, ellentétben a SetWidth (J
és GetWidth () hozzáfér6 függvényekkel , melyek másként dolgoznak. Nincs különbség
a viselked ésükben, csak abban, hogy a this nélküli metódus kódja talán olvasl1at6bb.

Tudta hogy...? Mire is val6 tehát ez a this mUlat6?


Ha csak ennyi értelme lenne a this használatának, nem lett volna érdemes meg-
emlfteni. A this mutató hordozza az adott objektum memóriacfmét - ez igen haté-
kony eszköz lehet!
A könyv egy késő b bi (14.) fejezetében láthatjuk majd ennek a gyakorlati felhaszná-
lását az operátorok túlterhelésénél. Egye l ő re elég annyi is, ha tudunk a this muta-
tó l é tezéséről, és arról. hogy magára a szóban forgó ob jektumra mutat.
Semmi dolgunk vele, nem kell létrehoznunk vagy törölnünk - ezt elvégzi helyettünk
a fordítóprogram.

Gazdátlan vagy "lógó" mutatók


A programhibák egyik forrása a gazdátlan mutatókb61ered, melyeket nehéz és kellemet-
len felkutami. Gazdátlan mutató akkor keletkezik, amikor törjünk egy mutat6t (delete-
leO - ily m6clon felszabadít juk a hivatkozott memóriaterillelet - és kés6bb anélkül kisé-
rcljük meg újra használni ezt a muratót, hogy bármit is hozL1rendeltünk volna.

Olyan ez, mintha egy cég elköltözne telephelyér61, és egy ügyfelük a régi sti'imon pró-
bálna telefonálni nekik. Lehet, hogy semmi különös nem történik - csöng egy telefon
egy elhagyott irodaházban. De az is lehet, hogy ezt a számot már valaki más használja,
aki esetleg v~gigdoJgozta az éjszakát, és ez a telefoncsörgés ébreszti legszebb álmából.

Röviden: ne használjunk olyan mutatókat, melyeket e16z6leg töröltOnk. A mulató to-


vábbra is a mem6ria egy bizonyos területére mutat, de ;1 fordítóprogramnak joga van
oda más adatokat tenni, így ennek a mtJtatónak a használata II progl"J.m összeomJásá-
hoz vezethet. Ennél is rosszabb, ha a program vígan fut tovább, és a hiba csak néhány
perccel kés6bb következik be. Ezt időzített bombának hívják, és nem túl vicces. A biz-
lonslig kedvéélt érdemes NULL-ra állítani (és ezzel lefegyverezni) a használaton kívül
helyezett mutat6kat.

Apropó K6bor mutat6k


Az. gazdátlan mutatókat kóbor vagy lógó mutatóknak is hívják.
10. óra • A mutatók kifinomult használata 191

Konstans mutatók
Mutatók esetében a const kulcssz61 ti típus előtt , után, vagy mindkét helyen használ~
hat juk. Az alábbi deklarációk mind helyesek:
const int • pOne ;
int • con s t pTwo ;
c onst int • const pThree ;
Ezek különooz6 mutatókat eredményeznek. pOne ch'Y konstans egészre mutat. A hivat-
kozou értéket nem lehet megváltoztarni a mutatón keresztül, azaz nem m űködik
az alábbi sor:
' pOnc = 5

Ha eZI kís6reljük meg, hibát ad a forJít6 progmm. pTwO konstans mutató egy egészre.
A hivatkoZQII szá m értéke megváltoztathal6, de a p'I'wo nem mutathat sehová máshová.
Ko nsWns mutat6hoz nem lehet más változót rendelni. Azaz nem működik a következ{}:
~p'rwo .. &x

pThree konstans mutató egy konstans egészre. A hivatkozott szám értéke sem válloz-
tatható mcg, és a pThree sem mutathat semmi másra.

Húzzu nk egy képzeletbeli függ61eges vonalat :l csillag jobboldal án. Ha a cons t szó
a vomtlt61 ba lra esik , akkor az objektum konstans, és ha jobbr-.l, akkor pedig a maga
mutató változtathatatlan
const int· pl ; II A hi vatko.,;ott egé!lz konstans
i nt • const p2; I I p2 ko nsta ns , ne m mu tat hat semmi másra .

Konstans mutatók és konstans tagfüggvénv.k


A 7. 6rán tanultunk az osztályok alapjair61. Oli volt szó arrol, hogya const kulcsszót
lehet tagfüggvényekre is vonalkOZL1.tn i. l ia egy függvényt konstansként dekladlunk,
a fordíl6progr-.un hibajelzésselm3sít viSsza minden kísérletet, amellyel az adon függ-
vény változtatni akama az objekrumon.

Ha konstans objcklumra hivatkoz6an deklará lunk egy mutatól, akkor ezt csakis kons-
tam; mctódusokkallehet használni. A 10.5 Lista ezt illusztrálja.

10.5 Usta - const objektumot cimz6 mutatá használata (constptr.cpp)


o : !! 10 . 5 Lis ta
l : I I ko n s tans objektumot c i mzo mu tat6 ha s.,;ná l ata
2 : 'incl ude <iostream>
3,
4 , class Rec t angle
5: {
6 : public ,
192 1111. rész • M.móri.koze~s

7: Rectangle() ;
8: -Rectangle() ;
9: void SetLength( int l e ngt h) ( itsLength = leng t h ;
10 : int GetLength() const { re t urn itsLength ; }
ll :
12: v o id SetWi d th {int wi dt h) l itsWid th - wi dth;
13 : i nt Get Widt h() c on s t { retu r n i t s Wid th ; )
14 :
15 : private :
16 : int itsLength ;
17 : int itsWidth ;
18 : };
19 :
20 : Rectangle: : Rectangle () :
21 : itsWidth(S) ,
22 : itsLength (10)
23 : (l
24 :
25 : Rcc t angle : : _Rectn ng l e ()
26 : {}
27 :
2B : int main()
29 :
30 : Rectang!a* pRect = new Rectanglc;
31 : const Rectangle * pConstRect • new Rectangle ;
32 : Rectangle • const pConstPtr = new Rectangle ;
33 :
34 : std : : cout « 'pRect szélessége : "
35 : «
pRect ->GetWidth()« láb"« std : : endl ;
36 : std : : cout « "pCon s t Rect szélessége :
37 : « pCo n stRect->Cctwidth() « ' láb' « std , , endl;
38 : stó : , cout « ' pCo n stPt r szé l essége ,
39 : « pConstPtr->CetWi d th () « ' láb ' « std :: endl ;
40 :
41 : pRect->setWidth(10) :
42 , II pConstRect->SetWidth(lO) ;
43 : pConstPtr->SetWidth(lO) ;
44 ,
45 : std : : cout « 'pRect szélessége:
46 : «pRcct->GetWidth()« láb'« std : : endl ;
47 : std , , cout « 'pConstRcct szélessége :
48 : « pCo n s t Rect->Get Width() « • l á b " « std :: cndl ,
49 : s td : : cout « 'pCon s tP t r szélessége :
50 , « pConstPt r - >Ge LWidth() « • láb' « s td : : endl ;
5 1: return O;
52 :

pRect sz élessége : 5 láb


pConstRec t szélessége : 5 l áb
pConstPtr szélessége : 5 l á b
pRect szélessége : 10 láb
pConstRcc t szélessége , 5 láb
pCo n s t Pt r s zé l essége : 1 0 l áb
10, óra • A mutatók

A 4- 18. sorokban deklarálunk egy téglabpot. A 13. sorban a GetWidth () tagfüggvényt


konstansként deklaráljuk A 30. sorban cgy mutatóva! hozzuk IéIre a pRect téglalapot.
A 31. sorban deklarált pConstRect egy konstans téglalapra hiv:ltkozó mulató. A 32.
sorban létrehozott pConstPtr pedig egy téglalapra hivatkozó konstans mulató.

11.34-39. sorok kiírják a három szélesség-értéket.

A 41. sorban a pRect segítségévcl álállitjuk az els6 téglalap szélességét 10-fe. A 42. sor-
ban a pConstRect használatával tennénk u~,'yaneZlJ de ez II mUlató egy konstansként fel-
veti téglalapra hivatkozik, melynek é rtéke nem változtatható, és nem hívható meg rá
nemkonstans L:tgfüggvény. Ez a sor tch:1t megje,gyzéssé van alakitv:-I, A 32. sorban
3 pConntPtr konstans mutatóként lett lélfehozv<l, va,gyis senuni másra netTI tud már mu-
tatni, csak az eredetileg kiszemelt tégla lapr.lj ez llzonban nem konstans, így változtatható.

Konstans this mutat6k

Amikor konstansként deklarálunk egy obje ktumot, ezzel a hozzá tartozó this mutatól
is konsla nSr:l hivatkozóként hal:1rozzuk meg. A konstans this mutató viszont csak
konstans tagfüggvényekkel használhat6.

A kom:t:lOs objC;!klumok és konstans mulatók II következő órán új!"'..! el6kerülnek, ami-


kor a konstans objektumokra vonatkozó hivatkozásokat tárgyaljuk

Kérdések és válaszok
Kérdés: Mié11 érdemes konsUII/ske1lf dek/arálllom egy objektumot, ha ezzel korlálozom
a jelhasz1lálltalóságál?

Vá/asz: Progmmozóként az az egyik célunk, hogy magával a forditóprog!"'dmmal minél


több hibátlelepleztessünk. Egy nehezen fe!derithet6 komoly hib<l lehel az, ha egy
függvény olyan módon válloztat meg egy objektumot, ahogy azt nem várnánk t6le.
A lehetCi legtöbb objeknun konstansként való deklarálása megel6zheti ::IZ ilyen hibákat.

Kérdés: Mi é11elme azza! baj/ódllom, hogy bármit is a dillamikllst/ll kezelt memóriá-


ban dek/aráljak?

Válasz: A memóriában deklarált objektumok megmaradnak a függvények visszatérése


után is. Ezek ráadásul dinamikusak iSi vagyis annyit lehet belőlük létrehozni, amennyi-
re csak szüksége van egy adott alkalmazásnak vab')' egy bemenő adath<llmaznak. Ezen
túl a dinamikusan kezelt objektumokat összetett adatszerkezetekhez is fel lehet hasz-
nálni, ahogy arr61 a láncolt listáknál is szó lesz a 19. órában.
194 ( 111. rész • Mem6riakezelés

Gyakorlatok
Az elmúlt órán a mutatók használatinak sokféle l ehetősége tárult föl az olvasó elön; nl-
dásának elmélyítéséhez válaszoljon meg most néhány kérdést és végezzen el néhány
gyakorlatot!

Kvfz

1. Milyen C++ kulcsszavakkal tudunk dinamikusan mem6riatelÜletet foglalni és fel-


szabadíta ni?
2. LeheL~ges-e törölni egy objektumOl (azaz meghívni a destruktorát) anélkül,
hogy nem használjuk a törlési (delete) p:mmcsot?
3. Melyik <l két t.1nult módszer az adattagok mutat6val történ6 eléréséhez? Melyik
a jobb?
4. Mi az a gazdátlan mUlató?

Feladatok
L Vegye ki a megjegyzésb61 <I constptr . hpp program 42. somt 00.5 Lista). Mi
történik? Mi a fordítóprogram üzenete?
2. 13iwnyára észrevette, hogy r16hány programkód \n-et használ, más helyeken
std : : endl nJnik fel. Helyenként std : :cout és std : : endl szerepel, másuLt pe-
dig a using utasítással együn használt cout és endl. Próbálja meg ezeket cse-
rélgetni a fejezet programjaiban. Melyiket jobb használni, a \n-et vagy az end1-
et? Melyik az egyszenlbb, a std : : elCítag vagy a using utasítás?
3. Kilszőbölje ki a " mulaf' operátort a heapaccess . cpp programban (10.2 Lista)!
Melyiket könnyebb olvasni, az eredeti vagy a módosítolI változatot?

Válaszok a kvfzkérdésekre
1. A new utasítással lehet memóriaterilletet lefoglalni a dinamikusan kezelt terüle-
len; a de1ete utasítással lehet ugyanezt fe lszabadítani.
2. Amikor a vezérlés kilép egy objektum hatóköréból, az objeknllll automatikusan
törl6dik. Ha egy objektu mot létrehozunk, de nem törJü nk a main () függvény-
ben, akkor a main (I-ből való kilépéskor hívódik meg a destmkror, ahogy eZL
a 10.1 Lista kimenetének utolsó sora is mutatja.
3. Az egyik lehetőség a (·pRags) . GetAge (), a másik a pRags - >GetAge ().
A ~ m utat " ( - » operátor jobb, mivel ránézésre is nyilvánvalóan mutatja a progra-
mozó szándékát.
4. Gazdátlan mutatóról akkor beszélünk, ha azután kíséreljük meg amemória
használatát egy mutató révén , miután már azt a mulatót töröltük. Ilyenkor nem
tudhat juk, hogy az adott memóriaterület milyen szerepben áll!
11. ÓRA

Hivatkozások
Ebben az órában a kővetkezókróllesz szó:
• Mik azok a hivatkozások
• Miben különböznek a hivatkozások a mutatóktól
• Hogyan lehet hivatkoz.'isokat létrehozni és használni
• Milyen korlátai vannak a bivalkozásoknak
• Hogyan lehet a függvényeknek értékeket és objeknllnokal átadni és t510k átven-
ni hivatkozások segítségével

Mi az a hivatkozás (referencia)?
Az elmúlt két órán áttekintettük a mutatók használatár; hogy segítségükkel miként le-
hel a dinamikus memóriában lévő objektumok kal bánni, és hogy hogyan lclu;!( ezekre
az objektumokra közvetett módon rámutatni. A hivatkozások, melyről ezen az órán sz6
lesz, a rnutatókhoz hasonlóan hatékony eszköZT adnak a kezünkbe, csak jóval egysze-
níbb szintaxissal.
196 1111. rész • Memóriakezelés

A hivatkozás egy alternatív név ( alias). A hivatkozást egy másik objektum, a célobjek-
tum nevével lehet inicializálni. Ettől a pillanattól kezdve a hivatkozás úb.'Y viselkedik,
mintha maga a célobjektum lenne; bármi , amit a hivatkozással teszünk, megtörténik
a célobjektummal is.

Ennyi az egész. Néhol úgy emlegeti a hivatkozásokat, mintha azok mutató k le nné-
nek, de ez nem pomos. Bár gyakran tényleg mutatóként valósítják meg 6kel, ez csak
a fordítóprogmmok gyártóira tartozik. I'rogmmozóként el kell mdni különíteni a két
fogalmat.

A mUlatók olyan változók, melyek egy másik objekmm memóriacímét tárolják. A hivat-
kozások ezzel szemben egy másik objektum névváltozatai.

Hivatkozások létrehozása
Hivatkozást úgy hozh:nunk létre, hogy megadjuk a <:élobjcklum típusát, majd a ~ hlva l ­
koz tí:," operátort (&), végül :1 hivatkozás nevét. Ez utóbbi bármi lehet, de könyvünkben
kis r-rel fogjuk kezdeni a hivatkozások neveit. II .. például van egy somelnl. ncvlT
egész változónk, akkor az .. Iábbi módon hozhatunk létre egy hivatkozást rá:
int &rSomeRef • someInt:

Ezt (Jgy olvassuk, hogy ~rSomeRef hivatkozás egy egészre, melyet a someInt-re hivat-
kozva inicializ:ílnmk ~. A '11 .1 Lista mutatja be a hivatkozások létrehoz.'lsát és haszn:ílatát.

Apropó Ahivatkozás operátora


A "hivatkozás operátor" !&) megegyezik a "elme" operátorral. Példánkban a dekla-
rációban fordul eló.
Emlékezzünk vissza, hogyamutatóknál is hasonló volt a helyzet ha a deklaráció-
ban szerepelt a csillag (,,) jel, az azt jelentette, hogya szóban forgó változó egy
mutató. Ha utasftásban szerepelt, akkor a .közvetett' operátor! jelentette
(amennyiben mutatóval együtt jelent meg), vagy matematikai kifejezésben
a HSlOrzás" operátOr!.

11.1 Lista - Hivatkozások létrehozása és használata (cre8tereference.cpp)


0 , II 11 . 1 Lista
1 : II Hivatkozások (referenciák) létrehozása, használata
2 : Jlinclude <iostream>
3,
4: int main ()
5,
6, int intOne;
7, int &rSomeRef intOne ;
B,
9: intO ne '" 5 ;
11. óra • Hivatkozá,ok 1197

10 , std: :cout « 'intOne : • « intOne « std: : endl;


ll : std : : cout « 'rSameRef : • « rSorneRef « std: : endl ;
12 :
13 : rSomeRef = 7;
14 : std : : cout «'intOne: «intDne« std : :cndl;
15 : std : :cout « 'rSomcRcf : • « rSomeRef « std : :endl;
16 : return O;
17 :

intOne: 5
rSomeRef : 5
intOne: 7
rSomeRef : 7

A 6. sorban tleklm'álunk egy egész változóI (intOne). A 7. sorb:m hivatkozást (rSomeRef)


deklarnlunk eb'Y egészre, melyet al intOne-r::t hivatkozva iniciali7...'Ílunk. Ha initiali:r.álás
nélkül akarunk deklarálni egy hiv;Ltkozásl, akkor rordítási id6ben hibát kapunk. A hival-
kozások:lt inidalizálni kell .

A 9. sorb:m a7. intOne-nak értékü] adjuk az 5-öl. A 10. és ll. sorb:1O kiír::tljuk
az intOne és az rSomeRef értékeit, melyek természetesen megegyeznek , hiszen
az rSomeRef egy egyszeru hivatkozás az intOne-rd.

A 11. sorban az rSomeRef-nek értékül adjuk a 7-el. Minthogy ez egy hiv:ltkoz..ís, az


intOne alternatív neve, a 7 tulajdonképpen az intOne változólxI kerul , ami a A 14. és
15. sorban ki is derül a kiímtáskor.

A.eime· operátor alkalmazása hivatkozásokra


[la hivatkozás eímére kérdezünk rá, akkor a eé!objeklum címét kapjuk vissza. Ilyen
a hivatkozások Lennészele - pusztán egyallernatív névként viselkednek. A 11.2 Lista
ezt mutatja be.

ll.2Usta - A hlvatkoz'lOk cfm6nak meghaUtrozása {addressreference.cpp)


o: II 11.2 Lista
1 : II A hivatkozások és az "&" használata
2: jinclude <iostream>
3,
4: int main!)
5,
6: int intOne;
7: int &rSomeRef = intOne ;
g,
9: intOne "" S;
198 1111. rész • Mem6riakezelés

10 : std: : cout « 'intOne : .


« intOne « std : : endl;
ll : std : : cout « 'rSomeRef : « rSomeRef « std : : endl;
12 :
.
13 :
14 :
std : : couL «
std: : cout «
' &intOne :
• &rSomc.Re ( : .
«
«
&intOne « std : : end1 ;
&rSomeRef « std: : e nd l ;
15 :
16 : re t urn O,
17 :

JGmenet
intOne : 5
rSomeRef : S
&intOne : 1245064
&rSomeRef : 1245064

A kapott kimenet terlll(:szelesen nem fog ezzel megegyezni, hiszen minden szá mító·
gép más-más címen t{lfolja a változó kat, aItól függ6en, hogy még Iru minden van a me-
mó riáhan és hogy mekkora az dérhel6 me mó ria me nnyisége. Ha nem Borland fordító!
használunk, akkor így is festhet a lulás eredménye:
intOne : 5
rSomeRcf : 5
&intOne: Ox0012FF7C
&rSomeRef : Ox0012FF7C

s.
III is n intOne-ra hivaLkozvH in icializáljuk az rSomcRe f -et. Ez a lkalommal a változók
me mó riadmét is kiír.ltjuk, ami szinté n megegyezik. A C++ nem ad lehet6s6gcl arra,
hogya hivatkozások té nyleges memóriadmét lekérdezzük, hiszen annak nincs é rte l-
me, szemben a mutat6kkal vagy egyéb változókkal. A hivatkozások lé trejöllü kt61 fogva
a célohjeklUm szinonimáiként visclkednek, még a ~címd' operátor a lkalmazásakor is,

Nézzünk egy másik példát. Legyen egy President osztályunk, melyet az alábbi mó-
don példányosítsunk :

Lé lrehozhatunk és inidaliz:i lhatunk egy hivatkozást a President-re:

Csak egyetlen President van; mindkét név ugyanazon osztály egyazon objektumára
utal. Bármi, amit Dubya-n e lkövetünk, George_W_Bush-on is végrehajtódik.

Gondosan különböztessük meg a 11.2 Lista 7. sorába n található & jelet a 13-1 4. sorban
talá lhat6akt61. Az e l6bbi egy egészre utaló hivatkozást deklarál rSomeRef néven , míg
az utóbbiak lekérdezik egy egész számnak (és a rá vonatkozó hivatkozásnak) a me-
m6riacímét.
~ba n nem szokás hivatkozások mcmóriacímével dolgozni, hanem egyszeruen
...-é1objektum helyett használjuk a hivatkozást, ,thogy ez a 11. sorban is lál'izik.

T.. pasztalt CH progmmozók is zavarba jönnek, ha szóba kerül, hogy mi történik ak-
.._ha egy bivatkozáshoz megpróbálunk valami más változ6t hozzá rendelni. Azt tud-
~_ hogy a hivalkozásokat nem lehet újminicializálni, s hogy mindig is az eredelileg ki-
SZt.'TTlelt célobjeklu m szinonimái lesznek. A 11.3 Lista futásának tanús.'1ga szerim az új-
r.a·hozzárendelést (Igy tekinthetjük, mim értékadást a célobjektum számára.

11.3 Lista - Hivatkozás sz6mára történ6 értékadál (assignrefareace.cpp)


II l l. 3 Lista
II Hivatkozás újrainicia l izálási kisérle t e
, : l inc lude <iostream>
using name space std : II hüsználjunk most std :: obj e ktumokat
4:
5: int main()

7: int intOm! :
~ : int &rSomeRef • i ntOne :
9,
.. C: intOne • 5 :
: 1: cout ' intOne : \t ' i nt One end1 ;
l2 : cou t " ' rSomeRef :\t"" "
rSomeRef « end l :
... 3 : "
cout « "&intOne , \t" "« &in t One « andI ;
l4 : cout « '&rSomcRef , \t' « &rSomcRef « cndI ;
: 5:
16 : i nt intTwo '" 8 ;
17 : rSomeRef = intTwo : /I nem az lesz , mint amit vár hat nánk
18 : cout « " \ n intOne , \t ' « intOne « endl :
19 : cout « ' intTwo : \t' « intTwo « endl ;
20 : cou t « ' r SoIlleRcf ,\t ' « rSomeRcf « endl :
21 : cout « ' &intOne : \t" «&int One« endl :
22 : cout « ' &intTwo : \t" «&intTwo« endl ;
23 : aout « ' &r SomeRe f : \t' « &rSomeRef « end l :
24 : re tu rn O;
25 :

i ntOne : 5
rSome Ref : 5
&int One : 12450 64
&rSomeRef : 1245064
intOne , 8
intTwo : 8
rSomeRef: 8
&intOne : 1245 06 4
&intTwo , 124 5056
&rSomeRef , 1245 06 4
200 1111. rósz ' Memóri.kezelé,

A kapon kimenet nem ugyanígy fog kinézni, hiszen minden számítógép más-más cí-
men tárolja a váltm:6kat, ;Illól függ6en, hogy mi van a memóriában és hogy mennyi
az elérhető memÓria. Ha más fordítót használunk, akkor ehhez hasonlót is kaphatunk:
intOnc: 5
rSorneRe f: 5
&in t One : Ox0012FF7C
&rSomeRef: Ox0012FF7C
intOne: 8
intTwo: 8
rSomeRef : 8
&intOne: Ox0012FF7C
&intTwo: Ox0012FF74
&r.-SomeRef : Ox0012FF7C

ltt is cgy egész S:dlrnOl és egy rá utaló hivatkozást inicializálunk a 7-8. sorban, majd
az egész sz.ámnak érté kűl adjuk az S-öt CI 10. sorb'Ln. A változók értékeit és címeit CI 11-
14. sorban íraljuk ki.

A 16. sorban egy (lj változó, az intTwo jön létre és B-as kezd6értékkel inidaliz./j I6dik.
A 17. sorban a programozó (lj hozzá rendelést kísérel meg az rsomeRef-hez; legyen 6
ezentúl az intTwo hivatkozása. Ám nem ez történik. Az rSomeRef továbbra is
az intOne szinonimája mar:ld, így az ominózus értéka d~s az alábbival egyenénékll:
intOne = intTwo ;

Erre elég bizonyság az rSomeRef és az intOne értékének kiíratása (18-20. sor) - ezek
az intTwo értékével egyeznek meg. Az is látszik, hogy az rSomeRef elme továbbra is
az intOne elmével egyezik meg, nem pedig az intTwo-éval.

HoIyeo
Objektumok szinollimájaként hasz- Ne rendeljünk új objekmlllol egy,
n{lljuk a hivatkozásoka!. már l ét ező hivatkozáshoz.
Jnicializáljuk a hivaLkozásokm. Ne tévesszük őssze a "címr!' operá-
tort a "hivatkozáS' operátorral.

Mire lehet hivatkozni?


Minden objekmmhoz rendelhet6 hivatkozás, beleértVe a relhasználó által létrehozott
objeknunokat is. Érclemes tudatositani, hogy objektumm lehet hivatkozást létrehozni,
nem pedig osztályrd vagy adattípusra, mint amilyen például az egész. Nem ml1ködik
tehát az alábbi sor:
i nt & r lntHe f " i n t ; II rossz
11. 6ra • Hivatkozások I z01

Csak egy bizonyos egész változónak az álneve lehet a hivatkozás;


int howBig = 200;
int & rIntRef = howBig ;

Ugyarugy lehetetlen a eat osztályf'd hivatkozást állítani:


CAT & rCatRef - CAT ; II rossz

Az rCatRef-et csak egy bizonyos eat objektummal inicializ.'ílhatjuk:


CAT frisky ;
CAT & rCatRef = frisky;

Az objektulllokm mulató hivatkozások ugyanúgy használh:lt6ak, mint maguk az objek-


tu mok. Az adallagok v,Lgy me l6dusok a megszokolt osztálytag-kiválaszt6 operátorral
(. ) é rhet{5ek e l. A be6pítell típusokhoz hasonlóan az objektumoknak is szinonimákal
reremLt!llck a hivatkozások

Null mutatók és null hivatkozások


Ha egy mutat6 t tö röloc:k vagy ne m iniöalizálnak, a null é rtéket kell t:utalm3znia. A hi-
vatkozásokkal nem így áll a helyzet. Hivatk07..á-; nem lehel null értékű, Az :l progra m,
amelyik null é rtékű hivatkozásl tartalrnaz, éJVénytelennek tekinthct6, ekkor pedig
akármire ké pes. Futhat normá lisan , v:lgy éppen tö rölheti az összes állományt merevle·
mezünk ről. ÉlVénylc1en progl"'J.mtól minden kitelik.

A legtöbb fo rdít6progr::lrn nem panaszkodik sokat, ha null értékű hiva tkozást lalá l, és
csak akkor omlik össze, ha megpróbáljuk valamiképp használni ezt :1 hiv:ukozást. Nem
érdemes azo nban ilyesmit haszn.ílni, me rt ha másik számítógépre vagy másik fordít{r
progmmnak pr6báljuk átadni a progl"'d munkal, titokzatos hibák jelenhelnek meg null
éné kű bivatkozásainkból ered6en.

Cím szerinti paraméterátadás


Az 5. órnn a függvényekn é l tanu ltuk, hogy van két korlát juk: a paramétereknek csak
az értékét veszi 51 a függvé ny, a ret urn utasítással pedig csak egyetlen értéket lehel
visszaadni.

A dm szerimi pa,.!méLcrátadással mindkét korlátozáson felül lehet emelkedni. C++·ban


kéúéle módon lehe l eZl megvalósítani: mulatóval vagy hivatkozással. A szintaxis eltérő,
de a hatás ugyanaz: a függvény hat6körében éJVényes másolat helyett maga az e redeti
objekmm keni! átadásra. Ezzel lehet6vé vá lik, hogya függvény mcgváltoztassa a hivat·
kozott objektumot.

Mielőtt eZl megvi7_sgálnánk, tanulmányozzuk a 11.4 Listát, melynek swap () függvénye


érték szerint kapja meg pa'dmélereil.
202 1111. rész' M.m6riakezelés

11.4 lista -~"" _ _ 6boModás l


o: /1 11.4 Lista /1 trték szerinti paraméterátadás
1: Unclude <iostream>
2,
3: void 5wap(int x, int Y)i
4,
5: int main ()
6: (
7: int x = 5. Y '" 10;
8,
9, std : : cout « "Main . 8efore swap, x: • « x
10 : « y: «y« "\n ";
11 : swap(x,y) ;
12 : s t d : : cout « "Ma i n . After swap , x: ' « x
13 : « • y: ' « y « '\n ";
14 : roturn O;
15 :
16 ,
17 : void swap ( int x. int y)
18 , (
19 : int tomp;
20 :
.
21 :
22 :
std : : cout « ·Swap. Before swap,
« ". y , «. «x y « • \n" ;
23 :
24 : temp ."
25 :
26,
x
y .
• y,
temp;
27 :
28 : std : : cout « ·Swap. After swap, . «.
". y , x
29 : « « y « • \n";
30 :
31 :

Main . Before swap . 5 y , 10


Swa p . Before swap . " 5 y , 10
Swap . A f ter swap . " 10 y , 5
Main. After swap . " 5 y, 1 0
"

A program két változót inicializál a main ( ) -ben, amelyeket átad a swap () függvény-
nek. Ez arm lenne hivatott, hogy a két paraméter értékél felcserélje. Azonban
a main () -beli ismételt kiíratásból az derul ki, hogya változ6k értékei változatlanok!
11 .Ó!a" 203

A probléma abból ered, hogy csak érték szeri nt kapta meg a swap () függvény 3Z x és
y paramétereket; azaz csak helyi másolatok készültek a függvény számára a változ6k-
r61, 1\ megoldást a dm szerinti paraméterátadás jelenti.

C++-ban két út kínálkozik a probléma megoldására. A swap () -nek átadhatunk (az ere-
deti változókra utaló) mUlató pa,dmélereket, vagy ugyanezekre ól változ6kra való hivat-
kozásokat.

A swapO javltása mutatók segitségével


Amikor átadunk egy mutatót, akkor az objektum tényleges címélludatjuk a fi.igg-
vénnyel, ami ezek után rud vá ltozUllni az adott mem6riacímen leviS értéke n. Hogy
a megjavítolt swap() ténylegesen tudjon cserélni, használjunk két egb;z mUlatót para-
méterként. Ezen mutatók feloldása ul:Ín már ténylegesen felcserélhelő x ~S Y, amim azt
a 11.5 Lista is szemléltt:li.

11.5 UI1II- CIm szerinti paraméterátadú mU1lt6kkallpanllyplJ. pI


o: /1 11.5 List~ - Cim szerinti paraméterátadás mut~t6kka1
1 : 'include <iostream>
2,
3: void swap(int "x, int *y);
4: II the "*" saya that the function expects pointers

,.
5: int main()

7, int x • 5, Y = 10 ;
8,
9, std :; cout « "Main . Bcfore 9wap , x: " « x
10 ; « Y' « y « "\n" ;
11 , s wap (&x . &:y) ;
12 : s td: : cout « "Main. After s wap , x: « x
13 : « " y, " « y « "\n";
14 : return O:
15 :
16 :
17 : void swap (int *px, int *py)
18 :
19 : int temp;
20 :
21 : s td : : cout « ·Swap. Bcfore s wap , "px: " « *px
22 : « " *py : " « .py « • \n' ;
23 :
24 , t e mp = *px ;
25 : ·px *py :
26 : .py = temp ;
27 :
28 : std: , cout « " Swap. After s wap, " px: " « *px
29: « • *py: • « "py « "\ n " ;
30 :
204 1111. rész· Memóri8kezelés

lIImenet
Main. Before swap . x: 5 y : 10
Swap. Before swap. ·px : 5 * py : 10
Swap . After swap . *px : 10 *py: 5
Main. After swap . x : 10 y : 5

Sikerült! 1\ 3. sorba n a swap () deklarációját úgy alakitouuk, hogy kél egész szám he-
lyett kf:l egészre mutató par.lméten várjon. A swap () függvény hívásakor (a 11. sor-
ban) az x és y változ6k címeit adjuk át paraméterként.

fl19. sorba n egy helyi változót deklarálunk a swap ( ) -en belül (temp) . Ennek nem kell
mutat6nak lennie, hiszen csak a *px értékét fogja hordozni (;lZ:U~ a hív6 függvénybeli
x ~rtékét) a függvény élettartam,l során. A függv ény visszatérése után már nem lesz
szükségünk a tempore.

A 24. sorban a temp-hez hozzá rendeljük a px címen t:.tlá lh,lt6 értéket. A 25. sorban
a px címen áll6 értéket felülírjuk a py címen wlálható értékkel. A 26. sorban a temp-
ben ideiglenesen tárolt értéket (azaz a korábbi px értékét) beírjuk a py mem6riacímre.

Mindezek eredményeként a hívó függvény kél változ6jámtk értéke, melyekkel meghív-


ta a swap () függvé nyt, ténylegesen fe1cserél6dik.

A swap() újabb megvalósítása hivatkozásokkal


Az cl6z6 program mG'ködik, de a swap () szintaxisa két okból is nyögvenyel6s. Egy-
részt a swap () meghívása kor kétszer is ismételt címfeloldás hibalehet6ségekel hordoz,
és nehéz is olvasni. Másrészt az a kényszer, hogya szóban forg6 változ6k címeit kell
megadni a hív6 fOggvényben, felfedi a swap () megva!6sításának technikai részleteit
a felhasználók szá mára.

A C++ egyik célja, hogy a függvények használ6it megkímélje a konkrét mGködés meg-
i.~m e résé n e k fáradságá tól. Mutatók átadásának a megkövctdésc a hívó függvény fel -
használ6jál1l tesz többlet terhet, amit pedig nem az 6 dolga viselni; így a hívó félnek
kell fejben tartania , hogy a swap () számára a felcserélend6 változók címeit kell átadni.

A címzés-szemantika megértésének terhét a swap () megalkot6jának kell hordoznia.


Ezt hivatkozások használatávallehet megoldani. A 11.6 ü sta ennek megfelel6en fogal-
mazza át a swap () függvényt. A hívó fél most csak az objektumokat adja ál, de - minl-
hogy az elvárt paraméterek hivatkozásként vannak deklarálva - ezek hivatkozásként
értelmez6dnck. A hívó függvénynek semmi különösel nem kell tennie.
11.6", • Hivatkozások 1205

11.6 Lista - Az újra/rt sw,pO immé, hivadc02ésokkal (passbyraf.cpp)


o: /1 11.6 Lis ta
1: / / eim szer.i.nti szép paraméterátadás - hivatkozásokkal l
2 : *include cios t ream>
3.
4 : void s wup (i nt &x, int &y);
5 : /1 az ' &' jel mutatja, hogy a függvény hivatkozásokat vár
6 : int main ()
7: (

,,
8: int x =5,y=10;

10: std : : cout « 'Main. Before swap, x: • <c x


11: C< " y : • « y « '\n';
12 , 6wl.lp{X , y) ;
13 : std : : cout « 'Main. After swap , x: c< x
14 : « • y: • « y « "\n';
15 : return O;
16 :
17 :
18 , void swap (int &rx, int "ry)
19 : (
20 : int temp;
21 :
22 : std : :cout « ·Swap. Before swap, rx : • « rx
23: « • ry : • « ry « '\n' ;
24 :
25 : tcmp " rx ;
26: rx ry ;
27 : ry = temp ;
28 :
29: std : : cout « · Swa p . After swap, rx : • « rx
30 : « • ry : • « ey « '\n';
31:

Main . Before swap , x: 5 y : 10


swap . Betore s wap, rx : 5 ry : 10
Swap . After s wap, r x : 10 ry: 5
Main . After swap , x : 10 y : 5

Ahogya mutatókkal megval6síton példában, in is deklarálunk két változ6t a 8. sorban,


melyeket ki is írarunk a 10-11. sorban. A 12. sorban hívjuk meg a swap () függvényt,
melyben maga az x és y kerül átadásF.l, nem a címeil.... A hívó fél egyszeruen magukat
a válLOzókat adja át.
206 1111. rész· Memóriakezelés

Amikor a swap () meghívódik, a vezérlés a 18. sorra kerül, ahol a változók hivatkozás-
ként értelmez6dnek. Kezdeti értéküket kiírjuk a 22-23. sorban. Fontos, hogy semmi-
lyen operátort nem használunk az elérésükhöz, hbze n ezek az eredeti változók szi nű­
nimái, így ö nmllgllkban is használhatóak.

A 25-27. sorblln fclcscrél6dnek az é rtékek, melyeket a 29-30. sorban ki is nyomtatunk.


A. vezérlés visszakerül a f6programba, és a 13-14. sorban a main () is kiírja :l változók
énékeit. A swap () pamméterei hivatko7..áskéntlette k deklarálva, így a main () -heli ér-
tékt:!k átadáSa is így történt; emilltl értékváltozásuk a main () -ben is megm;wddt.

A. hiv:ltkozások kényelmet és könnyű használhatóságol kölcsönöznek a normál válto-


z6knak, II mutatók cím szerinti paraméterátadási ké pességének le he t6ségével és e rejé-
vel felvértezve.

A függvények fejlécei és prototfpusai


Hivatkozásokat is elfogadó függvényekkel könnyebb az élet, és a kód is olvashatóbb,
de honnan tudja a függvény meghívója, hogy érték szerinti vagy cím szerinti paramé-
terátadás történik? A swap () -et használó programozónak tudnia kell , hogy ez a függ-
vény ténylegesen hozzá fog-e nyúlni a p;munéterekhez.

Ez a függvény deklarációjából (prototípusából) derül ki , amely többnyire a fejlécállo-


mányban található a többi deklarációval egyOtt. Az ou szerep l ő paramétereket megte-
kintve egyb61 látszik , hogy az adott függvény cím szeri nti par.Jméterátadással dolgozik;
így meggy6z6dhel arról a programozó, hogy tényleges cserét várhat II swap ( ) -t61.

Ha a swap () egy osztály tagfüggvénye [enne, akkor az osztály-deklarációból (amely


szil1tén a fejlécállomá nyban található) lenne kinyerhető ez az információ.

C++-ban a z osztá lyok ügyfelei (azaz valamely más oszt::ily tagfüggvényei, melyek
az adott osztá lyt használni sze retnék) bízhatnak abban, hogy a fejlécál1ományokban
mindent megtalálnak , amire szükségük van . Ez jelenti az osztály vagy a függvé ny keze-
16felületé1. A tényleges megva lósítás el van rejtve az ügyfélprogramok elől. Ez segíti
a programozót abban, hogya lényegre koncentrálhasson, és anélkül haszná lhassa
az adott osztályt vagy függvényt, hogy ismerné II pontos megvalósítását.

Több érték visszaadása


Ahogy korábban emIíteuük, a függvények csak egyetlen visszatérési értékkel rendel-
kezhetnek. Mit tehetünk akkor, ha két értéket szeretnénk visszakapni egy függvényt61?
A probléma úgy orvosolható, hogy két paramétert adunk át neki cím szerint. Ezeket
a fü ggvény [eltöltheti a megfelelő értékekkel. A cím szerÍllt paraméterátadás felruházza
11. 6ra • HivatXozás<>k I W7

a függvényt azzal a képess6ggel, hogy ténylegesen több értéket adjon vissza. Ebben
a megközelítésben a függvény eredeti vissz,n6r6si értéke más szerepbe kerül; használ-
ható pt:!ldául hibajelzés küldésére.

A dm szerinti paraméterátadás tehát mUlató vagy hivatkozás segítségével történhet.


A 11.7 Lista eb')' olyan függvényt mutat be, amelynek három visszatérési értéke van; két
mlLral6 paraméter plusz II Chibajelzésre használt) klasszikus visszatérési érték.

11.7 Usta - VisszatérNi értékek többsz6r6zltse mutatókkal (returnwithptr.cpp)


0, 1111.7 Lista
1: II FOggvények tObbszörös visszatérési értéke mutatókkal
2: linclude <iostream>
3,
4, short Factor (int, int", i nt*) ;
5,
6, int main!)
7,
8, int number, squared, cubed;
9, short error;
10:
ll: std : : cout « 'Enter a numbar (o - 20) : .,
12 : std : : cin » number;
13 :
14 : error = F'actor(number, &squered . &cubcd);
15 :
16 : i f (!errorl

...
17 : {
18: std: : cout « "number: « number « • \n" ;
19: std: : cout « "square: « squared « "\n' ;
'"O : std: : cou t « ' cubcd : « cubed « • \0' ;
21:
22 : e l se
'-3 , std::cout« "Error encountered !! \n";
2 4: return O;
7.5 :
26 :
27 : short Factor(int n, int *pSquared , int *pCubed)
2S :
29: short Value = O;
30 : if (n > 20)
31: value = 1 ;
32: else
33 :
34 : ·pSquarcd = n*n:
35 : *pCubed n*n*n:
36: Valuc '" O;
37 :
38 : re t urn Value;
39 :
1
20e III. rész • Mem6riakezelés

Enter a number (O-20) : 3


number: 3
square : 9
cubed: 27

A 8. sorban lélrehozzuk a number, squared és a cubed (szá m, négyzete, k6be) egész


változókat. A number--nek a felha sználó bemenő adata ad értéket. Ezt a számot, vala-
mint a squared és a cubed címét átadjuk a Factor () függvénynek.

A Factor () megvizsgálja az érték szerint áladolt els6 p;mun éterl. Ha ez nagyobb


húsznál (v:lgyis a maximumnál, amit ez a függvény még kezeln i ~ llld "), akkor II vissza-
térési vá ltoz6t (va.lue) egy egyszelű hibajelz6 értékre állítja. rigycljük meg, hogy
a Factor () visszatérési értéke vagy ez a hibajelz6 érték, vagy nulla (ami azt jelzi, hogy
minden rt..:ndben zajlott). A 38. sorban kerill visszakoldésre a Value ért6kc.

A két ténylegesen várt ~v isszatérési érték~, a szá m négyzete és köbe nem a return ré-
vén jut vissza a fl5program ba, hanem a fuggvénynek átadott mut..tók révén lehet6vé
vált közvetlen C:n6kvá ltoztatással.

A 34. és 35. sorban bekerülnek a kiszámított értékek a mutatók álUl! hivatkozott mcm6-
riacímekre. A 36. sorban a visszatérési énéket (value) sikeres állapotra állíljuk, és a 38.
sorban vissz..küldjük.

A progmmot továbbfejleszlhetjOk azzal, hogy felvesszük az al ábbi deklar.'lci6t:


enum ERROR-VALUE ( SUCCESS, FAILURE):

Ezek után már nem O vagy l értéket kellene visszaadnia a t"üggvénynek, hanem
SUCCESS-t (sike,t) vagy FAILURE-t (híbtit). Ahogy korábban láthattuk, II fcl sorolásos tí-
pus első eleme (SUCCESS) O, második eleme (FAILURE) l értéket vesz föl.

Több visszatérési érték használata hivatkozásokkal


Bár a 11 .7 Lista programja működik, könnyebb olvasni és karbantartani, ha mutatók
helyett hivatkozásokkal dolgozunk. A 11.8 Lista mutatja az átín programot, amely már
hivatkozásokat használ, valamint szebb hibakezelést valÓSít meg az ERR-CODE révén.
". ó.. • Hivatkozások 1209

11.81.is1a - A 11.71.is1a úlnlrúa: _ _ ""'"'" _ ... h i t _ a l


(-..withnof.cppl
o : /I 11. 8 Lista
l ; 1/ FOggvények felruháloása
2 : II tObbszOrös visszatérési értékkel hivatko zások révén
3: tincludc <iostream>
••
5: enum ERR-CODE ( SUCCESS, ERROR ) 1
6.

,.
7: ERR-CODE Factor(int , int&, int&) ;

9: int m",in()
10 : {
ll : int number , squared , cubed :
12 : ERR-COOE res ult :
13 ;
14 : std : : cout « "Enter a number (O - 20) :
15 : std : : cin » number ;
16 :
17 : re!lult • Factor(number , squared , cubed) ;
18 ,
19 :
20 : ,
i f (reGult .. SUCCE$S)

21 : std : : cout « "number : « number « • \n ";


22 : std : : cout « ·square :
23 : Btd : : cout « ·cubed: . «
«
squared « • \n" ;
cubed « • \n';
24 :
25 : else
26 : std :: cout« "Error encountered!!\n" ;
27 : return O,
28 :
29 :
30 :
3L ,
ERR.....CODE Factor (int n, int &rSquared, int &rCubed)

i f (n > 20)
32 :
33 : re t ur n ERROR; II simple error code
34 :
35 : ,
else

36 : r Squared :: n *n ;
37 : r Cubed '" n"n*n ;
38 : return SUCCESS ;
39 :
40 :

Enter a number (O-20) : ]


nwnbe r : ]
square : 9
cubed : 21
210 1111. rész • Mem6riakeze~s

A 11.8 ü sta megegyezik a 11.7 Listával , kél kivételle1. Az ERR-CODE felsorolásos típus
átlálhat6bbá teszi a hiba jele ntéseke t (33. és 38. sor) , valamint a 19. sor hibakezelésél.

A jelentősebb vá ltozás azonban abban áll, hogya Factor () függvényt most úgy dek-
laráltuk, hogy mutatók helyett hivatkozásokal vár a squared és a cubed helyére. A pa-
mméterek kezelése íh'Y sokkal egyszerűbb és érthetőbb.

Kérdések és válaszok
Kérdés: Miért IJtlsználjl/llk hivatkozásoka" Ita cl mutat6k mindarnl képesek, amire
CI hfvatl.lozások?

Válasz: A hivatkozásokat könnyebb használni és megérteni. A címfeloldás rejtenen tör-


ténik, nincs szükség isméte lt címfeloldó operáto rokra .

Kérdés: Aná,., hasz/lálj/.m k akkor /IIuUllókat, ha egyszerl1bbek CI lIiv(l lkoz!üok?

Válasz: A hivatkoz{!sokat nem lehet nu llázni, sem pedig újra felhasználni. A mutatók
rugalmasabbak, de valamivel nehezebb a használatuk.

Gyakorlatok
Most, hogy megismerkedfÜnk a hivatkozások használatával , válaszoljunk meg néhány
kérdést és végezzünk el néhány feladatO[ Uldásunk e l lenőrzésére!

Kvfz
1. Mi az a hivatkozás?
2. Milyen operátorral hozunk létre egy hivatkozást?
3. Mi egy hiv"koz's mem6"ocime'
4. Mi a függvények alapértelmezett paraméterátadási módszere a C++-ban? Milyen
módon lehet ennek korlátait átlépni?

Feladatok
1. Egyesítse a passbyptr. cpp és a passbyref. cpp (11.5 és 11 .6 Lista) programja-
iban használt módszereket úgy, hogy az egyik é rtékel mutató, a másikat hivat-
kozás segítségével adja át a swap () függvénynek! Ez jól mutatja, hogy e kél
módszer teljesen átjá rható .
l1.6ra • HivatkOLls.kl 211

2. Módosírsa úgy a returnwithptr. cpp programot (11.7 Lista), hogy mutatók he-
lyeu hivatkozásokat használjon!
3. Bontsa három részre a returnwithptr. cpp programot Cl 1.7 Lista) oly módon,
hogy HZ eredeti néven maradjo n meg a főprogram (returnwithptr . cpp),
a Factor () függvény megv.d6sítása kerüljön át egy külön fájlba (factor. cpp),
és legyen egy fejlécállomány is a Factor {} függvény proTolípusával
(fa ctor . hpp). Vegye fel a factor. cpp-t a projektállomá nyok közé, (:$ fordítsa
le a progmmot! Ez azt szemlélteti, hogy hogyan lehet megoszta ni a fü ggvény- és
osztálykönyvtárakat. A függvény lefordítható és a gépi nyelv{[ progmmkód a fej-
lécállománnyal (bclUle a ruggvényprototípussaD együtt közzélehe16 a termelé-
kenység javítására.

Válaszok a kvfzkérdésekre
1. A hivatkozás valamely váltOl6 vagy obje ktum szinonimája, névvá ltolat.1,
2. Al ~és" jel (&) használatávallehec hivatkozást deklarálni. A hiv:ltkozásokat inida-
lizálni kell a deklaráláskor. Nem lehet nu ll hivatkozásu l,k, csak le nulIázott mu-
tat6nk.
3. A hivatkozás címe annak a változónak vagy objektumnak a címe, amelyre utal.
Ha :1 . cím(/' operátorl alkalmazzuk egy hivatkozásra, akkor (a mutat6kkal ellen-
tétben) úgy tűnik , hogy semmi helyet sem foglal el a memóriában.
4, Az é rté k szerinti pamméter:'uadás. Ilyenko r az átadott vállOZó másolatával dolgo-
zik a függvény, így nem lehet felülírni az átadott változ61. Ezen kétféle m6don
le het felülemelkedni: egyrészt a mutatók használatával, ugyanis ilyenkor az ob-
je ktum címe kerül átadásra. Másrészt hivatkozások átadásával, hiszen ezzel
az eredeti változó névvá llozatál kapja meg a filggvél1y.
12. ÓRA

A hivatkozásokról és mutatókról
- mélyebben
Ebben az 6rában a következőkrőllesz sz6:

• llogyan tehetjük hatékonyabbá programjainkat cím szerinti paramétecitadással


• Mikor érdemes hivatkozásokat használni, és mikor mutatókat
• Hogyan Icgyiln k úrrá a mcmóriagondokon a mutatók ha.~ználata közben
• Hogyan kerüljük el a hivatkozásokka l kapcsolatos csapdákat

Hatékonyabb a cfm szerinti paraméterátadás


Az érték szerinti paramétcrát:tdáskor másolat készül az objeklumr61. Ha a függvény
visszatérési értéke egy objektum , akkor megint egy újabb másolat keletkezik.

Nagyobb mércrú, felhasználó általlétrehozou objekrumok esetén ezeknek a másolások


a száITÚtásigényc már igen számottevő is lehel. A szükségesnél több memóriát használ
a program, és végs6 soron sokkal !assabban fut.
214 1111. rész • Memóriak.,.lé.

Egy, a felhasználó által definiált objektum mérete a veremben az egyes tagváltozók


méretének összege. Ezen tagvállozók akár nagy objektumok is lehetnek. Egy efféle gi-
gantikus struktúra átadása (azaz a verembe másolása) igen sokba kerülhet memória és
futási idő vonatkozásában.

Másféle költségek is fellépnek ilyenkor. Az oszt{llyok pélclányosításakor készül róluk


t'b'Y ideiglenes másolat; ilyenkor a fordít6progmm meghív egy speciális konSlnlktOI1:
a másoló konstruktort. A 13. órán, a Ixmyolulwbb függvényeknél megtanul juk, hogy
hO!''Yan működnek ezek a másoló konstnlktorok, és hogy hogyan készíthetünk ma-
gunknak ilyeneket; most azonban elég annyit tudnunk, hogy egy objektum ideiglenes
másolatának a verembe másolásakor meghív6<.lik a másoló konstruktor. Amikor :1 függ-
vény kilépésekor véget ér ennek az ideiglenes másolatnak az éleuartama , az objektum
destll.lktora hívódik meg. Ha egy objektumot érté~ szerint adunk vissza, akkor ennek
az objektumnak II m{lsobtát létre is keH hozni, majd törölni is kell.

A konstruktorok és dCSlruktorok meghívása nagy objektull1okkal költséges lehet a se-


besség és a memóriah<lsználallcrén. Ennek illusztrálására a 12.1 Listában létrehozunk
egy lecsupaszított, felhaszná ló által definiált objektumot, a SimpleCat-et. Egy igazi ob-
jekulm sokkal nagyobb és még kölL'iégesebb lenne, de annak szemléltetésére az is elég
lesz, hogy milyen gyakl"'dn hívódik meg a másoló konstl1Jktor és a deslruktor.

A 12.1 Lista f6program ja IéIrehozza tehál a Simpl eCat objektumot, majd meghív kél
függvényt. Az els6nck érték szerint adja át a Cat -et, és éJ1ék szerinti kapja is viss:tll CZl.
A másodiknak cím szerint adhatja ál paraméterét, azaz a f'Üggvény az objektum mutató-
ját veszi ,It az objektum helyett, és ugyanezt kapja is vissza. A cím szerinli par::unéter5t-
adással kiküszöbölhetó a másolat létrehozása és a másoló konstruktor meghívása, és
így általában sokkal hatékonyabb a progl"'dnl. Másrészt ezzel valóban magát az eredeti
objektumot kapja meg a hívott függvény , és így lehet6sége van változtatni is rajla.

o: II 12 .1 Lista
l: 1/ Obj ektum mutat6j ána k az átadása
2 , winclude <i ost rcam>
3,
4, class sjmpl eCat
5: {
6: public :
7: SimpleCat () ; II constructor
8: SimplcCat (Simp l€Cat&) ; II máso l 6 ko n strukt o r
9: -Simp l eCat() ; II des truktor
10 : J ;
ll :
12: SimpleCat : : SimpleCa t ()
13 : {
14 : std:: cout « "Si mpl e Cat Constructor .. . \n";
15 :
16 :
12. 6ra • A hivatk.omsokfól és mutat6kr61-

17 , SimpleCat , , SimpleCat(Simp1eCa t &)


18 , (
19 : std , , cout « "Simple Cat Copy Constructo r ... \n ";
20 ,
21 ,
22 , SimpleCat , , -Simplecat ()
23 , {
24, std , , cout « "Simple Cat Destructor. .. \n" ;
25 ,
26,
27 : Simp!eCat Punctionane (SimpleCat theCat) ;
28 , simplcC~t· FunctionTwo (SimpleCat *theCat) ;
29 :
30, int main()
31 ,
32 : std , : couL « "Making a cat. .. \n" ;
33 : SimpleCu t Frisky;
34: atd : : cou t « "Calling FunctionOne ... \n ";
35 , FunctionOnc(frisky);
36 : std , : couL « "Calling Function'l'wo ... \n" ;
37 : FunctionTwo(&Frisky);
38 : return O;
39 :
40,
41 : II FunctionOne. érték szerinti paraméterátadással
42 : SimpleCat ~'unctionOne(Simp1eCat theCat)
4] : (
44 : std: : couL « 'Function One. Returning ... \n";
45: return theCat;
46:
47 :
48 : II functionTwo, passes by re ference
49: Simplecat* FunctionTwo (Simp1cCat ·theCat)
50 : {
51 : std :: cout « 'Punction '!'wo . Returning ... \n" ;
52 : return thcCat ;
53 :

1: Making a cat ...


2: Simple Cat Constructor .
3: Cal l ing FunctionOne ...
4: Simple Cat Copy Con s t ructor .
5: Function On e . Returni ng ...
6: Simple Cat Copy Constructor .
7: Simple Cat Destructor . .
8: Simple Cat Destructor .. .
9: Call ing PunctionTwo .. .
10 : Function '!'wo. Rcturning . .
ll : simple Cat Destructor ...
216 1111. rész· Memóriakezelés

Apropó Sorszámok akimenetben


Az itt látható sorszámok nem jelennek meg a képernyőn, csak az elemzés meg-
könnyítése végett tüntetjük fel őket.

Egy tdjesen lecsupaszított SimpleCat osztályt deklarálunk a 4-10. sorban.


A konstruktor, a másoló konstmktor és a destruktor mindössze egy-egy tömör üzenet-
leJ jelzi a meghívását.

A 32. sorban a main () értesít a macska létrehoz..'isáról , melyel a kimenet 1. sorában lát-
haTtlnk. A 33. sorban val6ban létrejön egy simpleCat péld{IllY, mégpedig
a konstmktor révén, melynek üzenete a kimenet 2. sorában olvashat6.

A .,4. sorban a main () jelzi, hogy kthizül a FunctionOne () meghívásÍIra (ez a kimenet
3. sora). Mivel itt érték szerinti pamméterátadás rörtónik, a simpleCat mÍlsolatát keH
létrehoznia a programnak a veremben, hogy legyen a fi.lggvt!l1ynek egy saját, helyi ob-
jektmna. Emiatt ismét meg1Lívódik a másoló konstruktor (ez a kimenet 4. sor:a).

A vezérlés a 44. sorra keri.il, :1 hívott függvény tönsébe, ahol egy nyomjelz6 üzenet író·
dik ki (a kimenet 5. sorában). A függvény kilép, és érték szerint ad vissza egy Simple-
Cat objektumot. Emi:llt ismét meghív6dik a másoló konstruktor (:1 kimenet 6. sorában).

A FunctionOne () visszatérési értéke nem rendel6dik hozzá semmilyen változóhoz,


így a (vissz.1térési értékként) ideiglenesen létrejön objekrumot el kell dobnunk , :unit
a destmktor végez el (ez a kimenet 7. sora). Ekkor a Funct ionOne () egyúttal ki is lép,
így a számáro. készült helyi objektummásolat érvényét veszti, és ismét munkába lép
a destruktor (a mit a kimenet 8. sara mutat).

A vezérlés visszakerül a fóprogmmba, és (a kimenet 9. sora szerint) meghív6dik


a FunctlonTlo.>o (), melynek paramétere már cím szerint adódik át. Nem készül máso-
lat, így nincs is err6l üzenet a kimenetben. A FunctionTlo.>o{ ) hírt aci saját rutásár61
a kimenet 10. sorában, majd visszaad egy SimpleCat objektumot cím szerint (így most
sincs nyoma konstnlktof vab'Y destruktor hívásának).

Végül II program kilép, Frisky érvényét veszti és meghívja a sorsát megpecsételő


destmktort, amit a kimenet utolsó sora nyugtáz.

Összességében elmondható, hogy a FunctionOne () hívása az érték szerinti paramé-


terátadás és visszaadás miatt két konstruktor- és két destruktorhívást igényelt, ezzel
szemben a FunctionTwo () hívása egyet sem.
12. 6ra • A hivatkozásokr61és mutat6kr61- mélyebben 1217

Mutassunk konstansra
Bár kétségkívül hatékonyabb dolog mutal6t átadni FunctionTwo () *nak, ez nem teljc*
st:n veszélytelen . Ettől a függvényt61 ugyanis nem várjuk az átac10tt SimpleCat () ob*
jektum megv{lltoztatását, mégis át.adjuk neki a dmét. Ez Ichetőv6 tesz akár tiltott vá[*
toztatás! is, és meghiúsítja az érték szerinti param~leráladás kínálta védelmet.

Az érték szerinti pamméterátadás hasonlít ahhoz, mint amikor valaki réltve őrzöu mú-
kincse helyett annak fényképét adja a múzeumnak. I-Ia a vandálo k meg is rongálják,
az eredetit semmi károsodás nem éri. A dm szerinti paraméterátadás ezzel szemben
olya n, minth:1 :1 l:lkcimünket küldené nk el a mÍlzeumnak azza l a biztatással, hOb'Y irá-
nyítsák csak hozzánk nyugodtan a látogatókat, hogy mcgnézhessék az eredeti kincset.

J-Ja egyszerre szeretnénk élvezni az é rték szerinti paraméterátadás biztonságát és a cím


szerint.i paraméterátadás haté konyságá t, konstans mutat6val kelJ átadnunk a Sil1\ple
Cat objektumot. Ez meggátol nlinden nemkonstans metódushívásI, így ke1!6 védelem-
ben részesül SimpleCat objekrunmnk. A 12.2 Lista ezt mutatja be.

o: II 12 . 2 Lista
l : II Objektum konstans mutAtójának az átadása
2 : .include <iostream>
3,
4: class Simpl eCat
5: (
6: public :
7: SimpleCat () ;
8: simpleCat(SimpleCat&) ;
9: -S impleCat();
10 :
ll : int GetAge () const return itsAge ; }
12 , void SetAge(!nt Age) ( itsAge = age; )
13 :
14 : private :
15 , int itsAge;
16: };
17 :
18 : SimpleCat: : SimpleCat ()
t9 : {
20 : std : :cout« ·Simple Cat Constructor ... \n" ;
21 : itsAge = l;
22 :
23 :
24: SimpleCat : : SimpleCat(Simplecat&)
25 : {
26 : std : :cout« "Simple Cst Copy Cons tructor ... \n ";
27 :
28 :
218 1111. réSZ· M,m6ri.k",lés

29 : SimplcCat : : -Simpl eCat ()


30 : (
31 : std :: cout« 'Simple Cat Destcuctor ... \n' l
32 :
33 :
34 : const Simplecat * const
35: F'unctionTwo (cons t SirnpleCa t * const theCat);
36:
37 : int main()
38 :
39 , std : : cout « 'Making a cat .. . \n" I
40 : Sirnpl eCat Fr isky l
41 : std : : cout « 'Frisky is "I
42 : std :: cout« Frisky .GetAge() « "ycars old\ n"1
43 : int age" 5 ;
d4 : r·cisky . SatAge(age) ;
45 : std ,: cou t « 'Frisky i s 'I
46 : !:Itd :: cou t « Fri s ky . GetAgc( ) « • yca r s o l d\ n"1
47 : otd : : cout « "Call i ng Fun ctlon'T'wo ... \n " ;
48 : FunctlonTwo(&Frisky) ;
49 : std :: cout « " Frisky is ";
50 : std : :cout « Fris ky . GetAge() « ' years old\n" ;
51 : return OI
52 :
53 :
54 : II functionTwo, konstans mutatót kap és ad vissza
55 : const SimpleCat .. const
56 : FunctionTwo (const Simplecat * const theCat)
57 : {
58 : std : :cout« "Functlon Two . Returning ... \n" ;
59: std: : cout« "Frisky is now " « theCat->GetAge()I
60: std : :cout « ' years old \n' ;
61: II theCat->SetAge(8); const!
62: return thecat;
63:

Making a cat ...


Simple Cat Constructor ...
Fris ky i s 1 years o l d
Fris ky i s 5 years o l d
Calling Fun ct i onTwo ...
Function Two . Returni n g .
Fris ky i s now 5 years old
Frisky is 5 years old
Simple Cat Destructor . .
12. 6ra' A hivatkozásokr61 és mutatókr61 - mély.bb.n 1219

A SirnpleCat számára két hozzáfér6 függvényt deklaráltunk: a GetAge () -el a 11. sor-
ban, amely konstans függvény , és a SetAge () -et a 12. sorban, amely nem az. Van egy
saját tagváltozója is, a 15. sorban deklarált egész itsAge.

A konstruktor, a másoló konstn Jktor és a destrukwf úgy lett megírva, hogy kiírja a saját
nyomje\z6 üzenetél. A másoló konstruktor azonban sohasem kerül meghívásn:I; prog-
ramunkban ugyanis csak dm szerinti paraméterátadás tönlmik, és ehhez nincs szükség
másolat készítésére. A 4Q. sorban létrejön egy macska; az alapértelmezeu életkorát ki is
nyomtat ja a 41-42. sor.

A 44 . sorban a SetAge () segítségével átá!1íljuk a korát (it~Age) 5-re, és az en..>(lmf:nyt


megjelenítjük a 45-46. sorban. A FunctionOne ( ) -l nem haszn:íljuk ebben a program-
ban, csak a FunctionTwo () -t, amelyet egy kicsit átírtunkj a 34. és 35. sorban láthatjuk
visszatérési értékét és átadand6 paraméter6t: mindkett6 egy konstans objektumra utal6
konstans mutató.

Minthogy az átadandó pamméter és a visszatérési érték is cím szerint kerUl ,ítad:'isl"""d ,


nem készülnek m:'isolatok, és nem hív6tlik meg a másoló konstmktor. A Function-
TwO () -beli mutató azonban konstans, így nC:!m lehet bel6le meghívni a nemkonsta ns
SetAge () -et. Ha az ezt megkísérl6 61. sort kivesszük a megjegyzésb61, nem fordul Ic
a program.

Érdemes megIIgyeini, hogy a main () f6programban létrehozott Frisky objektum nem


konst:ms, így például átáJlítható a kora, meghívhat6 rá a Setll.ge () . Ennek
a nemkonstans Frisky objektumnak a eimét kapja meg a FunctionTwo (), és mivel ez
úgy lett dC:!klarálva, hogy konstansra hivatkozó ll1utat6ra számít, az objektumot kons-
tansként fogja kezelni!

Hivatkozások, mint a mutatók kényelmes altematfvái


A 12.2 Lista megoldja a felesleges másolás (és ezzel a másoló konstmktor és destruktor
indokolatlan meghívásá nak) problémáját. Konstans objektumra mutató konstans muta-
t6t használ, így nem kell tartanunk att61, hogya függvény jogosulatlanul megválloztatja
a paraméterként átadott objektumo{ka)t. A módszer azonban még mindig nem tú l ele-
gá ns, mivel mégiscsak mutatókat kell átadnunk a függvénynek.

Mivel azt is tudjuk, hogy az átadandó paraméterek nem lesznek null értékűek ,
könnyebb lenne az élet, ha mutatók helyett hivatkozásoka t adhatnánk át. A 12.3 Lista
ennek szellemében korrigálja a 12.2 Listát.
220 1111. rész· Mem6riakezelés

o: II 12.3 Lista
1: II Objektum hivat ko zásának az átadása
2: ftinclude <lostream>
3,
4: class SimpleCat
5: {
6 : public :
7: SjmpleCat() ;
8: Simp leCa t (SimpleCat&) ;
9: -simp1eCat() ;
10 :
ll: int GetAge () co n st retur n itsAge : }
12 : void SetAgc( int age) { i tsAge • age; )
13 :
14 : private :
15 : int itsAge:
16 : );
17 :
18 : simpleCat : :Simp1eCat()
19 : (
7.0 : std : : cout « 'Simple Cat Constructor ... \1'1 ';
21 : itsAge = 1 ;
22:
23:
24 : SimpleCat: :Simplecat(Simp1eCat&)
25 : (
26 : std: : cout« "Simple Cat Copy Constructor ... \n";
27 :
28 :
29 : Simp1eCat : : -Simplecat ()
30 : {
31 : std : : cout « 'Simple Cat Destructoc .. . \n' ;
32 :
33 :
34 : const SimpleCat & Func tion'l'wo (const SimpleCat & theCat) ;
35 :
36 : int main()
37 : (
38 : std :: cout« 'Making a cat ... \ n ';
39 : Simpl eCa t Fr i s ky;
40: std : : cout « "Frisky is ' « Frisky . CetAge()
41 : « ' years 01d\n' ;
42 :
43 : int age", 5;
44: Frjsky.SetAgc{age) ;
45: std : : cout « 'Frisky is ' « Fri sky . GetAge()
46 : « ' years 01d\n' ;
47 :
48: std : : cout « 'Ca11ing FunctionTwo ... \n" ;
49 : FunctionTwo(Frisky) ;
50 : std : : cout « "Frisky is " « Frisky.Geti\ge()
12. 6ra • A hi_omokról és mutat6król- mé~ebb,n 1221

51: « • years old\n" ;


52 : return O;
53 :
54 :
55 : 1/ functio nTwo. konstansra utal6 hivatkozást kap és ad vissza
56 : const SimpleCilt & FunctionTwo (const SimpleCat (. theCat l
57 : (
5B : std :: cout« ' Punction Two. Returning ... \n' ;
59 : std : : cou t «'Frisky is now ' « theCa t . GetAgc()
60 : « • years old \n ";
61 : /1 thecat . SetAge(B); const!
62 : return theCat;
63 :

Making él ca t . ..
Simple eat constructor ...
Frisky lS 1 yoars o ld
Frisky is 5 years old
Calling FunctionTwo
FunctionTwo . Returning ...
Frisk.y is now 5 years old
frisky is ~ years old
simpl e eat Destructor ...

A kimenet megegyezik a 12.2 Listáéval. Az egyetlen emJílt!sre mélt6 külónbség az,


hogy most a FunctionTwo () függvény konsta ns objektumra utaló hiv,ltkoz{lsl vár és
ad vissza. Ezen II példán is láthatj\lk, hogy hivlltkoz.'isokkal könnyebb dolgozni, mint
mutat6kkal ; a ko nstansok által elérhct6 nyereség, hatékonyság és hizlonság azonban
itt is megval6su1.

Mikor használjunk hivatkozásokat,


és mikor mutatókat?
A C++ programozók általában sokkal szívesebben használnak hivatkozásokat, mint
mutat6kat, mivel ezek átláthat6bbak és hasznáJhat6bbak. A hivatkozásokhoz azonban
nem lehet új objektumot hozzárcndeJni. Ha a program futása közben változtatni kell
II célobjektumon, kénytelenek vagyunk mutatót használni. A hivatkozás nem lehet
null értékű; így, ha van esély arra , hogy acélobjektum nul1áz6dik, akkor sem tudunk
hivatkozást használni, csak mutatót. Ha a dinamikus memóriából szeretnénk területet
foglalni, akkor is mUlatókra van szükség, ahogy arról az előző órákon már szó volt.
222 1111. rész' Memóriekezelés

Ne adjunk vissza megszűnt objektumra mutató


hivatkozást!
Ahogya C++ programozók megtanulják a eim szerimi par.lméterátadásl, vérszemet
kapnak, és id6nként hajlamosak túlkompenzálni ifjúkori lévedéseikel. Ne lévesszük
szem eid!, hogya hivatkozás csupán egy névvá llOZ<ll. amely egy másik objeklumra
utaL I la egy hivatkozást adunk vagy veszünk át egy függvé nyt6J, mindig kérdezzük
meg önmagunkat: . Milyen objektumot álcázok is most? U:tezik az még egyá l lalán?~

A 12.4 Lista rámutat, milyen veszélyes is lehet egy már nem létező objektum hivatkozá-
sának II visszaadása.

12.4 Lista - Nemlátezli objektum hivatkozáiának vi81zaadás. (returnref.cpp)


0, II 12. 4 Lista
l : II Már nem létezo objekLwnra mutat6
2: 1/ hivatkozás visszaadása

,3:, 'include <iostream>

5: class SimpleCat
6: {
7, public:
8: SirrrpleCat (int age, int weight);
9: -SimpleCat () (l
10: int GetAqe() ( return itsAge; )
ll: int GetWeight() ( return itsWci9ht;
12 : private :
13 : int itsi\ge ;
14 : int itsWeight ;
15: ):
16 :
17: SimpleCat :: SimpleCat(int age, int weight) :
18 : itsMe (age), itsWeight (weightl {)
19:
20 : SimpleCat &TheFunct ion() ;
21 :
22: int main()
23 :
24: SimpleCat &rCat = TheFunction() ;
25 : int age = rCat . GetAge() ;
26 : std :: cout« "rCat is • «age « • years old!\n" ;
27 : rcturn O;
28 :
29 :
30 : SimpleCat &ThcFunction ()
31 : (
32 : SimpleCat Pr isky (5 ,9 ) ;
33 : rcturn Frisky;
34 :
I és mutatókról

A 12A Lista rordításakor az alábbi hibaüzenenel szembesülünk


"returnref.cpp· , E2363 Attempting to return a reference to local variable
'Frisky' in function TheFunction() at line 3]

(Azaz; " returnref.cpp~: E2363 A 'Frisky' helyi változóra utaló hivatkozás visszaadási kí-
sérlete a 111cFunctionO füru,'v6nybcn, a :$3. sorban)

Figyelem! Intelligens fordft6programok


Vannak intelligens fordft6programok, melyek észreveszik a nemlétező (null ) objek-
tumra utaló hivatkozást, és hibát jeleznek, mint ahogy ez itt is látszik. Más fordít6k
azonban ilyenkor is lefordftják a programot, és elöállítják a futtatható kódot. Fontos
tisztázni, hogy itt az alkalmazott programozási gyakorlat a rossz, vagyis nem érde-
mes olyan fordítót keresni, amelyikkellefordul az ilyen program. A Borland és
a g+ + például okos fordft6programok. Hibát jeleznek, ahogy az fent is látszik.

Az 5-IS. sorban deklaráljuk a SimpleCat osztályt. A 24. sorm.k az lenne a feh.data ,


hogy létrehozzo n egy o lyan hivatkoz;'ist, amelyet a TheFunction () visszatérési érték€:-
"el inici<lliz.1 Iunk. Ezt a 20. sorban ügy dcklar-Jltuk, hogy egy SimpleCat hivatkozást
adjon vissza.

A TheFunction () töm;e lé trehozza a SimpleCat egy helyi példányát, és inicializálja


anml k korát és sú lyál. Ez a helyi objektum lenne a cím szerinri vissz:nérési é rté k.
A jobb fordít6progrmnok elcsípik ezt a kísérletet és nem hoznak létre futtat ható prog-
ramot, míg Illások lefordít ják, pedig megjósolhatatlan a futás kimenetele. Amikor
a TheFunction () kilép, Frisky (bizton állíthatom, fájdalommemesen) megszűnik lé-
tezni. A függvény által visszaadott hivatkozás nemJétez6 objeklurnra utal, ami rossz.

A dinamikus memóriaterületen létrehozott


objektumra mutató hivatkozás visszaadása
Szeretnénk kijavítani a működésképtele n returnref . epp programunkat (12.4 LiSL1).
Csábít6 lehetóség, hogya TheFunction () segíL'iégével a dinamikus mem6riaterületen
hozzuk létre F'risky mllcskát, hiszen ily módon életben marad a függvényból val6
visszatérés után is.

Ezzel a megközelítéssel élve azonban felmerül a kérdés, vajon ki gondoskodik majd


224 1111. rész • M.m6ri.k".~s

a Frisky által lefoglalt mem6riaterületról, ha már nincs rá szükség. Ezt példázza a 12.5
Lista.

O, I I 12. 5 Li s t a
1: II A mem6riaelszivárgás javí tá sa
2: II includ c <iostream>
3.
4: clas s SimpleCat
5: {
6: public :
7: SimpleCat (int age, int weight) ;
8, -SimplcCat () ()
9: int GetAge() { rctu rn itsAge ; }
10 : i n t GetWeight() ( return itsWeight;
11 :
12 : pr i vate :
13 : i nt itsAge ;
14 : int itsweight ;
15 : );
16 :
17 , SimpleCat : , SimpleCat(int age, int weight) :
18 : itsAge(age) , itsweight(welght) fl
19 :
20 : SimpleCat" TheFunction() ;
21 :
22 : i nt main ()
23 : (
24 : Simp lcCa t &. r eat = Th e Function ();
25 : i nt age = rCat . GetAge( );
26 : std : : cout « "reat i s • « age « • years old ! \n ";
27 , std : : cout « ''-rCat : • « &reat « std : : endl ;
28 : I I How do you get dd of that memory?
29 : SimpleCat • pCat = &rCat ;
30 : delete pCat ;
31 : II Huha , ezek után mire utal az rCat??
32 : re t urn O;
33 :
34 :
35 : SimplcCat &TheFun c ti on ()
36 : {
3 7: SimpleCat * p Fris ky " n e w S impleCat (5 , 9) ;
38 : std : : cout « "pFrisky: • « p Frisky « s td : : end !;
39 : return *pFrisky ;
40 :

pFrisky: 8861880
Az rCat 5 éve!; !
&rCat : 886 1880
12. óra • A hivatkozásokfól és

Az olvasó által kapott kimenet másként fog kinézni, hiszen minden számítógép eltérő
címen tárol ja a változókat, attól függ6en, hogy mi van a memóriában és hogy mennyi
az elérhető szabad hely. Ehhez hasonlót is kaphatunk tehát:
pFrisky : Ox00431CAO
Az rCat 5 éves!
&rCat: Ox00431CAO

Figyeleml Gondoljuk át, mit törlünk!


Afenti program szépen lefordul és látszólag jól műkö d ik . Csak éppen egy idózített
bomba ketyeg benne...

A TheFunc t ion () függv~nyt rm.:gvállOZI:ltluk ; immár nem egy helyileg létrehoi'.oLt ob-
jektum hivatkozását adja vissz<I. A 37. sorban egy mutat6val dinamikus memóriaLerületet
roglalunk le. Az áll:lla r'yilvánl:lrtoll memóriacímet kinyomtatjuk, majd a mulató feloldá-
sa után visszatérési értékül adju k SimpleCat objektumunkat, méghozzá cím szerint.

A 24. sorban :l TheFunction () visszatérési énékével inicializ:'ilunk egy frisse n dekla-


rált hivatkozást rClIt néven. Ennek a macskának az életkorát adjuk át egy egész válto-
zónak a 25. sorban, amelyet ki is nyomtalunk a 26. sorban.

Annak igazol:'is.ír3, hogya f6progmmban deklarált rCat hivatkoz.ís ugyanarr3 az ob-


jektumm mulat, mint amit a TheFunction () létrehozott a dinamikus memóriában,
a .címe!' operntoTl alkalmazzuk az rCat -re. Minden kétséget kizáróan megegyezik
a két kinyomtmotl mem6riacím, vagyis az rCat hivatko7..ása és a dina mikus mem6riate-
rületen korább'Ln létrehozott macska azonos.

Eddig rendben vagyu nk. De hogyan szabaditjuk fel végül a macska áJtal lefoglalt me-
m6ri:Herületet? Hivatkoztlsra nem lehet törlést (delete) alkalmazni. Egy agyafúrt meg-
oldás: hozzunk létre egy másik mutatót, inicializáljuk az rCat-t6J kapott memóriadm-
re, majd töröljük . Ez relsi'.abadítja a lefoglalt memóriát és megszünteli a mem6riaelszi-
várgást. Van azonb,Ln még egy kis probléma: mire utal az rCat a 31. sor mán? Kor"áb-
ban úgy fogalmaztunk, hogya hivatkozásoknak létező objekrumra kell utalnia.
Ha n eml étező (null) objektumra utal egy hivatkozás, lrunt most, akkor a program ér-
vénytelen.

Figyelami Nemlétező objektumok


Nem lehet eléggé hangsúlyozni, hogy a ne m létező objektumra hivatkozó program
érvénytelen, és futásának kimenetele megjósolhatatlan.
226 1111. rész • Mem6riakezelés

Valójában kél megoldása is van a problémának. Az egyik az, hogy a függvényt61 magát
a (megfelelő mem6riacímre utaló, 37. sorban létrehozott) mutat6t kérjük el vissz.mérési
énékként. Ezután a hívó program törülheti a mutatót, ha nincs rá többé szüksége. En-
nek véghezviteléhez változtassuk meg a TheFunc t ion () függvé ny visszatérési énékét
(hivatkozás helyett) mut;ltóra, és a feloldott mut:ltó helyett adjuk vissza magát a rnuta16t:
Simp l eCat · TheFunction()
(
Simp!eCat • pFrisky = new SimpleCat(S , 9) ;
std : : cou t « 'pFrisky : • « pFrisky « std : : e nd !;
re t urn pFrisky ; 1/ Magát a mutat6t adjuk vissza

A másik (elegánsabb) megoldás: a macska objektumot még a hívó függvényben hoz-


zuk létre, és cím szerint adjuk ál a TheFunction () -nak. E megoldás szépsége, hogy
a hívó függvény foglalja le a szükséges memóriaterOletet, ~s az ő fele l őssége mar-J.d en ~
nek felszabadítása is. A következő sorokb61 kiderül, miért kívánatos ez így.

Mutató, mutató, k.ié a mutató?


Amikor progr-dmunk lefoglal egy szeletet a dinamikus memóriából , visszakapunk egy
erre hivatkozó mutatót. Musz:'tj megtartanunk ezt a mutató!, mert ha elvész, a m e m6ria~
szelel többé nem szabadítható föl; mem6riaelszivárgás keletke zik.

Amikor a függvénye k egymásnak adogatnak egy memóriaszeletet, valamelyik függ~


vény a mUlató lulajdonos;'\nak tudhatja magál. Általáb:m cím szerint keri.11 átadásm
a memória tartalma, l!s annak a függvénynek a dolga lesz felszabadít:lni a mem6rb sze-
letet, amelyik lefoglalta. Ez azonban csak egy javasolt sZ:lb;'\ly, nem bárm i {lTon kötele-
ző érvényű.

Meglehetósen veszf:lyes valamely függvény által lefoglalt memóriatetiilet felsz."1badítá~


sát egy másik függvényre bízni. Ha nem egyértelmű , hogy melyik függvény~ a mutató,
ez két csapdát is rejthet: elfelejtjük törölni vagy többször is töröljük. Mindkettő komoly
gondokat okozhat a programban. Biztosabb úgy megírni a függvényeinket, hogy :lme-
Iyik lefoglalja a memóriaeimct, az legyen a felszabadílója is.

Ha olyan függvényt ír az olvasó, amelyben létre kell hozni valamit a dinamikus mC1l16-
riaterOleten, majd ennek eimét vissza kell küldenie a hívó függvénynek, fontolja meg
a kezelöfelület megváltoztatását. Legyen a hívó fél fe ladata amemória lefoglalása - ez
adja át a megfelelő eimpar.lmélert függvényünknek. Ezzel a memóriakczclés fcleI6S5é~
ge a hívon függvénye n kfvülre kerOl; ahhoz a függvényhez, melynek egyébkt!nl is ne-
mes feladata a felszabadítás megszervezése.
12. 6ra • A hivatXozásokról és mutat6kr61- mélyebben 1227

Helyes HelyteIon
Akkor használjunk érték szerinti pa- Ne használjunk cím szerinti átadást,
raméterátadást, ha musz.1j. ha nem nyilvánval6, hogy létezik
Akkor adjunk viSS7.a értéket egy függ- a hivatkozou objektum.
vényMI, ha muszáj. Ne használjunk nemlétező objekmm-
ra utaló hivatkozást.

Kérdések és válaszok
Kérdés: /vfit!r' használj/mk lIIutatókat, ha egyszen7bbek CI hiva/kozások?

Vá lasz: A hivmkozások nem utalhatnak nemlétező objektumra, és nem lehet6kr.:t újF.!


felhaszn::j lni. A mutatók n.lg:llm:LS;Lbbak, dc valamivel nehezebb a hasznábtuk.

Kérdés: Miél1 adju nk ,lissza bá/'ll/iI ls énék szedI/! egy faggvéll)'oo/?

Válasz: Ha helyi objektumot kell visszaadnunk, akkor egyszerűen kénytelenek va·


gyunk az értékét visszaadni, mert egyébként nemlétező objekrumra utal6 hivatk07.ást
adnánk vissza.

KérdC"S: lia ilycn veszélyt"S a Ililxlfkozás visszaadása, mién nem használj/lk mindig
az él1ék szerimi visszaadásI?

Válasz: Sokkal hatékonyabb programokat lehet írni hivatkozás visszal.ldásával. Keve-


sebb mem6ria kelt hOZ7.á, és gyorsabban fut a program.

Gyakorlatok
Most, hogy mélyebben megismerheLtük a mutatók és hivatkozások haszná latftl, vála·
szol junk néhány kénlésre és o ldjunk meg néhány feladatot ludásunk mcgszitárdítás:l
végeU!

Kvíz
1. Miért jobb a cím szerinti paraméterátadás na!,'Y adalmennyiség esetén?
2. Miért lehet érdemes const mutatót használni a cím szerinti paraméterátadáskor?
3. Miért lehet jó hivalkozás helyett mutatót használni a dm szerinti paraméterát-
adáskor?
4. Lehet-e hivatkozást készíteni eb'Y mutató típusú változ6ra?
228 1111. rész • Mem6nakezelés

Feladatok
1. Bontsuk három rés7:re :l passobjectsbyref. cpp progmmot 02. 1 Lista) úgy,
hogy maradjon meg eredeti néven a főprogram és a fülli,'Vé nyek megval6sítása
(Passobj ectsbyref. cpp), az osztálymet6dusok kerüljenek át egy külön fújiba
(SimpleCat. cpp), és legyen egy fejlécállomány is az oSltály-deklarációkból
(SimpleCat. hpp). Vegyük fel a SimpleCat. cpp-l a projektállományok közé, és
fordítsuk le a programot! Ez azt szemlélteti, hogy hogyan lehet megosztani
az osztálykönyvUírakat. A tagfügf,rvények leFordíthat6k és a gépi nyelv(!' prog-
ramk6d a rcjlédllománnya l (benne a függvényprolotípussal) egyiin közzétehető
a termelékenység javíL'isára.
,t
2. Módosítsuk Clgy lcaky. cpp progmmOl 02.5 Lista), hOh')' az mutatókat hasz-
náljon a TheFunction () hívásakor, majd törölje a mUI,Hókal a memóriaelsúvár-
gás kivédése é rdck6ben!
3. MódosíLo;uk úgy a pUI:lDobjectsbyref . cpp és a passre[toobj. cpp (12.1 és
12.3 Lista) programokat úgy, hogy azok nyorntassaák ki a változók memóriaci-
meit a függvényhívások el6tt és után! Ezzel betekintést nyerhetünk a Iamlit mód-
szerekbe.

Válaszok a kvfzkérdésekre
L Az alapértelmezett al. érték szerint par.lrnéter.'itadás. Ekkor m{tso!atot kell készí-
tt::ni al. átadandó objcktumokr61. Nagy objektl.lmoknál ez számottev6 ideít és me-
móriát igénydllcl.
2. A const kulcsszó alTa utasítja a fordít6programot, hogy tiltsa meg a bíVOIl függ-
vénynek a mulató által hivatkozott érték megváltozt3tását. Így a músolás költsé-
gei nélkül is érvényesül az érték szerinti paraméterátadás biztonsága.
3. Ha a hívott függvény dinamikus memóriát foglal le, ennek cime viss7.aküldhet6
a hívó félnek egy mulatÓ segítségével. Nem szabad azonban később elfelejteni
a lefoglalt memória fc1sl.abadítását, hogy kiküszöböljük a memóriaelszivárgást.
4. Miért is ne lehetne? Azonban érdemes megfontoltnak lenni e téren, mert egy
ilyen hivatkozás sok zavart okozhat, Gondoljl.lnk ugyanis bele, hogy már magl.lk
a mutatók is elég fejtörést okozhatnak.
IV. RÉSZ

Haladó eszközök
13. óra A függvények kifi nomultabb használata
14. óra Operátorok túlterhelése
15. óra Tömbök
13. ÓRA

A függvények kifinomultabb
használata
Ebben az órában a következőkrőllesz szó:
• Hogyan k:hCl II tagfüggvényeket lúlterhelni
• Hogyan írjunk o lyan függvényeket, amelyek dinarnikusan kezelt változ6kkal
rendelkező osztályokat is támogatnak
• Hogyan inicia lizáljunk különréle objektumokat
• Hogyan lehet másoló konstruktorokat írn i

Túlterhelt tagfüggvények
Az S. órában láthattuk, miként lehet megval6sítani a függvények löbbalakús<Ígát (azaz
a függvénytúlte rhe lést) az.1hal, hogy több fÜgb'Vényt hozunk létre azonos néven, kü-
lönböző pammétcrekkcl. Az osztályok tagfügf,,,,ényeil is hasonlóképp lehet túlterhelni.
232 1 IV. rész· Haladó eszközök

A 13.1 Listában szerepl6 Rectangle osztálynak két DrawShape () függvé nye is van.
Az egyik nem ké r paramétert; ez az osztály pillanatnyi adatainak megfelel6en rajzol
egy téglalapoc. A másik két paramétert kér: a szélességet és a hosszúságot, és - elte-
kintve az osztály a kmális adataitól- ezek alapján rajzo lja meg a téglalapol.

13.1 Usta - Tagfüggvánvak túharheláse (overloadfunctions.cpp)


o: II 13.1 Lista II Egy osztály tagfOggvényeinck túlterhelése
1 : tinclude <iostream>
2,
3: II Rectan9lc osz tály-deklaráció
4 : class Rectangle
5: (
6 : public :
7 : II KonsLruktor
8 : Rectangle(int width . int height);
9 : -Rcctangl e () ( J
tO :
11 : II Túlterhelt tag füg gvényOnk : a DrawShape
12 : void OrawShapc() con~t ;
13 : voId OrawShape(int aWidth , int aHeight) const ;
14 :
IS : priV/lltl".! :
16 : int itsWidth;
17 : int itsHeight ;
18: };
19 :
20 : 1/ A konstruktor mcgva16sitása
21 : Rectangle: : Rectangle(int width, int heightl
22 : (
23 : itsWidth : width;
24 : itslleight - height;
25 : J
26 :
27 :
28 : II Az egyik túlterhelt DrawShape nem kér enni
'-9 : II Az osztály pillanatnyi értékei szerint rajzol t églalapot
30 : void Rectangle: :DrawShape() const
31 : (
32 : OrawShapc( itswidth . itsHcightl ;
33 : )
34 :
35 :
36: 1/ A másik túlterhelt DrawShape két paramétert kér
37 : II A két a daL alapján rajzolj a a Légldiapol
38 : void Rectangle: : DrawShape (int width, int heightl const
39 :
40: for (int i '" O; i<height; i H)
41: {
42 : for (int j '" O; j< width; j++l
43 : {
44 : std : : cout « ".';
13. ő... A kifinomultabb használata

45 :
46 : std : :cout « "\n" ;
47 :
48 :
49 :
50 : 1/ A túlterhelt függvények bemutatása a foprOgramban
51 : int ~in()
52 : (
53 : /1 30 ; 5 - re inicializáljuk az egyik téglalapot
54 : R~ctanglc thcRcct(30,5);
55 : std : : cout « "DrawShape() : \n ";
56 : theRect . DrawShape () :
57 : std :: cout « "'nDrawShapc{40,2): \n" ;
58 : LheRecL.DrawShape( 40,2) ;
59 : reCurn O;
60 :

KImenet
DrawShape ( ) :

...............................
••••• * ••••••••••••••••••••••••

DrawShapc(4.0, 2) :

A fenlÍ lista lt!gfonlosabb k6drészlele a 12~ 13. sorban találhat6, ahol a DrawShape ()
függvényt lúlwrhdtnck deklaráljuk. Túlterhelt osztályfüggvényeinket a 28-48. sorban
valósítjuk meg. Figyeljük meg, hogya paraméter nélküli változat cgyszeníen meghívja
a kétparaméteres váltoZ:ltOL a7. o~zLály aktuális tagváltozó-értékeivcl. Érdemes kemé-
nyen taltani magunkat ahhoz, hogya két függvényben ne ismétclji.\nk k6drészJetet,
mert úgy igen nehéz a későbbi váltm:LaLáliokal öSlizehangolni, és ez számtalan hiba le-
hct6séget tartogrtl.

Az 50-GO. sorban látható főprogram létrehoz egy Rectang le tégla lapot, majd meghívja
a DrawShape () függvényt, e l őször paraméter nélkül, majd két egész számmal.

A fordítóprogram dönti ej a paraméterek száma és típusa alapján, hogy melyik met6-


dust hívja meg. Elképze l hető Icnnc ch')' olyan DrawShape () függvény is, amely szintén
két paraméter kérne: egy hosszúság jellegűt és egy felsorolásos tipusüt, amely rt fel-
használó kívánságának megfelelően lehetne szélesség vagy hosszúság.
234 l iV. rész • Haladó asz!<özök

Alapértelmezett értékek használata


Ahogyanannál függvényeknek is lehet alapértelmezett paraméterilk, ugyanez a hely-
zet tagfüggvényekkel is. Megadhatunk alapértelmezett értékeket a dcklaráci6kor, amint
azt a 13.2 Lista bemutatja.

13.2 UsI. - Alap6rtalmazett értékek megadása (usingdefautts.cpp)


o: / I 13 . 2 Lista / I Alapértelmezett érték ek a tagfüQ'l1Vényekbcn
l : #include <iostream>
2.
): II Rectangle osztály-deklaráci6
4 : class Rect.angle
5: (
6 : public :
7 : II Konstruktor
8: Hectanglc(int width , int h eight) ;
9: -Rec t angle () (}
10: void DrawShape(int aWidth, int aHeight .
ll : bool UscCurrentVals = false} const ;
12: pdvale :
l) , int itsWidth ;
14: int itsHeight ;
15 : J;
16:
11: II A konstruktor megva16sitása
18 : Rectangle : : Rect.anglc(int width, int heightl :
19 : itsWidth(widthl , II inicializálás
20 : itsHeight(height)
21 : (j 110res fUggvénytOrzs
22 :
23 : II A 3 . paraméter helyén álló érték 6rtc1mezése
24 : void Rectangle : : DrawShape{
25 : int width,
26 : int height ,
27 : bool UscCurrentValue
28 : ) const
29 :
30 : int printWi dth ;
31 : int printHe ight ;
32 :
33 : iC (UseCurrentValue "':o true)
34: (
35 : printWidth", i t sWidth ; II Használja az osztály pillanatnyi értékeit
36 : printHeight :o itsHeight;
37 : )
38 : else
39 :
40 : printWidth", width; II Használja a megadott paramétereket
41: printHeight = height ;
42: j
43:
13. 6ra •

44 : for (int i = O; i<printHeight; i H )


45 :
46: for ( int j = O: j< printWidth: jH)
47 : t
48 : s td : : cout « ' ~';
49 :
50 : std : , eout « "\ n';
51 :
52 :
53 :
54 : II A túlterhelt fUggvények bemu tatása a foprogramban
55 : int main()
56 : {
57 : II 30;5 - re inicializáljuk a z egyik tégla1apot
58: Rectangle theRect(30,5);
59 : std :: cout« 'OrawShape(O,O,truQ) .. . \n" ;
60 : theRect . OrawShape(O , O. truQ) ;
6l : !:ltd : : cout «"Or-awShape(40 , 2 ) ... \n";
62 : theHect. DrawShape (40,2) ;
63 : return O;
64 :

menet
OrawShape(O,O , true) ...
~ ••••••••••• • * •••• ** •••• *** •••

• _•••••••• * •••••••••••••••••••
••••••••• •••••••••••••••••••••
DrawShape(40, 2) ...
~ •• •••• • * ••••••••• *.* •••• ** •••• * •••••••••••••••• o • • • • • *. 0 •• *

A 13.2 lista a DrawShape () függvény túlterhelése helyen <llapértelmezeu értékekkel


felruházott egyetlen függvénnyel dolgozik. A függvény deklarációja a 10-11. sorban
van, mely szerint három pamméterr vár. Az ds6 keu6 (a szélesség és hosszúság) egész
sz..'im, a harmadik (amely arm uwl, hogy használjunk-e alapértelmezett értékeket) logi-
kai típus (igaz vagy hamis), mely alapértclmezésként hamism van állítva.

A függvény megval6sítása a 29. sorban kezd6dik. Ha a harmadik (useCurren tValue)


paraméter iga:.:, akkor az itswidth és itsHeight tagváltoz6kal adjuk értékül a helyi
printWidth, illetve printHeight változ6knak.
I
236 IV. rész • Haladó eszközök

Ha ellenben a UseCurrentValue hamis - akár azért, mert nincs is Cés hamis az alapér-
telmezés), akár azért, mert a felhasználó azt adta meg - akkor a kél első paraméter
alapján állít juk be a printWidth és a printHeight értékét.

Figyeljük meg, hOb'Y ha a UseCurrentValue értéke igaz, akkor senkit nem érdekel
a másik két paraméter értéke.

Hogyan válasszunk a túlterhelés


és az alapértelmezett értékek használata között?
A 13.1 és a 13.2 Lista ugyanazt a feladatot oldja meg, de az el.sőbe n haszná lt túlterhelt
függvényeket egyszeníbb megérteni és kézenfekvőbb használni. Ha esetleg egy har-
m:ldik függvényvá llozal igé nye is felmerül (például a felhllszná16 szeretne egy olyat is,
mclybcn csak a szélességet vagy csak a hosszúságot adjuk meg, de nem mindkeu6t),
:Ikko r is könnyebb kiterjeszteni a túilerhelt függvényeket. Az alaphtdmezett t'! rtékek
használata hamar elbonyo l6dhat az utó lagos hozzátold(lsokkal,

Hogy,ln lehet eldönteni, hogy tLllterhelt fü ggv~nye k et vagy al:lp~ rtelmezcll l!rté kekel
érdemescbb-c használni? Ime egy gyors iránymutatás:

Használjunk függvl: nytúlterhelést, ha

• Nincs {:sszerú al:l pél1elmezeu érték.


• Különböz<l eljárásokra van szükség az egyes VáltOZ<1tOknál.
• Különböző paraméterlistákat szeretnénk támogatni.

Konstruktorok túlterhelése
A konstruktorok, mint minden más tagfüggvény, túlterhelhet5ek. Ez nagy hatású és ru-
galmas eszközt ad kezünkbe.

I [a péld{IU[ kétféle téglalapkészít6 konstruktorunk vanj az egyiknek ál lehet adni para-


méterül a hosszllsá glsz~ le.sség adatpárt, :I másiknak viszont nincs szüksége rá, akkor
a fordítóprogram "szerint hoz létre ilyen v:lgy oly:ln téglalapot, ahogy II megadott para-
méterek száma és típus diktálja (mint bármely más túlterhelt függvény esetében).

A konstrukto rokkal ellentétben a destnlktorokat nem lehet túlterhelni. A destruktorok


paraméter-szignatúrája (deklarációja) mindig ub'}'an3Z: az osztály neve, el6tte egy
hullám(-) jellel, és nem adható meg pamméter.
13,6ra • használata 237

Objektumok inicializálása
Mostanáig az objekrumok tagváltozóit a konstruktor törzsében inicializáltuk. A konstruk-
toroknak azonban kél része van: a kezcl6értéklista és a konstruktor tör.lSe.

A legtöbb változó bármelyik részben inicializálható, azonban világosabb és gyakmn


hatékonyabb is a kezd6értéklistában inicializáini a tagváltozókat. Az alábbi példa eZl
mutatja be: •
CAT () , II a konstruktor neve és a paramétGrek
itsAge(S) , II kezdoértéklista
i t sWeight(8)
( l II a konstruktor törzse

A konstnlktor par:améterlistáj{mak zárójele után egy kett6spont áll~ ez utá n köve tkezhet
a tagvá ltoz6 neve és egy zlirójelpár. [bbe frandó az a kifejezés, amelyet kezd6értékOl
szeretnénk adni a tagvá ltoz6nak. Ha több tagváltoz6t is inicializálunk, vessz6vel kell
elv~!asztanunk 6kt!t.

Figyeleml A konstansok nem vákoznakl


Ne felejtsük el, hogya hivatKozásokat és a konstansokat inicializálni kell, és utána
már nem lehet öket megválloztalni. Ha adat1ag hivatkozásaink vagy konstansaink
vannak, ezeket a lenti m6don, a kezdöértékJistában kell iniciaHzálni.

Egyszerűbb - és azt kell mo ndjam, hogy hatékonyabb - a tagváltoz6kat a kezcl6érté k-


listában inicializál ni, mintsem kés6Db értéket rendelni hozzájuk. Ennek megértéséhez
el6ször meg kell ismerkednünk a másoló konstruktofídl

A másoló konstruktor
Az alapértelmezett konstruktoron és destmktoron kívül a fordítóprogram még egy
alapértelmezett m:ísoló konstruktort is rendelkezésünkre bocsár. A másoló konstnlktor
mindannyiszor meghívódik, ahányszor csak másolat készül az objektumról.

Ha érték szerint adunk vagy kapunk objektumot egy függvényt61 , ideiglenes másolat
készül r6la. Ha egy felhasználó által defmiált objekrumról van szó, akkor az oszt{llyhoz
tartozó másoló konstruktor hívódik meg.

Minden másoló konstruktornak egyetlen pammétere van: a sz6ban forgó osztá ly egy
példányám utaló hivatkozás. Ezt nyugodtan írhatjuk konstans hivatkozásként, hiszen
a másol6 konstruktornak nem kell változtatnia al átadon objekrumon. Példáu l:
ClIT(const CAT & thecat) ;
IV. rész • Haladó eszközök

ln a CAT konstruktor egy (már létező theCat) macskám utaló konslans hivatkozást
vesz áL A másoló konstruktor célja másolatot készíteni a theCat objektumr61.

Az alapértelmezett másoló konstnlktor egyszen.1en lemásolja a paraméterként kapoll


objektum nunden tagvá ltoz6ját, és így hozza létre az új objektum tagváltoz6it. ELL fel-
színi (vagy tagonkéntO másolásnak Cshallow copy) hívják, mivel jól mt1ködik a legtöbb
tagváltozóra, de nem kezeli jól a dinamikus memóriatcrületre utaló mutatókat.

A fe lszíni vagy tagonkénti másolás tehál az objekrum tagváltoz6inak pontos m(lsolatát


viszi át egy másik objekrumba. Ez aZl is jelenti, hogya mutatók ugyanarra a mcm6ria-
címre fognak mutatni mindkét objektum esetében. A mélységi másolás (deep copy) ez-
zel szemben képes arra. hogy a dinamikus mem6riateJilleten lev6 értékeket is meg-
duplázza.

lIa a CAT osztálynak van egy itsAge tagv:íltoz6ja, ;1rnely egy a dinamikus mem6riában
lev6 egész sz:ímrJ mulat, akkor az alapértelmezett m{lwl6 konstruktor a paraméterként
kapott CAT objektum it sAge tagváltoz6ját, mintmulat6t másolja ál az (Lj CAT objektum
itsAge nevű l:!8válloz6jába. A kél változ6;1 mem6ri:l egyazon területére mutat, :!hogy
azt a 13.1 ábm is mutatja.

Free Slore

old CAT New CAT

,---"":'::A,,,,,-...Jf-' I\sAge

13.1 ábra
Az a/t/{Jértc/mczell m{/so/ó kol/slmklol" hasz1lálara

Ez kataSZlrnfához vezet, ha bármelyik macska megszúnik létezni. Ekkor ugyanis meg·


hívódik a destruktor, amely meg fogja kísérelni a lefoglalt memória felszabadítását.

Esetünkben, ha például az eredeti macska halálozik el, akkor :!nnak destruktora felsza-
badítja a dinamikusan lefoglalt memóriát. A másolatbeli i tsAge ugyanakkor még min·
dig ugyanoda fog mut:!tni, így, ha a program megpr6bálja használni ezt a mutat6!,
összeomlik. A 13.2 ábm illusztrálja a katasztrófát.
I
13. óra • A függvény.k ki1inomultabb h,sznál.ta 239

Fr86 Store

NewCAT

.'

13.2 ábra
Gazdál/llll IIIIIUIIÓ IélrcllozóslI

Úgy oldhat juk meg ezt a felszíni másolásból fakadó problémát, hogy megírjuk gólját
másoló konslruklorunk:n, és lefoglalunk a másolat számára egy új mem6riacímcl.
A mély másobt készít(:sc lehet5vé teszi, hogy a már létez6 értékeket is átmásolju k
:IZ új memÓrialerületrc. EZL mutatja be:\ 13..' Lista.

13.3 Lista -A _ _ ktur oHIk6d6Ie I_ _ .epp)


o: /1 13.3 Lista
l : /1 máaoló konstruktorok
2 : linclude <iostrcam>
3,
4: class eliT
S: (
6 , public :
7 : CAT(); 1/ alapértelmezett konstruktor
a: eAT (const eliT 'l; 1/ másoló konstruktor
9 : _CAT(); 1/ destruktor
10 : int GetAge() const ( rc tu rn *itsAge ;
ll : int GctWeight() const ( return *itsWeight ;
12 : void SetAge(int age) { *i tsAge = age; }
13 :
14 : private :
15 : int * itsAge;
16 : int *it sWeight ;
17 : };
18 :
19 : CAT: : CAT()
20 : {
21: itsAge = new int;
22 : itsWe ight = new int;
23 : *i tsAg e = 5 ;
24 : * i tsWei ght = 9;
25 :
26 :
I
140 IV. ré" • Haladó aszközök

27: CAT , : CAT(const CAT & rhs l


28 : (
29 , itsAge '" new int;
30 : itsWeight '" new int;
31 : *itsAge '" rhs ,GetAge () ;
32 : " itsWeight "- rhs .GetWeight() ;
33 :
]4 :
35 : CAT ,: -CAT()
36 : {
37 : delete itsAge ;
38 : itsAge '" O;
39 : delete itsWeight ;
40 : itsweight • O;
41 :
42 :
43 : int mai n (}
44 :
45 : CAT frLs ky;
i1 6 : std :: cout « "frisky ' s age : • « frisky.GctAge() « '\n' ;
47: std :: cout « 'Setting frisky to 6 ... \n';
48 : frisky . SetAge(6) ;
d9 , std :: cout « ' Creating boots from frisky\n';
50 : CAT boots(frisky) ;
51 : std :: cout « "frisky's age : • « frisky .GetAg e{) « "\n' ;
52 : std :: cout« "boota' age: • «boots.GetAge() « "\n";
53: std : : cout « 'setting frisky to 7 ... \0";
54: frisky.SetAge(7) ;
55: std :, oout« "frisky's age , " « frisky.GetAge() « "\n" ;
56 , std : :cout « 'boot ' s age : • « boots.GetAgc() « "\n";
57 : return O;
58 ,

friaky' a age , 5
Setting frisky to 6 ...
Crea t ing boots from frisky
frisky ' s age : 6
boota ' age , 6
sp. t ti ng frisky t o 7 ...
fri sky ' s age : 7
boota ' age , 6

A 4-17. sorban deklaráljuk a CAT osztályt. Figyeljük meg a 7. sorban az alapénelmezett


konstruktor, a 8. sorban a másoló konstruktor deklarációját. A lS. és 16. sorban két tag-
változót deklarálunk, mindkettő egész számra mutat. Általában nem használunk egész
mutatókat egy Q'jztály ta&",áltozóiként, de így lehet a legegyszerúbben illusztrálni a di-
namikus memórialerületen definiált tagváltozók kezelését.
Az alapértelmezen konstruktor a 19-25. sorban helyet készit a dinamikus memóriában
ki!t egész számnak, majd az értékadást is elvégzi.

A másoló konstruktor a 27. sorban kezd6dik. Figyeljük meg az rhs nevű pammétert.
Gyakran nevezzük így a másoló konstruktornak átadott paramétert, amely a rig!Jf-
1/CI lld-side(jobboldalt) rövidítése. Ha vetünk egy p illantást a 31. és 32. sorba n található
értékadásokra, megf1gyelhetjük, hogy :LZ átadott paraméter áll az egyenl6ségje1 jobb ol-
dalán. Vizsgáljuk ezt meg részletesebben:

• A 29. és 30. sorban lefoglalunk egy-cb'Y egész számnyi helyet a dinamikus me-
móriában , melyeket a már létez6 CAT objektum megfelel6 é rtékei alapján töl-
tünk fel a 31. és 32. sorban.
• Az rhs eb'Y macska (CAT), melyet konstans hivatkozáskénr adunk át a másoló
konstruktornak. Az rhs . GetAge () tagfüggv&ny azt az értéket adja vissza, mely-
re az rhs sajíit itsAge tagváltoz6ja mutat. Mint igazi CA'! objektumnak, rhs-nek
megvan minden szüksl!ges tagváltoz6 ja.
• Amiko r a másoló ko nstruktor mcghív6dik egy ct j macska létrehozására, kapnia
kell egy (már létez6) macskát par.!méterként.

A 13.3 ábra felvázolja, mi történik itt. A már létez6 macsk .. változ6i álta l mutatott értc:!-
kek átmásolMnak az új macska számá r.! lefoglalt memóriaterÜlctckrc.

Free Store

r- 5

5 1-
oIdCAT NewCAT

ItsAge I--' itsAge

13.3 ábra
A mély mdsolás bemutatása

A 45. sorban létrejön egy frisky nevű m,lCska (CAT), majd kiír6clik az életkora, melyet
a 48. sorban gyorsan át is állírunk 6-m (természetesen ezt is dokumentáljuk). Az 50.
sorb-.lO kl6nozunk egy új macskát (boots néven) egy másoló konstruktorral, melynek
frisky-t adjuk át pamméterként. Ha bármilyen más függvénynek adnánk át érték sze-
rint frisky-t pardméterként, akkor is ugyanezt a másoló konstruktort hívná meg a for-
dítóprogmm.
242 1IV. rész · Haladó as2l<özök

Az 51. és 52. sorban mindkét macska életkorát kiíratjuk Elég meggyőz6en látszik,
hogy hoots életkora nem az alapénelmezen 5, hanem a frisky-161 megörökölt 6.
Az 54. sorban frisky életkorát 7-re állítjuk, és ezt rögtön ulána ki is írat juk. Ekkor
frisky életkora már 7, boots azonban megmaradt fiatal 6 évesnek, jól nlUtatva, hogy
kora különálló memóriatcrülctcn lárolódik.

Amikor macská ink kimúl nak, destruktoraik automatikusan meghív6dnak. A CAT dest-
ruktor mcgval6sítása a 35-41. sorban látható. Mindkét mutat6t (it s Age és itsweight)
töröljük egy-cgy delet e paranccsal, fe lszabadítva ezzel az általuk lefoglalt dinamikus
memóriát, és a biztonság kedvéért le is nullázzuk a mutatókat.

Kérdések és válaszok
Kérdés: Miél1 haSZ/lálnánk afapértelmczcu é/1ékekkel dolgozó jüggvényeket, Ita lellet
jllggvénytúlterhelést is alkalmazul?

Válasz; EgyszenThb egy függvényt karbantanani, mint kettót, és sokszor könnyebb


megérteni egy alapénelmezett énékekkel definiált függvé nyt, mi nt két függvény törzsét
végignézni. Szintén fontos szempont, hogya tapasztalatok szeri nt gyakori hiba, hogy
az egyik fOggvény kijavítása után elfelejtkezünk a másikr61.

Kérdés; Ha ilyen problematikus afi."lggvéllyek flílterltalése, miérlllem használu lIk he-


Iyetfl"/k mindig alapél1elmezett érlékeket?

Vála..tt: A rüggvénytúlterhelés olyan lehetőségekkel is szolgál, amelyek nem állnak ren-


delkezésünkre az alapértelmezett értékekkel dolgo7..6 függvénycknl:1. A paraméterek
számán túl ezek típusával is befolyásolhat6 ugy.mis a túlterhelt függvényck működése.

Kérdés: Egy osztály komtrllktoriinak megírásakor mi alapjáll dóntlletó el, mit érdemes
az kezdoo'1éklisttibcIII e/vegez/l i, és mit ajiiggvényt6rzsbell?

Válasz: Egy egyszenl alapszabály az, hogy am il csak lehet, intézzünk el a kezd6érték-
listában; azaz minden tagváltoz6t inicia lizá jjunk ill. Az egyéb dolgok, mint a számítá-
sok vagy kiírások a konstruktor törzsébe valóak.

Kérdés: Lelle/-e Ilíllerhell függvéllynek alapérlelmezelf paramétere?

Válasz: Természetesen. Semmi sem liltja, hogy ezeket a hathatós eszközöket egyszerre
használjuk. A túlterhelt függvények közül egy vagy több tarta lmazhat saját alapértelme-
zett énékeket a szokásos szabályok szerim, mint ahogy ez így van bármely más fü gg-
vénnyel is.
13. 6ra • A függvények kifinomultabb használata 1243

Gyakorlatok
Ebben az óráblln a füg&yvények kifinomultabb használatával ismerkedtünk meg. Vála-
szolju nk néhány kérdésre és végezzünk el né hány feladatolludá suok ellenőrzése és
megsziJárdít;ísa végett!

Kvfz
1. Honnan tudj:l a fordit6progmm, hogy a túlterhelt függvényválto7..alOk közül me-
lyiket kell meghívnia?
2. Lehet-e ala pénelmezelt paraméte rekkel ellátni tllltcrhelt függvényváltoz31okat?
3. Miért érdemes túlterhelni egy konstruktort?
4. Mit csinál a másol6 konstruktor?

Feladatok
1. Módosítsa úgy az over l oadfunctions . cpp programot 03. 1 Lista), hogy legyen
egy olyan változata is a DrawShape () függvénynek, amelynek két alapértelme-
zett egész paramétere van. SikeriI llefordítani és futtatni? Milyen következtetést
tud levonni ez alapján a túlterhelt függvények és az alapértelmezett paraméter-
listák összekapcsolását illet6en?
2. Módosítsa úgy a copycons truc t ors . cpp programot (13.3 Lista), hogy azután
m6dosítsa boot s korth, milltfin f ris ky életkora már megvá ltozott! I [alássa! van-
e bOOLS életkora f dsky-érc? Azt már láttuk, hogy f ris ky életkorának váltoWI-
tása nem befolyásolja boots-él.
3. Goodo!kodjon el a copyconstruc t ors . cpp program 03.3 Lista) alapján azon,
hogy mi történne akkor, ba az itt láthat6 destruktor helyen az alapértelmezettct
használnánk? Mi (vagy pontosabban mi nem) történne?

Válaszok a kvfzkérdésekre
1. A megadott para méterek számából és típusából.
2. Ige n, mindaddig, amíg a különböz6 függvényváltozalok paraméterlistái egyedi-
ek ma radnak, hiszen csak ebb61 tudhatja a fordít6 progmm, hogy mikor melyiket
kell hívnia.
3. A konstruktorok t'Últerhelhet6sége rugalmassá teszi az objektumok létrehozását.
Létre lehet hozni paraméterek nélkül is egy objektumot (például egy alapértel-
mezeIlet vagy üreset), és egy másik konstruktor segítségéve! egy olyat is, amely-
nek kezd6 paramétereit megadhatjuk; vagy akár lehet több konstruktor is több-
féle paraméterlistával.
244 1 W, rész ' Haladó aszközök

4. A másoló konstruktor akkor Iúvódik meg, amikor másolat készül egy objektum-
r61; például amikor egy objektumot kifejezetten le szeretnénk másolni, v3b'Y
amikor egy függvény számám érték szerinti pamméterMadás történik. A fordító-
program rendelkezésünkre bocsát egy alapértelmezett másoló konstruktort, de
a bonyolultabb objektumok esetén ez nem biztos, hogy igényeinknek megfele-
l6cn működik.
14. ÓRA

Operátorok túlterhelése
Ebben az 6rában a következ6kr61lesz sz6:

• Hogyan terhelhetOnk túl tagruggvényekel


• Hogyan terhelhet jük túl a hozzárendelési operátort mem6riakezelés céljáb61
• Hogyan írhatunk olyan függvényeket, melyek támogatják a dimtmikus vá1Lozó-
kal hlsznál6 osztályokat

Hogyan terhelhetünk túloperátorokat


A C++ s:dunos beépített típussa l rendelkezik. Ilyen például az i nt, real, char, stb,
Mindegyik típushoz tartozik számos beépített operátor is, mint például az összeadás
Ct) és a szorzás (*). A C++ lehet6vé teszi, hogy ilyen oper.í.torokat saját osztályainkkal
kapcsolatban is fel használjunk.

Az operáto r túlterhelés bemutatásához a 14.1-e5 listában létrehozunk egy Counter osz-


tályt. A Counter osztályt számlá lásra fogjuk fel használni - minő meglepetés - például
ciklusokban és olyan helyeken, ahol egy számO[ növelünk, csökkentűnk, vagy bármi
egyéb módon vállozlaljuk az értékét
246 1W. rész· Haladó eszközök

0 , II 1 4. 1 Lista
1 : / 1 Coun ter os ztá l y
2: tlinclude <iost r eam>
3.
4: c lass Coun tcr
5,
6: public :
7: Countcr() ;
8: -Counter(){}
9: int GetItsVal()const { return itsVel ;
10 : void SctItllVal (int xl {itsVal • x : }
11 :
12 : private :
13 : int i tsVal ;
14 : };
15 :
16 : Count er: : Counter{) :
17 : itsVal(Q)
18 : fl
19 :
20 : int main{)
21 :
22 : Counter l i
23 : std : : cout « "The val u e of i is • « i . GetItsVal()
24 : « std : : cndl :
25 : re t ur n O;
26 :

The value of i is Q.

Ebben a formában nem sok haszna van ennek a z osztálynak. A definícióját a 4- 14 so-
rokban láthatjuk. Egyedüli tagvá ltolója egy egész típusú változó. Az alapértelmezett
konstruktor - amelyet a 7. sorban deklaráltuk és a 18, sorbnn imple mentáltunk - bcíil-
lítja az i tsVal értéké! Q-ra.

A valódi, beépített, mondhami vérbeli int típusú változ6val szemben a counter típu-
sú objektumunkat se növelni, se csökkenteni nem tudjuk Nem tudunk se hozzáadni,
se elvenni bel61e, nem tudjuk máshoz hozzárendelni , OOt semmilyen egyéb módon
sem tudjuk manipulálni. Cserébe még az é rtékét is problémásabb kiíratni.
14. 6ra •

f~uk meg az inkrementáló függvényt


Az operátor túlLcrhelés révén lehetőségünk nyílik osztályainkat (például a fenli
Counter l is) számos olyan hasznos ké pességgel bővíteni , amelyekkel amúgy nem ren-
delkezik. Operátor túlterhe1ésról beszélünk, lm ch'Y operátorr egy osztályban implemen-
tálunk. A 14.2 Listában láthatjuk, hogyan terhelhetjük túl az inkrcmentálás operátorát.

14.2 Lista - Az inkremantál6 oper., túherhel6sa (overloadincrementcppl


0, /I 14.2 Lista
l: /1 Az inkrementá16 operátor túlterhelése
2: hnclude <iostream>
3.
4: class Counter
5.
6: publ i c :
7: Counter() :
8: -Counter(){}
9: int GetltsVal () const { return i tsVal;
10, void SetItsVal(int xl (itsVal = Xi )
11: void Incremcnt() { ++itsVal; l
12, const Counter& operator++ (l;
13:
14 : privatc :
15 : int üsVal ;
16 : ):
17 :
18 : Counter : : Coun ter() :
19 : itsVa1(0)
20 : ()
21:
22: const Counter& Countcr : : operator++ ()
23:
24 : ++itsVal ;
25 : return - this :
26 :
27 :
28 : int main(}
29 : {
30 : Coun ter i;
31 : std : : cou t « 'The value of i is ' « i. GctltsVal(}
32 : « s t d :: endl ;
33 : i .lncrement() ;
34 : std : : cout « 'The value of i is ' « i .GetltsVal ()
35 : « std : : endl;
36 : ++i;
37 : std : : cout « 'The value of i is ' « i.GetltsVa1()
38 : « s t d : : end!;
39 : Counter a = -+ -+i;
40 : std : : cout « ' Th e value of a : ' « a . Getl tsVa1 () :
24l1 1IV. rész • Haladó aszközök

41: std :: c o u t « · and i : .« l.GetltsVal()« std :: endl ;


42: rcturn O;
43:

value
The
The value
value
of i
of i
of i
"" O
1

"' "
The 2
The value of 3 and i : 3

A 36. sorban meghívtuk az inkrcmentá!6 opcrátor1:


++1 ;

A fordító ekkor valójában a 22-26 sorokban megva16sítoTt operator++ függvényt hívja


meg, mely növeli az itsValue tagváltoz6 értékét majd visszatérési értékként az aktuá-
lis objckuHllot címz6 (l his) mutat6n keresztül mag{ll az objektmnot adja vissza.
Ez egyben lehet6vé teszi azt is, hogy a Counter objekwmot hozzárendelhessük a-hoz.
l ia a Counter típusú objektumok dinamikusan is roglalnának memóriát , akkor kötele-
z6en felül kellenI.! bír{llnunk a konstruktort is. Jden esetben azonban az :l lapértelme-
zelt másoló konstruktor is megleszi,

Figyeljük meg, hogya példában hivatkozásként adjuk vissza a Counter objektumot


<I hívónak, így nem kelllélrehoznunk felesleges ideiglenes objektumot. A const: kulcs-
szóra azért V:1ll szükség, men a Counter objektummal dolgozó függvény nem módo-
síthatja az értéké!.

A postfix (utótag) inkrementáló operátor túlterhelése


Mi a helyzet abban az esetben, ha a postfIx inkremcntáló operátOlt szeretnénk túlter-
helni? Ez problémás kérdés a fordító szemszögéből nézve, hiszen elsőre nem világos,
hogyan tehetünk különbséget preftx és postfLx operátorok között? Megegyezés s;.;erint
czt egy egész típusú változó segítségével jelezzük az operátor dekJarád6jában , A paOJ.-
métcr értéke lényegtelen, csupán jelzés értékkel bír: közli a fordít6programma l hogy
postlIx operátorr61 van szó.

A prefix és a postfix közötti különbség


Mielőtt megírhatnánk S<"lját POStfIX operátorunkat. meg kell értenünk, miben különbö-
zik a prefix változatról. Emlékeztetőül: a prefIX operátor előbb inkrementál. majd kiol-
vas, a postfix pedig el6bb kiolvas, csak aztán inkrementál.
túltemelése

Amíg prefIX operátor esetén egyszeruen csak növeljük az értéket és visszaadjuk magát
az objektumot, addig postfIX esetén el6ször kell értékel visszaadnunk és csak utána
növelhet jük meg. Ehhez feltétlenül létre kell hoznunk egy ideiglenes objektumot.
Ez az ideiglenes objektum fogja tárolni az eredeti énéket, miközben az eredeti objektu-
mot megnöveljük. Ezt az ideiglenes objektumot fogjuk aztán vis.<;7..aadni, hiszen
a postflX operátomái az eredeti értéket várjuk és nem a már inkrementáltat.

Apropó Nézzük át még egyszer


Tegyük fel, hogy a programunkban szerepel a következő sor:
(l = X++1
Ha x értéke 5, akkor az utasrtás végrehajtása után a értéke 5 lesz, de x értéke már
6. Az x értékét kiolvassuk és hozzárendeljük a-hoz, csak aztán növeljük meg.
Amennyiben x egy felhasznál6i objektum, úgy postfix növelés esetén el kell tárol-
nunk az eredeti értéket (5) egy ideiglenes objektumban, aztán megnövelhetjük x ér-
tékét 6-ra, végül pedig visszaadjuk az ideiglenes objektumot, amelyet azután hozzá-
rendelhetünk a-hoz.

Figyelembe véve, hogy ideiglenes objektum tartalmazza a visszatérési értéket, ncm


haszná l1mtunk hivatkozássa l történ6 átadást, hiszen amint lefut 3 függv6ny, 34 ideigle-
nes objektumot nt:m tudjuk többé elérni. Éppen ezért érték szeri nti atadást kell hasz-
nálnunk.

A 14.3. Lista bemutatja, miként használjuk a preflX és a postfIx operátoroka!.

14.3 Ulla - Prefix és po.lfix opo,6torok hosznállla (pr...dposlfix.cppl


o: II 14.3 Lista
1 : II A this pointer 6rt6kének visszaadását mutatjuk be
2 : jinc1ude <iostream>
3,
4: class Counter
5: (
6: public:
7: Counter():
8: -Counter{) ()
9: int GetltsV",l(lconst { return itsVal;
10 : void SetltsVal(int xl {itaVal x;}
ll : const Countcr& opera t:or++ (l; II prefix
12 : const Counter operator+ + (int) ; /1 postfix
13 :
14 : private:
15: int itsVa1;
16: );
17 :
18: Counter : : Counter{) :
19: itsVal (O)
20 : fl
21 :
I
250 IV. rész • Haladó eszközök

22: const Counter& Counter : :operator++ () /1 prefix


23 , (
24 : ++itsVal:
25 : return *this;
26 :
27 :
28 : const Counter Counter : : operator++ (int) II post fix
29:
30 : Counter temp(*this);
31 : ++itsVal;
32 : return temp;
33 :
34 :
35 : int main()
36 :
37 : Counter i-
38 : std :: cou t « 'The value of i i s • «i.GetltsVa1! )
39 : « s t d ::end l;
40 : i ++ ;
41: std : : cout « 'Thc valuc of i is • « LGctltsValO
42: « std : : endl ;
43 : +Ii ;
44 : std :: cout« "The value of i is • «i.GetItsVal()
45 : « std : : endI ;

. "' .
46 : Counter a ~ ++i;
47:
48:
I:Itd :: cout «
std : : cout « ."The value of
and i:
« a . GetItsVa,l();
« i .GetItsVal () « std : : endl;

.'The value. of« ., . «


49 : II .. i.+;
50 : std : : cout « a . GetltsVal();
51: std :: cout « and i : i .GetltsVal () « std: :cndl;
52: return O;
53 :

vllIue of i
The
The
The
vaIue
vllIue
of
of
i
i
"O
i. l
i. 2
The vaIue of 3 and i: 3
The vaIue of "'
a , 3 and i: 4

A 12. sorban deklarált postfIx operáton a 28-től a 33-ig sorig ccrjcd6en implementálmk.
ÉszrevchetjOk, hogy a 43. sorban sze replő prefix operálornál nem használtuk az egész
lípusú jelz6l (x).Postfix operátor esetén viszont így jelezzük, hogy poscfixr61 és nem
prefixr61 van szó. Természelesen az x énékét nem használjuk semmire.
14.6ra • túltemelése 251

operator+
Az inkrementál6 operátor egyopemndusú (unary), tehát csupán egy operandusr vár 16-
Jünk. Ezzel s7.emben az összeadás operátora (+) kétoperandusú (binary), két operan-
dust kell neki megadnunk (például a + b). Magától adódik a kérdés: hogyan terhelhet-
jük fÚ] a -+- operátort a Counter osztályunk esetében?

A célunk tehát az, hogy kél Counter típusú változót össze tudjunk adni az alábbi
módon:
Counter varOne, varTwo, varThree ;
varThree = varOne -+- vurTwo l

Ismétlem: nyugodtan írhatnánk mondjuk egy Add () nevCí függvényt, amely kél
Count er típUSlI paramétert vá r és egy Counter típust ad vissza eredményként. EZl
:1 megközelítést mutatja be a l4A-es lista.

14.4 Ulla - Az Add(l függv6n, laddlunction.eppl


o: /I 14 .4 Lista
1 : II Add fOogvény
2: linclude <iostream>
3,
4: class Counter
5: (
6: public :
7: Counter{);
8: Counter{int initialValue) ;
9: -Counter(){}
10 : int GetltsVal()const ( return itsVal;
11 : void SetItsVal(int x) {itaVal " x; }
12: Counter Add(const Countcr &);
13 :
14 : private:
15 : int itsVa1 ;
16 :
17 : };
18 :
19 : Counter: : Counter (int initia1Va l ue) :
20 : HsVal (initia1Va1ua)
21 : ()
22 :
23 : Counter: : Counter() :
24 : itsVa1(O)
25: {}
26:
27 : Counter Counter : : Add(const Counter l< rhs )
28 :
29 : return Counter(itsVa1~ rhs.GetItsVal(»;
30 :
252 1 IV. rész • Haladó ,s2i<özök

31 :
32 : int main ()
33 :
34: Countcr varOne(2) , varTwo(4) , varThree;
35 : vdrThree = varOnc .Add (varTwo) :
36 : std::cout« ' varOne : • «va rOne.GetltsVal()« std: : endli
37 : std : : cout « 'var'l'wo : «varTwo.GetltsVal()« std : : cndl ;
38 : std : ,cout « ' varThree : • « varThree.Gctlts Val()
39 : « s t d :: endl ;
40 : return O:
41:

va rOne : 2
varTwo : 4
varThree : 6

AZ Add () filggvéllyt a 12. sorban deklaráljuk. Cím szerint átadjunk egy Councer típusú
konstan!;t, mcly aZl a l:rtékct tartalmazza, melyet hozzá szeretnénk adni az aktu:'ilis ob-
jekrumhoz. Counter típusú objektumot ad vissza, melyet a 35. sorban hozzárcndclünk
a balo ldalhoz. Összefoglalva: varOne az objektum, varTwo az Add ( ) -nak ~Íladott para-
méter, varThree-be pedig elhelyezzük a végeredményl.

Ahhoz, hogya varThree-t l6trehozhassuk inicializ:ilás nélkül , alapértelmezett


konstmktort kell írnunk hozzá. Az alapértelmezett konst!l,lktor inicializálja az itsVal
értékél O-ra, ahogy azt a 23-25 sorok között is megfigyelhctjük. Nem nulla értékkel ini-
cializáljuk varOne és v.!IorTwo objektumokat, ezért hozzájuk másik konstruktorra van
szükség, melyet a 19-21 sorokban találunk. Másik leheL'>éges megoklása ennek a prob-
lémának, ha alapértelmezésként o-ára á11ítjuk a konstruktorban (8. sor) az értéket.

Az operator+ túltemelése
A 14.4-es lislában az Add () függvényt a 27-30 sorokban val6sÍlottuk meg. Noha a meg-
oldás működ i k , eléggé ~Ie rmészetellenesnek " tűnik használat közben . Az operator+
túlterhelésével sokkal természetesebbé tehető a Counter osztály használata . Ezt a 14.5-
ös listában mutatjuk be.

0 , 1114 . 5 Lista
1 , lj Az összeadás operátor (+ ) túlterhelóse
2 , lIinclude <iostrearn>
3,
4, class Counter
14.6... Oporátorok túttemelés. 1253 ..r.,
5,
6: public:
7: Counter () ;
8, Counter(int initialValue) ;
9: _Cou nter() {J
10 : i n t Ge tltsVal()const { ret u rn i tnVa l ;
11 : void Set ItsVal(int xl (itsVa l '" x ; )
12 : Counter operator+ (const. Counte r &);
13 : pdvate :
14 : int itsVal ;
15 : );
16 :
17 : Counter : : Counter(int initialvalue) :
18 : itsVal{initialValuel
19 : ()
20 :
21 : Countcr : : Count er() :
22 : itaVal ( O)
23 : ()
24 :
25 : Counter Counter : : operat.or-+· (const Counter " rhs)
26: (
27 : return Counter(itsVal + rhs.GetltsVal() ;
28 :
29 :
30 : int main()
31: (
]2 : Countcr varOne(2) , varTwo(4) , varThrec;
3] : varThree = varOne + varTwo ;
34 : s td: : cou t « 'varOne : • « varOne . Getl t s v al( ) « s td: : cnd l ;
35 : std : : cout « ' varTwo : • « varTwo.GetltsVa!() « std : : e ndl;
36 : std : : cout « 'varThrec: ' « varThree.GetltsV51()
37 : « std : : endl ;
36 : return O;
39 :

v5rOnc : 2
varTwo : 4
varThree : 6

Ezek után meghívhatjuk az összeadás operátorál úgy, ahogy azt a 33. sorban is [esszük:
varThree : varOne + varTWO;

A fordít6 ilyenkor úgy értelmezi, mintha CZl írtuk volna:


varThree = varone ,operator+(varTwo) ;
254 1 W. rész • Haladó ,,",özök

Ennek megfelelően meghívja a 12. sorlY,m deklarált és 25-28 sarokb,1n megval6síton


metódusL Összevetve a korábbi Add { ) függvény deklarációjával és definíciójával szin-
te nincs is különbség. Ami al alkalmazást illeti, ott viszont szembeötl6 az e ltérés. Men
hiszen mennyivel tennészetesebb a
varThree ~ varOne + varTwo ;

mint a

varThree = varOne.Add{varTwo) ;

Nem nagy változt.atás, de ahhoz épp e lég, hogy könnyebben átlátható és használható
programot kapjunk. Látható tehát, hogy operátorok t(JlterheJéséveltulajdonképpen
explicit függvényhívásokat helyeuesíthetünk.

Az operátortúlterhelés korlátai
/I. beépíLCU típusok opcrálom il (ilyen például <LZ int) nem terhe lhcljük túl. A prece-
dencia nem változtatható meg, mint a hogy az operáto r o perandusain:lk SZÍlma (:t dly)
sem. Új operálorokat sem hozhatunk létre, tehát nem dekbrálhaljuk mondjuk a *"'-ot
a hatványozás o perátorakénl.

Mit célszerű túlterhelni?


Az operátortúlterhelés le het6sége a c++ egyik olyan terillete, melyet kezdő programo-
zók gyakra n feleslegesen használnak s6t visszaélnek a lehct6sl!ggel. Csábító lehet6ség
ugyanis újsze n1 és érdekes módon felhaszná lni a legzavarosabb operátorok31, azonban
ez á ltalában zagyva és nehezen értelmezhet6 kódot eredml!nyez.

Egy ideig persze vicces lehet a + operátorral kivonást, a ., operátorral meg összeadást
végeztetni, dc profi programozók soha nem tesznek ilyel. Veszélyforrás az is, ha valaki
jó szándékk~d , de egyéni logik:lval használja az operálorokat - pé ldául + operi'tlo rL
használ betúk összefúzésére, vagy j - t karakterláncok relbontáslíra. Fel le he t pe rsze so-
rakoztatn! az cff6le megoldások mellett s:.:6l6 érveket is, dc minde n eselbe n körülLe-
kint6en kell eljárnunk. Ne feledjük: az operátoltúlterhelés célja a progl<ununk stabilitá-
sának növelése és forri'tskód érthetóségének javítása.

operator=
Ugye e mlé kszünk, hogy a fordít6 biztosít alapértelmezeu konstruktort, destruktort és má-
sol6 konstruktort. A negyedik és egyben utolsó függvény, melyet a fordító biztosít-
amennyiben nem adunk meg sapt vált07..atol- a hozzárendelési operátor (operator= (l).
14. 6ra • tútterhelése

Ezt az operátol1 hívjuk meg, valahányszor értéket adunk egy objektumnak Például:
CAT catOne (S , 7);
CAT catTwo(3,4) ;
II .. a program többi része
0

catTwo '" catOne;

Példánkban létrehoztuk a catOne-t és a megadott pamméterekkel (5,7) inidalizáltuk


az itsAge és az itsweight tagváltoz6kat. Ezután létrehOlllIk a ca tTwo-t, amit ismét
a megadott paraméterekkel (3,4) inidalizáltunk. Az utolsó sorban azt{m színre lép
az alapértelmezett hozzárendelési operátor (operator=):
catTwo '" catOne;

A hozzárendelés eredményeképpen a catTwo objektum itsA<;I'e és itsWeight változói


felveszik a catOne megfelel6 értékeit. Az utasítás lefurtatásll után a catTwo itsAge ér-
o

téke tehát 5 lesz, a catTwo. i t s Weight értéke pedig 7.

Figyeljük meg, hogy példánkban nem hívnlk meg a másoló konstruktort. A catTwo
ugyHois már létezett, nem kellett azt létrehozni, í!,'Y a fordító intelligens módon a hoz-
zárendelési operátort hívta meg.

A 13. órában megtárgyaltuk 3 felszíni Clagonkénti) és a mély (közvellen) másolás kÖZli


különbséget. A felszíni másolás csak a tagokat másolja és dinamikusan kezelt tagokn{d
mindkét objektum ugyanarl"..t a címre mutal. A mély másolás ezzel szemben lefog;lalja
a szükséges memóriaterületel. Az alapértelmezett másoló konstruktor m(íködéséről
már láthaUunk korábban egy ábrát 03.1. ábra), lapozzunk vissza, ha szükségesnek
érezzük.

A hozzá rendelési opecltornál meglehet6sen hasonl6 a helyzeT, mint a m{\soI6 kons!·


ntktomál. Persze itt van egy kis csavar a dologban: a cat'J'wo objektum már létezett, te·
hát tartozott hozz<1. lefoglalt tárterület. Hogy elkeri.iljük a memóriaszivárgást, törölnünk
kell ezt a terü letet. A hozzárendelési operátor implementá lás.1 során tehát első dolgunk
a mutatóhoz tartozó memóriaterület [örlése, kitakarítása.

De mi is történik, ha a ca tTw"o-L önmagához rendeljük, va lahogy így:


cat TWo = catTWo ;

Persze senki nem csinál ilyesmit szándékosan, de a programnak akkor is le kell mdnia
kezelni. Van valami, ami viszont még ennél is fontosabb: az efféle értelmetlenek túnd
hozzárendelések véletlenül, rejten módon is bekerülhetnek a kódba , például úgy, hogy
a hozzárende1ést mutatókon keresztül végezzük, amelyek elfedik 3 háttérben történő
eseményeket.
25S I IV. rész' Haladó eszközök

Ha ezt a problémát nem kezeltük körültekintóen, a catTwo törölheti saját memóriafog-


lalását Aztán amikor a hozzárendelési operátor oda érkezik, hogy ténylegesen rnásolná
a mem6ria tartalmát, akkor szembesül csak a nagy problémával: nincs is mit másolni.

Ezt elkerülendő
ellen6riznünk kell, hogy a hozzá rendelési operátor jobb o ldalán sze-
replő objekcum nem ugyanaz-e? Ezt a this mUlató vizsgálatával teheljük meg. A 14.6
Listában egy osztályt mutarunk be, amely rendelkezik hozzárendelési operdlorral.

14.6 Uata - Houárandelési operátor (assignoperator.cppl


o: /I 14.6 Lista
1 : II Másoló konstruktorok
2: #include <iostream>
3,
4 : c lass CAT
5: {
6 : public :
7: II alapértelmezett konstruktor
a : II cl mnsoló konstruktort és destruktort kihal]Ytuk
9: int Gethge() const { return *i tshge; }
10 : int GetWaight {) const { return *itsWeight;
ll: void SetAge(int age) ( ' itsAge = age ; )
12 : CM' operator:c(const CAT &) ;
13 :
14 : private :
15 : int *itsAge;
16 : int " itsWeight;
17 : );
18 :
19: CA'!' : : CAT()
20 : (
21 : itsAge = new int ;
22: itaWeight = new int ;
23 : *itsAge • 5;
24 : *itsWeight = 9 ,
25 :
26 :
27 :
28 : CAT CAT: : oper ator=(const CAT & rhs)
29 : (
30 : i f IthiEl == &rhs)
31 : return " th i s ;
32 : delete itsAge;
33 : delete itsWeight;
34: itshge = new int;
35 : itsWcight = new int;
36 : *itsAge = rhs .GetAge{);
37 : " itsWeight = rhs . Ge tWeight () ;
38 : return *this ;
39 :
40 :
41 :
14.6ra ·

42: int rnain()


43 :
44: CAT frislcy:
45 : std : :cout « "frislcy's age: • « frisky.GetAge()
46: « std: : end1 ;
47: std :: cout« 'Setting frislcy to 6 .. . \n' ;
48 : frisky . SetAge(6) :
49: CAT whiskers:
50 : std: : cout « "whiskers' age: • « whiskers.GetAge()
51: «std: : cnd1 ;
52 : std : : cout « 'copy ing frisky to whiskers ... \n' :
53 : whiskers ~ frisky :
54 : std: :cout « 'whiskers' age : • « whiskcrs.GetAge()
55 : « std : : end,l;
56 : reLurn O;
57 :

frisky' s age: 5
Sctting fri!;ky to 6 ...
whiskcrs' age: 5
copying frisky to whiskera ..
whiskcrs' age : 6

A 14.6 Listában ismét viszomláthatjuk a már jól ismert CAT osztályt, (gy mern helytaka.
rékoss.1gból kihagynJk a másol6 konstruktort és destruktor!. A 12. sorblln dekbráltuk
a hozzá rendelési opcrátort, melyet kés6bb a 2s..39 sorokban definiáltunk.

A 30. sorba n n aktu ális objektumot, amelyhez hozzárendelünk, megvizsgáljuk, hogy


nem egyezik-c azzal, amelyet h ozzá szeretnénk rendelni. Az elle nőrzést az rhs mem6·
riacímének ~s a this memóriacímének összehasonlításával végezzük el.

Természetesen az egyen lőség operátort (,,"') is túlterhelhetjük, ennek köszö nh etően sa·
ját kritériumok alapj{tn dönthetjük el, mikor "egyen lő " két objekntmunk.

Apropó Kezdjünk tiszta lappal


A 32·töI35·ig terjedő sorokban töröljük, majd újra létrehozzuk a tagváltoz6kat
a memóriában. Noha ez nem kötelező, jó és korrekt programozói szokás, mert meg-
kfmél a mem6riaszivárgás okozta problémáktól, amikor olyan változó hosszúságú
objektumokkal dolgozunk, melyek nem rendelkeznek saját hozzárendelési operátor·
ral.
25S I IV. rész • Haladó ,szI<özök

Konverzi6s operátorok
Mi történik ha egy beépített típust - legyen az egész vagy el6jel nélküli rövid egész -
kívánunk egy felhasználói osztály példányához rendelni? A 14.7-es listában a Counter
típusú objekru mhoz kísérelünk meg int tipusú váltoZÓI hozzárendelni.

FIgyelem! Ne lep6djünk meg


A 14.7. Lista kódját nem lehet lefordítani . A hibaüzenetet az elemzés elolvasása
után fogjuk csak megérteni.

o: /I 14 . 7 Li!;ta
1 : II Ez a példa nem fog leforduln! !

,.
2: j include <iostream>

4: class Counter
5: (
6: public :
7: Counter{) ;
8: ~Counter(){}
9: int GetltsVal{)const { return itsVal ;
10 : void SetItsVal(int x l litsVal = x : }
ll : private:
12 : int itsVal;
13 : l;
14 :
15 : Counter : : Counter() :
16: itsVI!Il(O)
17 : ()
18 :
19 : int /'IIlIIin()
20 : (
21 : int theShort = 5;
22 : Counter theCt r = the Short ;
23 : s td : : cout « "theCtr : • « theCtr.GetItsVal()
24 : « std: : e nd l ;
25 : r eturn O;
26 :

" counterconvert.cpp " : E2034 Cannot convert 'int' to 'Counter' in


... function main() at line 22
14. óra • tú.emelése 259

A 4~13 sarok között deklarált Count er osztály csupán alapertelmezctl konstruktorral


rendelkezik. Nem rendelkezik eb'Yooi metódllssal arra vonatkozólag, hogyan alakíthat
át int típusú értéket Counter típusú objektummá, így fordítási hibát kapunk a 22. sor-
ban. A fordító nem mdja kitalálni - hacsak nem közöljük vele - hogyan kell hozzárcn-
delnie egy i nt típusO vá!toz6t az itsval-hoz, mely az osztály tagváltozója.

A 14.8. Listában megfelelő konverzi6s operátor létrehozásával javítjuk a hibát:


a konstruktor al. int típusO változó alapján létreholza egy Counter objektumot

14.' Lista - int étalakItása Counter..,é


o : II 14. 8 Li sta
l : II Konstruktor , mint konvcr% i 6s operátor
2: #include <iostream>
3.
4: class Counter
5: (
6: public :
7: Countcr() ;
8: Counter ( l.nt val) ;
9: -Counter() { l
10 , int Gc t ltsVal()conS L { rn turn itsVal ;
ll : void SetItsVal(int xl (itsVal = X; l
12 : private :
13 : int itsVal :
14 : l;
15 :
16 : Counter : : Count er( ) :
17 : itsVal {Ol
18 : ()
19 :
20 : II Konstruktor túlterhelés
21 : itsVal (val)
22 : (J
23 :
24 : int main()
25 : (
26 : int theShort " 5 :
27 : Counter thcC tr = t heShort ;
28 : std: : cout « " thcCtr , • « thectr.GetltsVal() « std : : end1 ;
29, re tu r n O,
30 :

thectr : 5
I
260 W. rész • Haladó eszl(özök

A lényeges változás a 8. sorban lálható, ahol is il konslruktolt terhe1tük túl úgy, hogy
j nt típust fogadjon. A mcgv'116sílás a 20-22 sorokban ](llkHfJ, A módosítások eredmé-
nyeképp ez a konstruktor az int típusból Counte r lípust hoz létre.

Ezután a fordító már meg tudja hívni a konstruktort, amely in t típust vár argumentum-
ként. De mi történik, ha például megfordítjuk a hozzárendelést az alábbi módon?
1 : Coun t.er thcCt.r(5) ;
2 : int theShort ~ theCtr ;
3 : cout « " theShort : • « theShort « endl ;

Ez ismét fordítási hibát fog eredményezni. Noha a fordító m{l r tudja, hogyan hozzon
lé tre Count er lípusú változó! i n t típusb61, nem tudja megfordítan i a hm:zárendelést.

Az intO operátor
I logy megoldjuk a femi problé m{u - vagyis hogy miként csinÍl lhatunk Counter típus-
ból int típust - és az e hhez hasonlókat, használjuk a C++ konverziós o peráto rnil. Ezc k
a konve rziós operátorok ké pessé tcszik osztá lyunkat arrd , hogy implicit módo n kon-
vcrtá ljunk objektumokat a beé pített típusokra. A 14.9. Listában mutatjuk be ezl. Egy
megjegyzés cSllpfln: a konverzi6s operátoroknlil nincs megadva visszatérési típus an-
nak e llenére, hogy az átalakított értéket visszakapjuk.

14.9 Ulta - Countar tfpust intO típust alakftjuk Icounterconvamntcpp)


o: II 14. 9 Lisla
1 : II konverzi6s operátor
2: linclude <iostream>
] ,
4: class Countar
5,
6: public :
7: Count ar(j ;
S: Co un ter ( i nt v a l j ;
9: _Coun t ar(j { j
10 , int GetItsVal()con st { return itsVa 1 ;
11 : v o i d SetItsVa1(int xl ( itsVal =: x ; )
12 , operator unsigned short C) ;
13 : private :
14 : int itsVal ;
15 : );
16 :
17 : Counter : : Counter() :
lS , itsVa !(O)
19 : ()
20 :
21 : Counter : :Counter(int vall ;
22 ; itsVal (val)
23 ; {}
24 ;
25 : Counter : : operato r uns i gned short ()
26 ; {
27 : return ( int (itsVal) ) ;
28 :
29 :
30 : int main()
31 ;
32 : Counter e t r(5) ;
33 : int theShort • etr ;
34 : std : : eout « " theShort : " « theShort « std : : endl;
35 , return O;
36 :

theShort : 5

A konver.d6s o peT"átort a 12. sorban deklarlilmk. Figyeljük meg, hogy nincs visszatérési
értéke. A függvény implementki6ját a 25-28 sorokban t;:tláljllk. A 27. sor adja vissza
az i tsVal értékét i nt típusra alakitva.

A fordít6 most már tudja, hogyan kell egész (int) és Counter típusú objektu mokat
egymásba átalakítani oda-vissza, vagyis a hozzárendelésekkel kapcsolatban teljes
a szabadság.

Kérdések és válaszok
Kérdés: !H/él1 definiálunkfe/ül egy operátol1, hll tagmelódllsl is lelrehozhall/./Ik?

Válasz; Túlterhelt operlitorokkal könnyebb dolgozni, feltéve persze, hogy j61 ismerjük
a viselkedésüket. Lehetővé teszik osztályaink számára, hogy a beépített típusokhoz ha-
sonlóan viselkedjenek.

Kérdés; Mi ti különbség II másoló konstluktor és a hozzá/"CI"Ide/ési operátor i...'Özött?

Válasz: A másoló konstruktor új objektumot hoz létre, amelynek értékei megegyeznek


a meglévő objekcuméival. A hozzárendelési operátor ezzel szemben egy már meglév6
objektumot módosíl, mely felveszi egy másik objektum értékeit.
262 1IV. rész • Haladó 8S2I<öZök

Kérdés: Mi förlélllk azzal az int típusú pammélerrel, amit cl postfix operátorok lúlter-
lIc1éséllél haszlIáftmkl

\Iá/asz: Semmi. Az az int csupán annak jelölésére használjuk, hogy postfix opedtort
kívánunk túlterhelni.

Gyakorlatok
Most, hogy már ismerjük az operátorok tú ltcrhelésével kapcsolatos módszereket, vála-
szoljuk mcg a kvízkérdéseket és ;1 gyakorlatok segítségéve! mélyítsük el a 1l1egszerzelL
tudást.

Kvfz
1. Miétt nem hozhatunk létre új operátorokm, mim például a **-ol hatványm.áshoz?
2. Miért nem definiálhat juk felül a már meglév6" operátOrl úgy, hogy hatvánY0-
zásként viselkedjen?
3. Miért különbözik a szintaxis prefIX és postfix inkrcmcntálás/dekremcntálás esetén?
4. Mil csinálnak a konverzi6s operátorok?

Feladatok
l. A 14.6-05 Jistát (assignoperator . cpp) írjuk át úgy, hogy az c&'YenI6ség operá-
tort (==) terheljük túl. Használjuk az operátol1 frisky és whisker életkorának
ÖSSzchasonlítására.
2. Irjuk át a 14.3-as példát (Preandpost fix. cpp) úgy, hogy al inkrernentál6 ope-
rátor hatására csökkenjen az itsVal értékc. Ha megcsinfiltuk és Iln1köc\ik, te-
gyük félre egy kis időre (egy órúm vagy akár egy napr::l). Aztán nézzük meg újra
a programot. Még mindig egyértelmúnek tűnik, hogy hogyan viselkednek
az operátorok? E.'ielleg némi zavart érzünk, mikor a progmlllot fu ttat juk? Ebból
talán kiderült, miért nem szabad a túherheléstleljesen új fu nkciók kialakítására
használni.
3. Módosítsuk a 14.5. Listában bemutatott kódot (pl usoperator . cpp) Ú&'Y, hogy
amínusz operátort is túherhcljük. Úgy írjuk meg a kódOL, hogy az csupán egy
egyszeru kivonást hajtson végre, majd futtassuk tc néhány különböz6 bemenel-
tel. Számít az objekrumok sorrendje az utasításokban?
14. 6ra • Operátorok Memelése 1263

Válaszok a kvízkérdésekre
1. A ** nem része a C++-nak, így magát a fordítóprogramol kellene átírni, hiszen
nem tudja, hogyan kell azt kezelnie.
2. Megtehetnénk és talán lenne is értelme. Azonban más programozók 7A v;lrónak
ta lálnák - 561 akár akár mi magunk is néhány év múlva, A nyelvben semmi sem
akadályoz meg abb:m, hogy ezt megtegyük, mindazonáltal ha egy m6d van rá,
(,lián ne nehcótsünk meg a sajálll1unkánkat.
3. A két operátor viselkedése teljesen különbözik Prefix jelölés esetén előbb vég-
rehajtjuk a műveletet és csak azután OIV:lssuk ki az értéket. Postfix használat
esetén ezzel szemben előbb kiolvassuk az értéket, csak aztán hajtjuk végre
a műveletet. A túlterhelést használó programnak utánoznia kell eZl a viselkedést.
Pontosabban nem kell, de ha nt:.:m szeretnénk magunkat megutáltatni a kollégá-
inkk:tl, akkor kövessük ezt a szokást!
4. A konvertáló ope~torok segítségével objektumot alakíthatunk beépített típussá.
Ebben az órába!' a counter objektumot tanulmányozluk és a végeredményt
egyszenl egészkém kívántuk megkapni. A cél eléréséhez II konverziós o perálo-
rokat használtuk.
15. ÓRA

Tömbök
Mir611esz szó ebben az órában:
• Mik <I tömbök és hogyan deklaráljuk őket .
• Mik a karakterlíincok és hogyan hm;ználhal6k karaktertömbök karaktcrHinckl:nt
• A tömbök és mutllt6k kÖzölti összefüggések
• Hogyan használhal'Unk mulmóműveleteket tőmbökön

Mi az a tömb?
A tömh a7.onos típusú objektumok összessége. A tömböt ennek mcgre1e16en akár lIdat-
tárolásra szolgáló rekeszek sorozataként is elképzelhetjük. Minden egyes rekesz
a tömb egy-egy elemének felel meg.

Tömböt úgy deklarálhatunk, hogy megadjuk a benne tárolt adatok típusát, leírjuk
a tömb nevét, majd közvetlenül ez után szögletes zárójelek között megadjuk a fl.ltóin-
dexének Csubscripú felső határát Ez utóbbi rulajdonképpen nem más, mint a tömbben
tárolható elemek száma szögletes zárójelek közé írva. lássunk. C&'Y pl!ldát:
long LongArray [25] ;
I
266 IV. rész • Haladó ,S2!<özök

E deklaráció szerint a LongArray nevű tömb 25 hosszú egész (long) típusú elemet ké-
pes tárolni. Amikor a fordítóprogram e hhez a sorhoz ér, lefoglal annyi memóriát,
amelyben az előírt mennyiségű adat elfér. Mivel esetünkben egy long típus 4 bájtot
foglal, a fordító egy 100 bájtnyi összefüggő mem6riateruletet fog lefoglalni. Ez látható
a 15. L ábrán.

~
100 byte s

15.1. ábra
l;gy 16mb deJdarálÚ$a és tm/lak lia/ása

A tömbök elemei
Ha hozzá akarunk férni a tömb egyele mé hez, csupá n meg kell adnunk a tőmb neve
után annak indexét, mégpedig ugyanúgy szögletes zárójelek közé zátva, mint a dekJa-
rációban. Ügyeljünk rá, hogyatömbeleme nullától számoz6dnak, vagyis az els6 elem-
re a tOmbNeve[OJ formában hivatkozhatunk. E16z6 pé ldánknál maradva teh:ít
longArray [O I jelenti az imént létrehozott 2S demű tömbönk első elemét,
longArray [ll a másooik:u és így tovább.

A swmozásnak ez a módja els6re sokak si'..ámára zavaró. A SomeArray (31 dekla ráció
azt jelenti, hogya someArray nevű tömbnek 3 eleme van: SomeArra [O l ,
SomeArray [ll és SomeArray [2 ] . Általánosságban tehát a SomeArray [n] deklaráció
mindig egy n e l emű tömböt mkar, amelynek awnban az e l ső eleme Somell.rray [O 1,
az utolsó eleme pedig SomeArray [n- l l. Megint visszatérve saját példánkhoz a 2S ele-
mű longArray tömb elemei loangArray [O) -tóllongArray [24 J-ig számoz6dnak.
A lS.l. lista egy olyan kódot mutat, amelyben lérreho zunk egy 5 egész értékMI álló
tömböt, és egyenként feltölt jük az elemeit.

15.1. ÜS.. - Eg6szekb6I AI16 tömb lótni_II és blllZllálata limnay. pl


o: 1115.1. Lista - Tömbök
1 : *i ncl ude <iostrearn>
2,
3 : int main ()
4,
5 : int myArray[51:
6 : for (int i=O ; i<5; iH) II O- 4
7: (
8 : std : :cout « ·Valuc for myArray [" « i « • J : ";
9 : std : :cin » myArray[il ;
10 : )
ll : for (int i = O; i<5 ; i++)
12 : std : : cout« i« ": " «myArray[i] « "\ n";
13 : return O:
14 :

ValuE:l [oc myArray [Oj : 3


Va l ue fo c myArray [l J : 6
Valuc foc myArrny[2 1' 9
va lu e for myArraytJ j: 12
Va lue foc InyArray [ 4] : 15

0, 3

"
2, 9
6

3 , 12
4 , 15

-
Az ;. sorban tétrehozunk egy myArray nevtl tömböt, amely öt eb'Yszeres pontosságú
egész értéket képes tárolni. A 6. sorban kezdődik egy ciklus, amelybe n a ciklusválloz6
o-t61 4-ig megy, vagyis é ppe n megfelel egy 5 elemű tömb indexei nek. A felha sznál6t61
minden egyes elemnek bekérilnk egy értéket majd beírjuk az a megfele l ő töm belem be.

A tömbök számozásának megfelelőe n tehát az első bekért érték myArray r OJ -ba fog
kerülni, a másod ik myArray [lJ -be és így lovább. " második ciklus kiírja II bekért érté-
keket a ké perny6re.

Apropó A C++-ban a tömök indexelése nullával kezdődik


A tömbök számozása nullával és nem eggyel kezdődik. Ez sokak számára elsőre
elég furcsa, Igy a kezdők által elkövetett programozási hibák igen jelentős hányada
ennek a "nyelvi érdekességnek" köszönhető. Ne felejtsük tehát el, hogy ha van egy
10 elemből álló tömbünk, akkor arra csak TömbNeve [Oj -tól TOmbNeve [91 -ig hi-
vatkozhatunk. TOmbNeve (10 J elem egyszeruen nem létezik. Ha tehát a fenti pél-
daprogramunk 6. és 11. sorában megengedtük volna az egyenlőséget is a ciklusfeJ-
tételben « =5), akkor túlszaladtunk volna a tömb végén.
I
26S IV. ró" • Haladó eszközök

Tömbök túlírása
Amikor felül akarunk írni egy tömbe!emet, a fo rdítóprogram a tárolt adatok mérete és
a tümbindex alapján számítja ki a megfelelő eltolási é rtéket. Ha például a LongArray [5 ]
elemel akarjuk módosítani programunk valamely pontján, akkor a fordítóprogram veszi
a long típus hossz{n (vagyis 4 bájtoü, megszorozza azt az index énékével (az eredmény
20), majd a tömb elejét61 ennyi bájt távolságra beírj~_ a megadott értéket.

Mármost ha azt kérjük ól fordíl6p rogmffitól, hogy írja felül a LongArray [50 l elemet,
az akkor is pontosan ugyanezt fogja csinálni, függet le nül 31tó l, hogya kf:rdéses tömb-
nek nincs is ilyen sorszámú eleme. Kisdimítja, milyen messzire van a hivatkozou elem
a tömb elejétől - esetünkben 200 bájtnyira - aztá n beírja oda a megadott értéket. Csak-
hogy azon a mem6rÍ3!.erületen gyakorlatilag bármi lehetett, "mit felOllrva <'lZ eredmény
egyszeruen megj6solh:llatlan. Ha szerencsénk v,ln, a progL'"munk azonnal leáll valami-
lyen misztikus hibával. Ha viszont nincs, akkor a program tovább fu t, kiszámol min-
denféle őrü lt eredményeke t, mi pedig hosszan törhetjük :l fejünket, hogy hol is le het
a problém:L

A tömbök kezelésével kapcsolatban tehát a fordítóprogram úgy viselkedik, mint a vak


ember, aki elindult hazulról egyet sétálni. E l őször e lmegy az e lső házig, vagyis
a F6Utca (O] e lemig. Eddig a dolog rendben is volna. Ha most ilt azt mondjuk neki,
hogy menjen el :1 FG utca hatodik házához, "kkor azt mondja magában: most <'lZ els6
háznál állok, tehát men nek kell még öt Mznyit. Minden ház négy lépés hosszúságú, tc-
hát mennem kell összesen még húsz lépést. Ez egészen addig rendben is va n így, amíg
van a Fő utcának o lyan háza, amire hivatkoztunk. Ha viszont azt mondjuk a vak em-
bernek, hogy menjen e l II FC; utca 1OO-ba, de a F6 utc:{lban összesen csak 25 ház van,
akko r is menni fog 400 lépést, men 6 ezt nem tudja. Po ntosabban csak akarna menni
400 lépést, mert szinte biztos, hogy még mielőtt odllérne, beleesik valami gödörbe,
vagy elüti egy busz. Szóva l ha egy mód van rá, lcgyünk óvatOSlIk, hogy hova küldjük
a szerencsétlent.

Kerftésoszlop hibák
Az egyik leggyakoribb programozási hiba az, amikor pontosan egy elemmel ínLnk túl
egy tömböt. Ami azt illeti , annyira gyakori, hogy külön neve is van: ezt nevezzük kerí-
tésoszlop hibának. A név egy az emberi gondolkodásmód nucsaságát kihaszná ló talá-
165 kérdésból ered. Ha megkérdezünk embereket, hány kerítésoszlop kell egy tíz mé-
ter hosszú kerítéshez, ha méterenként egy oszlopot akarunk leásni, akkor a legtöbben
azt fogják válaszolni hogy tíz. Pedig valójában tizenegy, hiszen a végére is kell egy zá-
r6 (lásd a 1;.2. ábrát).
15. óra· 269

15.2. ábra
Hány osz/op I..'CII egy tíz méter lIosszrí kerlléshez? ..

Ez az eggyel való túlszaladásos probléma rengeteg programozó életét keseríti meg. Id5-
vel aztán megszokja az ember, hogya 25 elemű tömb utolsó eleme a huszonnegyedik,
men a számozás nullát6l indul, de addig elköveti néhányszor II kerítésoszlopos butasá-
got. CA C++ programoz6k egyébként gyakran nem értik, miért nincs az épülctekbel1
nulladik szint. Olyannyira nem, hogy egyesek tényleg a négyes gombot nyomják mcg
a liftben, ha az ötödikre akarnak menni. Ezen aztán a többiek jól szoktak sz6mkozni.)

Tömbök előkészítése
Ha egy tömb elemei egyszeru beépített típusok (például egészek vagy kaT"J.kterek), :ik-
kor a deklarfici6val egy időben inidaliz..i lhatjuk is. Ehhez nem kell egyebet tenni, mint
a neve után egy egyenl6ségjelet írni, majd kapcsos zárójelek között fe lsorolni az ele-
mek énékét. L-',ssunk egy példát:
int IntegerArrey[51 = ( 10 , 20 , 30 . 4 0 , 50 J ;

Esetünkben az IntegerArray öt egész számot tanalmaz. Az I ntegerArray [O J elem-


be tőnénetesen 10 kerül, az IntegerArray [1) -be 20, és így tovább.

Ha a deklar:'id6ban nem adjuk meg a tömb méretét, akkor II fordítóprogram akkora


helyet fog lal le neki, amennyiben az inidalizációs értékek elférnek. Ha tehát azt í~uk
hogy
i nt Integerl\rray l ] = ( 10, 20, 30, 40 , 50 );

az eredmény pontosan ugyanaz lesz, mim az imént, amikor explicit módon megadtuk,
hogy öt elemb61 álljon a tömb.

Ha kíváncsiak vagyunk egy tömb méretére, meghatározhatjuk azt a már ismert


sizeo f () függvény segítségévcl a következ6 módon:
cons t int Integerl\rrayLength ~
sizeofClntege rAr rayl Is izeo f ClntegerArray[O } l ;

Példánkba az I nt egerArrayLengt h változó értéke a tömb hosszának, és az els6 elem


hosszának hányadosa lesz, ami értelemszerűen nem más, mim a töm elemszáma.
I
170 IV. rész' Haladó aszközök

Ha a deklarucióban megadjuk a tőmb méretét, akko r az inicializáció során nem ad h a~


tunk meg ennél több elemet. Így például a következő deklaráci6m fordítói hibát fo-
gu nk kapni, hiszen hat elemet próbálunk elhelyezni egy öt elemből álló tömbben:
int IntcgcrArray[S] '" { 10, 20, 30, 40, 50, 60};

Ugyamikkor kevesebb elemel adhatunk meg az inicializációban, vagyis a következő


forma helyes:
int IntegerArray[Sl = ( 10, 20);

Objektumtömbök
B:'lrmely objektumot - legyen az bc{:pítetllÍpus vagy a felh:lszná l6 álta l definiált - tá-
rolhan,mk tömbben. Amikor tömböt deklarálunk, tulajdon képpen csa k két információt
adunk meg a fordít6progf<lIUoflk: a tárolni kívánt objektumok típusát és azok számát
A fordító ez abpján már elrudja dönteni, hogy mekkO'd mem6rialerületet kell lefogla l-
nia és ez neki elég is. Hogy egy adott objektumtípus tárolásá hoz mennyi memória
szükséges, azt a fordítóprogram a megfelelő osztály deklarációja abpj{\o dönti el. En-
nek az osztálynak mellesleg kell legyen egy olyan ,\Iapértelmel.ett konstruklű'J. is, ami
arglllnentumok meg:ldás:l nélkül képes alaphe1yzetbe állítani az objektumokat, hiszen
ezen kereszrill hozz., lC:tre II fordítóprogram a tömbben tárolt valamennyi objektumot.

Apropó Az alapértelezett konstruktor


Itt szeretnénk emlékeztetni arra, hogy az alapértelmezett konstruktort nem föltétlen
a fordftóprogram hOlla létre automatikusan. létre jöhet [gy is, de megfrhatja maga
a fejlesztő is. A lényege mindössze annyi, hogy nem lehetnek b emenő paraméterei.

Egy tömbben tárolt objektum valamely adanagjához val6 bozzá(l':rl':s mindenképpen


kétlépésl foglal magában. Először is meg kell adunk a r l operátorml, hogy az objek-
tl.lmtömb h:'inyadik eleméről van szó, ntán pedig a tagoperátor (.) segílségével hozz{\-
férhetünk a kérdéses adattaghoz. A 15.2. Listában beml.ltatjuk, hogyan hozhatunk létre
egy öt CAT objektu mból álló tömböt.

15.2. Lista - Objektumtömb létrehozása (objarray.cpp)


o: II 15 .2 . Lista - Ob jektumok tömbje

,,
1: #include <iostream>

,,
3: class CAT

5: public :
6: CAT() ( itsAge = l; itsWeight =5; ) II Az alapértelmezett konstruktor
1: ~CAT() {} II A destruktor
8: int GetAge{) const I re t urn itsAge; }
9: int GetWeight() const { return itsWeight;
15. 6ra· Tömbök 1271

1 0 , vo id SctAgc{in t agc) { it s Ag e = age; }


ll :
12 : private :
13 , i nt i tsAge ;
1 4: int itsWcight ;
15 : };
16 :
17 : int mainO
18 ,
19 : CAT Littcr[5] ; II Létrehozunk egy 5 obj ek twnból .1116 tömböt
20 : int l ;
" 1: f or (i :: O; i < 5 ; i ++)
22 : Litterr i l . Set Ag e(2 ~ i +1) ;
23 :
24 : f or (i:: O; i < 5 ; i ++ )
25 : s td:: C':Q u t « · eat ~ . « i +1« . :
26 : « Li t t er r i ) . GetAge () « s t d ,; e nd l ;
27 : r e tu rn O;
28 :

oat U : l
c ot '2 :
3
cat '3 : 5
cat H : 7
cat '5 :
9

A CAT osztály d eklarációja a 3· 15. sorokban látható. Ahhoz, hogy CAT lípuSlI o bje ktu-
mokat tömbben lehessen tárolni a CAT osztálynak mindenképpen rendelkeznie kell
a lapérteJezelt konslruklorl".ll. Ennek a deklarációja látható a 6. sorba n. Ne fclcj lsí.ik cl ,
hogy ha hozunk létre bármilyen más konstruktort, akkor a fordít6progr::un állal biztosí-
toct alapértelmezett konstruktor nem jön létre, vagyis azt is nekünk kell megírnunk.
A 19. sorba n lefogla ljuk a megfelelő nagyságú mem6rüít egy o lyan tömbnek, amely 5
CAT típusú objektum tárolására képes. Ennek a tömbne k Litt er lesz a neve.

Az első fo r ciklus (2 1. és 22. sorok) beállítja az öt CAT objektum korát. /\ másod ik cik-
lus (24-26. sarok) újra egyenként végigmegy a tömbdemeken , és valamen nyinek meg-
hívja a GetAge ( ) metódusát

Lálható, hogy ez ut6bbi esetben mindig e lóbb a Litter [ il konstrukciót haszná ljuk,
amivel kike ressük a megfe l elő sorszámú tömbelemet, majd ezt követi a pont (.) operá-
tor, amivel a lagfüggvényhez férü nk hozzá .
I
272 IV. rész· Halad6 eszközök

Többdimenziós tömbök
Egy tömb tulajdonképpen nem egyéb, mint adatsor. Adatokat azonban nem C!iak sorba
lehel rakni. Elképzelhetjük például őket rácsba szervezve, ahol vannak soruk és oszlo-
pok is. Ezt a felépítményt nevezzük kétdimenziós tömbnek. Az els6 dimenzió felel
meg a soroknak, a második az oszlopoknak. Innen persze már csak egy lépés a három
dimenzió, vagyis a kocka. Itt a kiterjedések a széles.ségnek, magasságnak és a hosszú-
ságmIk felelnek meg. Mi több, akár háromnál több dimenziós tömbökkcl is dolgozha-
tunk, ha éppe n erre van szükség. Ezeket persze már sokkal nehezebb elképzeln i, vagy
valós, térbeli objektumokkal kapcsolatba hozni, de néha tényleg hasznosak.

Amikor deklarálunk egy tömböt, minden megadott index egy-egy tömbdimcnzi6nak


fc lcl mcg. A kétdimenziós tömböknek tehát két futó indexe van, a h{lromdimcmd6sok-
nak három és így tovibb . A C++-ban egy tömbnek gyakorlatilag lctszO"legcsen sok di-
menziój" Jehel, de l megoldandó fejadatok lermészetéb61 adódóan a legtöbbször egy-
vagy kétdimenziós tömböket használunk.

A kétdimenzi6s tömb használltára kiváló péld,l a sakktábl:!. A tömb egyik kiterjedése


a nyolc sornak, a másik a nyolc oszlo pnak felel meg (lásd a 15.3. ábrát).

."

15.3 ábra
A sakkIábia és a kéIClimellziós 16mb megfeleltetese

Tegyük fe l, hogy van egy SQUARE (mezŐ) nevű osztályunk. A sakktáblának megfelelő
tömb deklarációja ezzel a következőképpen nézhet ki:
SQUARE Board [8) [8) ;
l5.óra-

Ugyanezt az adatmenyiséget természetesen tárolhatnánk cm' 64 elemből áll6 egydi-


menziós lömhben is a következőképpen:
SQUARE Board[64l

Ugyanakkor ez a megoldás nem áll szerkezetileg olyan közel a valós, leírni kívánt ob-
jekturnhoz, mint az előző. A játék kezdetén a király az első sor negyedik mezőjén áll.
Figyelembe véve, hogy a sz.í lllozás most is nullától indul, ez a pozíció a következő­
képpen írható le:
Board{O] [3) ;

A fenti kód persze tartalmaz még egy fe ltételezést is, nevezetesen hogya sorokat az els6
index reprezentálja. A helyes indexelést és a mezők helyzetét szemlélteti a 15.3. ábra.

Figyeleml A tömbök helyfoglalásár61...


A többdimenziós tömbök félelmetes gyorsasággal képesek elfoglalni a teljes me-
móriát. Ne felejtsük el, hogy a szükséges tárhely számításánál az indexértékek itt
szorzódnakl

Többdimenziós tömbök inicializálása


Az egydimenziós tömbök höz hasonlóan természetesen a többdimenziós tömbök dek-
larációjában is megadhatunk kezdóértékeket. Ilyenkor az elemeket úgy kell felso rol ni,
hogya tömb utolsó indexe változ.zon a leggyorsabban, vagyis miközben az utolsó nö-
vekszik, addig !lZ összes többi legyen állandó. Nézzünk egy példát! Legyen egy töm-
bünk, melynek deklarációja a következő:
int theArray[Sll31

Ha ebben a dckl:iráci6ban kezd6értékckct adunk meg, akkor az első három a the-


Array [O) indexlÍ sorba keru I, a második három a theArray [1) sorba és így tovább.

Nézzü k például a következő deklarációt:


int theArray[5l[3 ] = { 1, 2 , 3 , 4, 5 , 6 , 7 , 8 , 9 ,1 0 ,1 1,12,13 , 14 , 15

Ha áuáthat6bban szeretnénk leírni az inicializációs részt, akár azt is megtehetjük , hogy


k i egészítő
kapcsos zár6jelekkel csoportosítjuk a felsorolás elemeir valahogy így:
int thcArray[5] [3] .. ( {1,2 , 3} ,
{4,5 , 6},
{7 , a , 9},
{lO,11.12},
{13,14 , 15} );
I
274 IV. rész • Haladó 'szKözök

A fordítóprogram automatikusan figyelmen kívül fogja hagyni II bels6 kapcsos zárójele-


ket, vagyis ezek csupán a programozó számára teszik olvashatóbbá a kódot, hiszen vi-
lágosabbá teszik, hogy melyik érték melyik tömbelembe kerül.

Az inicializáló é rtékek felsorolásában minden egyes számOl vessz6vel kell elválasztani


a szomszédaitól, mégpedig függetlenül att61, hogy alkalmaztunk-e bels6 7.1ir6jelezést.
Ezen kívül II teljes inicializációs szakaszt kapcsos zárójelek közé kell zárni , a deklaráci-
Ól pedig szokás szerint pontosvessz6 zárja. A 15.3. listában egy kétdimenziós tömböt
hozunk létre . Ennek az els6 dimenziója II O-4 indexeket, míg II második II 0-1 értékeket
veheti fel. Az alapértékeit úgy állítjuk be, hDb'Y minden l -es indexű elem a O-s indexíi
kétszerese legyen.

o: /I l.lstl ng 15.3 - Creati ng A Mul t idime ns i ona1 Ar ray


,,
1 : *inc l ude <i ost r e am >

3 : int mai n ! )
4: (
5 , int SomcArray [5 1 [21 .,. ( (O , O), {t , 21, (2 ,4 ), (J , 6), {4, 8}} ;
6 , for (int l = O; 1<5 ; i+ + )
7 : for (int jeD ; j<2 ; j ++)
8, (
9 , std : , eout « "SomeArray[" « i « ° l ( 0 « j « o ) ,o;
10 : ~td :: eout « SomcArray[il [jl« std : : end1 ;
11 , J
12 : return O;
13 :

SomeArray[OI [Oj : O
SomeArray[O) [l) , O
Somehrray [ ll [Ol :
SomeArray [ ll [ll ' ,
1

SomeArray [2 ) {O l : 2
Some Array[ 2 1 (l ): 4
Some Array (3 1 [0 1 , 3
Some Array {3 ] [l ]: 6
Somc Ar ray [ 4] [O ], 4
SomeArray[ 4] [l ]: 8

Az 5. sorban szerep l ő deklaráció szerinl SomeAr r a y egy kétdime nziós tömb. Az első
kite rjedése öt , a második kél elem hosszúságú. Ezr rehát egy 5x2-es rácsnak megfelelő
adalSzerkezel (lásd a 15.4. ábrát).
15. 6ra • Tömbök 275

, ,
3 3
2 2

o o
some Array [5[ [2[

15.4. ábra
L;iV' 6tszór J..'Clles rács megfelelóje a mell/óriálxIII

A tömb elemeit páronkt:nt inicializ;íljLlk, bár ami azt illeti , akár ki is számíUlatnánk őket
egy m egfelel ő k6drészlettel. A 6. és 7. sorban két egymásba :'igy:l zott ciklLlS kezd6clik.
A külső ciklus az első, míg :1 bels6 a második tö mbindexen halad végig, ,1111int az :1 ki·
írásból is lálható, hiszen SomeArray [ Ol [Ol-át SomeArray [O J [l J követi. Az első
tömbindex aktuális értékér természetesen csak akkor inkrementálja a progrdm, amikor
a második leheL<kges értékein végigért a bels6 ciklus. Aztán (jjra kezd6dik elölr61
a belső ciklus.

Néhány szó a memóriahasználatról


Amikor deklarálunk egy tömböt, pontOsan megmondjLlk a fordítónak, hogy hány cIc·
met szcretnénk benne tárolni. A fordítóprogram ennek htttÍls{]ra lefoglalja a szükséges
memóri atenlletet, amiL akkor is lefogla lva tan, ha a tömb egyes részeir soha nem is
használjuk. Ez a stratégia nem jele nt különösebb problémáI abba n az esetben, ha előre
meg lehet mondani, hogy a tömbnek legfeljebb hány eleme kell legyen. Egy sakktábla
például mindig pontosan 64 mezőből áll , dc az is sejthető, hogy egy macskának (CAT
objektum) eb'Yszerre körülbelül 1-10 kölyke lehet. Vannak ugyanakkor esctek, amikor
fogalmun k sincs, hogya program futása sor{m hány adon típusú elemre lesz majd
szükségünk. Ilyenkor kifinomultabb adaLo;Zt;'rkezctckct kell haszn:\ lnunk. Ebben
a könyvben éppen ezért szó esik még a mutat6tömbökről, a dinamiklIs memóriában
lefoglalt te rül etek tömbkém való használatáról, illetve néhány egyéb adattá rolási szer-
kezctr61 is. A 19. órában például az úgynevezett láncolt listákróllesz majd szó. Ugyan-
akkor a még e nnél is kifinomultabb adaLo;zerkezctck, amelyek kifejezetlen nagy
mennyiségIl adat hatékony tárolására használllatÓk már túlmutatnak e könyv keretein.
Errol a témáról szólnak azok a bizonyos ,.Algoritmusok és adatszerkezetek" címú tan-
könyvek, amelyekb61 számos programozó tanul a különböz6 cgyelcmi és főiskolai
kurzusokon. A programm..ásban amúgy az a szép, hogy soha nem lehet a végére érni.
Mindig van még mil tanulni , íb'Y mindig van még másik könyv is, amil el kellene olvas-
ni. Az út végtelen.
I
27S IV. resz • Ha~d6 eszközök

Mutatótömbök
Az eddig tárgyalt tömbök közös vonása, hogy valamennyien a vermen (stack) kapnak
helyet. Mivel azonban a veremteriilet a rendszerek többségénél meglehetósen korlátos,
a nagyobb tömböket cé l szerű a dinamikus memóriában létrehozni . Lehetőségünk van
például arra, hogy a tömbbe szervezett objektumokat ténylegesen :t dinamikus memó-
riában hozzuk létre, magában a tömbben pedig csak egy-egy őket dr11i:6 mutat6t t:írol-
junk. Ez OL módszer önmag{tlxlll drasztikusan csökkenti él verm~n tárult adatmennyisé-
geL A 15.4. Listában átírrl.lk a 15.2. Listába n bemutatott kódot llgy, hogy ell a kombi-
nált módszert használja. Mivel így sokkal kevésbé kell aggódunk altól , hogy elfogy
a rendelkezésre álló mem6ria, a tömb méretét S-r61 500-ra emeltük és álneveztünk
Litter·r61 Family·re.

15,4. Lista - Tömbelemek tárolása a dinamikus memóriában (arravonheap.cpp)


0, /I 15.4 . Lista Objektumok eimét tároló tömb
l: .include <iostream>
2,
3 , class CAT
4: (
S : public :
6 : CAT() ( itsl'lge '" l: i tsWeight =5 : ) II alapértelmezett konstruktor
7 : ~CA'I'() {} II dest.r uktor
8 , int GctAge{) const ( rct u rn itshge: )
9 : int GetWeight{) const ( return itsWeight;
10 , void SetAgc(int age) ( itsAge = <.Ige; )
ll :
12 , privata :
13 : int itsl'lge ;
1 4 : int itsWeight;
15 , );
16:
17 : int main()
18 :
19: CAT • Family[500);
2 0 , int 1;
21 : CAT • !;leat ;
22 : for (i ." O; i < 500; i H)
23 : (
24 : pCat = new CAT;
25 : pCat->SetAge (2 *i +1) ;
26: Family[i) = pCat ;
27 : )
28 :
29 : for (1 = O ; i < 500; i+ + )
30 : std : : cout « "Cat ff' « i +l « " :
31 , « Family[ i]->cctAge() « s td, : end l ;
32 :
33 : for (i = O; i < 500 ; i ++ )
34 : {
15. 6ra • Tömbök

35 : delet e Family [i l ;
36 : Family[i ] NULL ;
~

37 : )
]8 :
39 , return 0 ,
40,

menet
Cat fl, l
Cat '2 : 53
c.t '3 :
Cat M499 : 997
Cat ~500 : 999

A CAT osztályt a 3-15. sorokban deklaráljuk. Ez a form .. amClgy mindenben megegyezik


a 15.3. Listában bemutatott:!!. Ugyanakkor ez alkalommal a 19. sorban talá lható tömb
neve m!lf Family, és a dekl:lrációja szerint 500 darab CAT típlJSÚ objektumot dmz6
mulató! képes tá rolni.

A nyitó ciklusban C22-27. sarok) 500 új CAT objektumot hozunk létre, amelyek a dina-
mikus memóriában kapnak helyet. Valamennyi új ohjektumban beállírjuk az életkort is,
mégpedig a sorszám kétszeresl:nél eggyel nagyobb értékre. Az els6 CAT objektumnál
tehát az é rték 1 lesz (a sorszám nulla!), a másodikná13, a harmadiknál 5 és így tovább.
A dklusmag Ulols6 műveletével az objektu mot címző mulatót elhelyezzük a megfele l ő
tömbt:le mbe n.

Figyeljük meg tehát, hogy ebben az esetben nem maga az objeknnn (eAT) kerül bele
a tömbbe, hanem csa k egy azt címző mutat6, ami a tömb deklarációja alapján pcrsze
nem is lehet másként. A második ciklus (29·3 1. sorok) kiírja az egyes eltároh é nékeker.
Ehhez ebben az esetben kétlépésrc van szükség. El őször kivesszük a megfelelő objek-
tu m dmét a tömbból (Family lil), majd ezen kereszCi.iJ meghívjuk a Ge tAge () tag-
függvé nyt, ami visszaadja a kívánt é rté ket.

Példánkb-.1O a Family tömb és n abban tárolt valamennyi mutat6 a vermen kap he-
Iyel. Ugyanakkor az 500 darab CAT objektum a dinamikus memóriában van. Ennek
a módszernek :~ verem tehermentesítésén fÚl megvan az az el őnye is, hogy megfelelő
progmmszervezéssel csak annyi memóriát kclllefoglalnunk a CAT objekollnok számá-
rn, nmennyit ténylegesen használunk is. Igaz ugyan, hogya fenti k6dban nem ez törté-
nik, hanem egyszeruen egy dklussallétrehozunk 500 ilyen obje ktumot, de a dklusvál-
tozó felső határát akár a fe lhasználótól is bekérhellük volna. Bizonyos esetekben cz
nyilván sokkal gazdaságosabb módszer, mint eleve létrehozni adott számú objektu mot.
278 1 IV. rész • Haladó eszközök

Végezetül egy harmadik ciklusban töröljük a memóriából mind az 500 CAT objektu-
mot, a Family tömb elemeit pedig null értékre 5l1íljuk

Dinamikus memóriában tárolt tömbök deklarálása


Arra is lehetőségünk
van, hogy a teljes tömböt a dinamikus memóriában [ároljuk, ne
csak a bCl1ne elhelyezett objektumokat. Ehhez a new operátorL kell használnunk de
úgy, hogy aZI kombinMjuk a tömbindex megadásával. Ennek a műveletn ek az eredmé-
nye egy mulató lesz, amely :17.1 ól memóriateriilelet címzi, ahova ti [őmb került. Lássunk
egy példát:
CAT *Family = new CAT15001;

Ez a deklaráció azt mondja, hogy Family egy muLatÓ, amely egy 500 darab Cl\T típusü
objektumot t'iro16 tömböt dmez. Másként fogalmazva Family nem egyéb, mint
a Family[OJ elem dmI:!.

,I
Ennek megoldásnak az egyik nagy előnye az, hogy ettől kezdve Inumtóaritmetikát is
használhatunk a tömbelemek dmzésére. Lássunk erre is egy példát:
C1\T "Family = new ClIT(5001 ;
CAT *pCaL = Family; II pCat a Family[OI e l emre mutat
pCat->Setllge(lO ); II F«mily[O ] érLékét lD-re á ll it juk
pCat++; II advance to Family [l ]
pCat->SetAge(20); /I Pamily(ll értékp.t 20-ra állit juk

A femi els6 művelet létrehoz egy 500 CAT típusú demet tároló tömböt a dinamikus me-
móriáb:m és egy mutatÓl, amely ennek a tömnek az cls6 elemét címzi. A harmadik sor-
ban ezen a mut:1tón (pontosabba n a másolatán) keresztül hívjuk meg az első ilyen CA'l'
objektum SetAge () nevű tagfüggvényét, és heállítjuk vele a 1Q-es értéket. A mlllalól ez-
után inkrementáljuk, melynek hal.ís{ira immár a következő CAT objektumot címzi. EZl is-
mét a már látott módon használjuk, vagyis megint meghívjuk az aktuálisan címzett ob-
jektum Set1\ge () tagfü~'Vényét, de immár a 20 értéket adjuk neki bemenelkénl.

Tömb mutatója és mutatók tömbje


Vi:t.Sgflljuk meg a következ6 három deklarációt:
1 : Cat FamilyOne[500l
2: CAT • FamilyTwo[SOO];
3: CIIT • Family'l'hree = new CAT[500] ;

FarnilyOne egy 500 CAT objektumot tároló tömb. FamilyTwo 500 olyan mutatót lárol,
amelyek CA'r típusú objt!ktumokat címeznek. FamilyThree maga egyetlen mulató egy
olyan tömbre, amely 500 CAT objektumol tárol.
15.6ra • Tömbök 1279
A három de klaráció közti eltérések drámaian befolyásolják a három dolog múködését.
Ami talán még e nnél is meglepőbb az az, hogy flÚg FamilyThree rulajdonképpen
FamilyOne valamiféle variá nsának is felfogható, addig FamilyTwo·tól gyökeresen kü-
lönbözik.

Mindezzel el is jutottunk ahhoz a kérdéshez, hogy miben is egyezik illetve tér cl egy-
mástól egy tömb és egy mutató. FamilyThree egy mutató, amely egy tömböt címez,
vagyis tanalma nem más, mint e tömb e l ső elemének a címe. A FamilyOne esetében
szó szerint ugyanez a helyzet, vagyis maga a tömbnév nem c!''Yéb, mint a tömböt cím-
z6 mutató.

Mutatók és tömbnevek
C++-ban a tömb neve ncm egyéb, mint egy konstans mutató, amely,) tömb els6 ele-
mét d mz!. N(:zzük a következ6 deklnrációl:
CA'r Family l S0 1 ;

lu Family ncm egyéb, mim egy mmaló, mégpedig egy olyan mm:Hó, amelynek t:utal-
maz az a cím, amit a &Family 101 mllvelettel is megkaphatn5nk. Kicsit egyszenlbben :
a tömb nevét leírva ml::~kapjuk a tömb c1s6 elemének címét.

A C++-b:m teljesen elfogadott, ha egy tömbnevet állandó nllltatókénr használunk , vagy


épp fordítv:l. A Family + 4 forma tehát ebben a nyelvben ugyanazt jelent i, mint
a Family r41, ugyanúgy hivatkozhatunk vele bárhol a tömb ötödik demére.

Ha egy mulalÓ értékéhez hozz:'iadunk egy számoL, inkrememáljuk, v:tgy dekremen-


táljuk aZl, az ehhez szükséges Imlvelete a fordít6progra m végzi el. EZl azért fon tos
hangsúlyozni, mert a Family + 4 forma természetesen nem egy olyan dmet jelent,
ami 4 bájtnyira van a Family tömb e1ejét61. Ha például minden, a tömbben tárolL ob-
jekmm 4 bájt hosszlIságú, a kkor a Family + 4 cím 16 bájtnyirn lesz a tömb kezdeté-
t61. Ha viszont a Family tömb CAT típusú elemeket tárol, és minde n CA'!' 20 bájtnyi tár-
helyet igényel, :lkkor Family ~ 4 jelentése egy a tömb elejét6180 bájt távolságra nm-
taeó CÍm lesz.

A 1S.5. Lista e~y o lY'1n k6clor mutat be, amelyben a dinamikus me móriában hozunk
lélre egy tömböt.

15.5. Lista - Tömb létrehozása 8 new operátorral (oewarrav.cpp)


o: II 15.5 Lista - TOmb a dinamikus memóriában
1 : Hinclude <iostream>
2,
3 : cla.ss CAT
4: {
5 : public :
280 l iV. rész · Halad6 eszközök

6: CA'!'() { itsAge = 1; itsWeight=5; } 1/ alapértelmezett konstruktor


7: ~CAT() ; /1 destruktor
S : int GetAge() const ( retllrn itsAge ; )
9 : int GetWeight() const { return itsWeight;
10 : void SetAge(int age) { itsAge '" age ; }
11 :
12 : private :
13 : int itsAg-e :
14 : int itsWeight ;
15 : J ;
16 :
17 : CAT : : ~CAT()
18 : (
19 : /1 std " eout« "Destructor called!\n' ;
20 : )
21:
22 : int main()
23 :
24 : Cl\T * Fl.lmily .. new CAT [ 500j ;
25 : int i;
26 : CAT • pCat ;
27 : for (i '" O; i < 500 ; iH)
28 : {
29 : pCat = new CAT ;
30 : pCat->SatAqe(2 * ] +1};
31 : Fumily[ll ,. ' pCat ;
32 : delete pCat;
33 : l
34 :
35 : for (i .. O; i < 500; iH)
36 : std :: cout « 'Cat ." « 1+1 « ': •
37 : «Family[ij.GetAge() « s t d :: endl ;
38 :
39 : deletc [I Family;
40 :
41: return O;
42 :

Kimenet
Cat U: 1
Cot 1/2 : 3
Cot B: 5

Cat "499 : 997


COL 8500: 999

A 24. sorban deklaráljuk a F'amily tömböt, amely 500 CAT típusú objektumot képes tá-
rolni. A new CAT[SOOI utasítás hatására ebben az esetben az egész tömb a dinamikus
memóriába kerül.
Apropó Egy mGszaki részle!...
Technikai értelemben a 24. sorban tulajdonképpen egy a dinamikus memóriában le·
vő névtelen tömböt deklarálunk. A Family mufafóba csupán a tömb első elemé·
nek d me ke rűl. Ennek ellenére általiIban azonosnak teki ntj űk ezt a mutatót magával
a tömbbel, ami azért nem helytelen, mert maga a C++ fordító is ~így gondolko-
dik". Amikor létrehozunk egy tömböt -legyen az akár a vermen, akár a dinamikus
memóriában - annak a neve tulajdonképpen nem más, mint egy mutató, amely
az elsó elemét clmzi.

A tömbbe kerül6 CAT objektumokat szintén a dinamikus memóriában hozzuk létre (29.
sor). Figyel jük meg ugy,makkor, hogy I:!bbl:!n az I:!setben nem az új objektumok mUlat6i
kerülnek a tömbbe, hanem m:Lguk az objl:!ktumok. Ez a tömb tehát nem mUlalótömb,
amelynek ekmci Cl\T típusú objektumokat címeznek, hanem CA'I' objektumok tömbje.
Áltah'iban a [ l operátor( helyeltesíthetjük a * operátorra l Gndirekdó) is. A Family [3 J
tehát cbben a helyzeten ugyanazt jelenti, mint a * (Family + 3) forma.

Dinamikus memóriában létrehozott tömbök törlése


A Family név tehát nem egyéb, mit egy mutató, amely egy a dinamikus memóri!ib:u,
talál ható, CAT objektumokat tarta lmazó tömb els6 elemét címzi. Amikor a 15.5. Lista
31. sorában használjuk a pCat mlllatót, akkor az garantáltan egy II dinamikus memóriá-
ban létrehozou CAT objektl.lmra mOIat, ami most bekerül a megfelelő tömbelembe.
( Eddig ugye semmi meglepö nincs 3 dologban, elvégre pont arról van szó, hogyan kell
a dinamikus memórifib:m objektumtömböket kezelni .) Csakhogy a pCat mutatót ti cik-
lus következ6 iterációjában is feihasználjlIk. Nem jár ez azzal a veszéllyel hogy az el6-
zó CIIT objeknllnra már nem mutat semmi, és soha többé nem fildjuk felszabadítani
az {t1t:da lefoglalt memóriát?

Ez valóban nagy gond lehetne, de valójában nem kell t61e tartanunk. A fordítóprogram
elég okos ahhoz, hogyemlékezzen az össze dinamikusan kezelt objektumra, így bizto-
sak lehetünk benne, hogy az objektumtömb valamennyi eleme törölhető, és progra-
munk vissL1kapja az általuk lefogla lt memóriát.

Hogy megbizonyosoclhasSlmk err61, módosítsuk egy kicsit a 155. Listában bemutatott


kódot. Először is változtassuk meg a tömb méretét 500-r61 IQ-rc a 24., 27. és 35. sorok-
ban. Aztán vegyük ki a megjegyzésjelct a 19. sorban láthat6 cout utasítás elől. Amikor
a program futása a 39. sorhoz érkezik, a tömb már nem létezik, a rendszer meghívta
az abban tárolt valamennyi CAT ohjektum deslruktorál.

Amikor a dinamikus memóriában létrehozunk. egy objektumot a new operátorral, azt


a delet e utasítással lehet onnan törölni, amit az elem neve követ. Ehhez teljesen ha-
282 l iV. rész • Halad6 eszközök

son!óan ha a new <osztály> [l utasítással hozunk létre egy objekmmtömböt, akkor


azt a delete [J <osztály> utasítással lehet törölni. Az üres :z.'lr6jelpár jelzi a fordító-
nak aZl, hogy in egy egész tömb törlésé ről van szó.

Ha véletlenül kifdejtjük a zárójeleket, akkor a mcrvelet csak az objektumtömb első ele-


mét fogja törölni. EZl szintén beláthatjuk ~ kísér1elileg~ is, ha a 15.5. Lista 39. sorábó] el-
távolítjuk a megje~,'yzésjeleL Ha ezzel párhuzamosan átszerkesztjük a 21. sort is úgy,
hogya destruktor a képernyőn jelezze, ha lefut, akkor láthat6, hogy csak egy ilyen jel-
zés jelenik meg, vagyis csak cb'Y CAT objektum tÖrl6dÖtt. Sz6val gr..ltulálunk! Épp most
sikerültl étrchozni cgy mcmór;akczclési hibát (memory leak).

Helyes Helytelen
Ne felejtsük e l, hogy egy n elemű Ne próbáljunk egy tömb vége után ír-
tö mb elemei O-lól n-J-ig sz5moz6d- ni, vagy onnan olvasni.
nak. Ne keve rjük össze a tömböt dmző
Tö mbö k indexelését csa k olyan mu- muta tót a mlllat6 tÖmbbe l.
tatókon keresztül végezzük, amelyek
valóban az adott típusú tömbre Ilm-
tatnak.

Karaktertömbök
A kar.lktcrl:'inc ne m egyéb, mint karakterek sorozata. Eddig C.'i upán névvcl nem rcndel-
kező karakterláncokkll ltalálkoztunk, általában a eout utasítással kapc.'>Ohnhan. íme
egy példa e rre:
cout « "hel lo world. \n";

C++-ban a kal"oIkterlánc tulajdo nképpen egy karakte rtö mb, amelynek végét egy null
karakte r jelzi. (A null karakter jelö lésére - megkülönböztetend6 a NULL értékU mutató-
tól- a '\0' használ:1tos.) Ennek megfelel6en egy kal"oIktcrláncot deklar.'ilhatunk és ini·
dalizá lhntu nk is ugyanúgy, mint egy tömböt. Helyes tehát a kövctez6 forma:
char Grccting [l .. { ' H', ' e ', 'l' , 'l ', 'o ' , '
' W' , ' o ', ' r' ,' l ' , ' d ', ' \0 ' } ;

A sort záró i \0 ' karakter az a bizonyos null kllrakter, ami alapján a C++ függvényei
meg tudják mo ndnni, ho gy hol van a karakterlánc vége. Bár a fe nti módszer működő­
képes, sem kényelmesnek, sem biztonságosnak nem nevezhet6. Sz{unos helyen el le·
het gépelni, arTÓI nem is beszélve, hogy egy szöveget betfinként, vesszők kel elválaszt·
va begépelni inkább gépír6i bravúr, mint értelmes mcgoldás. Éppen ezért a C++ bizto-
sít egy sokkal kézenfekvőbb lehetőséget a karakterláncok megadás."ira :
char Greeting[) " "Hello World" ;
15, 6ra • Tömbök 1283

Ezzel a szintaxissal kapcsolatban két dologra szeretnénk fölhívni a figyelmet:

• Az előző módszt:rlől eltérően itt nem egyszeres, hanem keltős idézőjeleket kell
használni, a vessz6k és a kapcsos zár6jelek pedig nem kellenek.
• Nem kell külön kiírnunk a záró null karaktert, az II fordítóprogram automatiku-
san beilleszti a karakterlánc végére.

A "Hello World n karaktedánc 11 bctűb61 áll , tehát 12 bájt hosszúságú. A Hello 5 b{ljtot
tesz ki, II sz6köz egyet, a World újabb ÖlÖl, a záró null kardkter pedig egy bájlOS.

Deklarálhatunk inicia1i7..áci6 nélkül is kar:.lktertömböket. Ezeket szokás puffereknek is


nevezni. Mini ffiindtm lömbnél, természetesen ill is ügyelnünk kell arra , hogy nem
a karjuk több karaktert elhelyezni egy ilyen lárban, mint amekkora a mérete.

Az inici:llizáció nélkü ! dck!arálL karaktertömbük használatára mU lat példát a 15.6. Lista.

15.6, Usta - Egy karaktertömb feltöltése (arrayfilled.cpp)


O; /1 15 . 6 . Lista KaraktertOmbOk pufferkánt való használata
l ; linclude <iostream>

3;
4,
" i n t main ()
{
5; char buffer[801 ;
6: std : : cout « "Enter the string ; ' :
7: std : : cin » buffer :
8: std : : cout « " Here ' s the buffer , • « buffer « std : : endl;
9: return O;
10 : }

Kimenet
Enter the string : Hello Wo r l d
Here ' s t he buffer : Hello

Az S. sorban létrehozunk egy puffert, amely 80 karAkte rt képes tárolni . Ez azt jelenti,
hogy ebben a tömbben egy legfeljebb 79 bettls szövegel helyezhetünk el, hiszen kell
egy bájt a lezár6 null karaktcrnek is.

A 6. sorban bekérunk a felhasznál6tól cgy szövegel, amit a 7. sorban el is helyezünk


imént kialakított pufferben. A cin automatikusan elhelyezi a karakterlánc végét jel-
37..
z6 null karakte r, ezzel tehát nem kell tör6dnOnk.

A 15.6. Listában bemutatott kóddal két probléma is van. El6ször is nem tudjuk, hogy
mit fog gépelni a felhasználó. Ha 79 karakternél többet ad meg, akkor a puffer túlcsor-
284 1IV. rész' Haladó aszközök

dul, hiszen a c in nem érzékeli a végél. A másik probléma, hogy ha a felhasználó szó-
közt is gépel, akkor a cin azt fogja a bemenet végének tekinteni , és a maradékot be
sem írja a pufferbe.

Ezeknek a problémáknak a megoldásra kicsit át kell alakítanunk a programot: a cin


egy speciális metódusát, a get () -et kell használnunk. A c i n . get () három paramétcn
vár bemt!nelké nl:

• A kitöltcnd6 puffer nevét


• A beolvasni kívánt karakterek legnagyobb számát
• Azt a jck:t, amit a bemenet végének kell tekinteni

Az alapértelmezett "bemenet vége jel az újsor. A módosított kód a 15.7. Listában látható.

15.1. Lista - Karaktertömb feltöltése (második változat) larrayfilled2.cpp)


o : 111 5 .7. L i!l t ól A ci n . get (} met6dus ha szná léltv.
l : #i nc l udc <io9t r c am>
2.
3 : int ma in()
4: I
5: char buffer[80] :
6: std : : cout « " Enter the st r ing : ";
7: std :: cin . get!buffer, 79) : II get up to 79 or newlinú
8: /ltd : : cout « "Here ' s the buffer : • « buffer « std : : endl ;
9: return O;
10 :

menet
Enter the sLri ng : Hello World
Here ' s the buffer : Hello Wo r l d

A ci n osztá ly ge t () rnetódusát a 7. sorban hívjuk meg. Ennek az első argu rnenmma


az a puffer, amit az 5. sorban deklarálrnnk. A második bemenő paraméter a bekérhet6
karakterek maximális száma . Esetünkben ez 79, mivel a pufferben így még é ppen elfér
a záró null karakter is. A bemenet végét jelző karakte r megadására itt most nincs szük-
ségünk, mivel az alapértelmezett újsor erre a célra éppen megfelel. A cin-ről és annak
mindenféle különlegességér61 majd a 21. órában sz6lunk részletesebben.

Az strcpyO és az stmcpyO függvények


A C++ örökölte a C nyelvtől a karakterláncok kezelésére szolgáló függvényeket. A szá-
mos lehetséges mO"velet közül iti csak kettőt szeretnénk kiemelni, amelyek karakterlán-
lS.6ra-

cok másolásra szolgálnak. Az c&'Yik az strcp () fijg&'Vény, a másik az s t rncpy ( ) .


Az strcpy () működése viszonylag egyszenT: veszi a megadott kamkLerláncot, és a tel-
jes tartaImát átmásolja a szintén megadott pufferbe. Használatát a 15.8. Lsta szemlélteti.

15.8. Lista - Az strcpyll függvény használ... (..ingstrepy.epp)


o: 1/ 15 . 8 . Lista - Az s trcpy() fo.ggvény használata
l : #include <iostream>

) .
2 : #inc l ude <string h>

4: int main ()
5: {
6. chnr Stringll] = "No man is an island";

,.
7: char String2lBOJ ;

9 . strcpy (String2 , Stri ngl) ;


10:
ll : s td : :cout « · string1 : « Stringl « std :: endl ;
12: std : : cout « "String2 : « Stri.ng2 « std: : endl ;
l): return O;
14:

JGmanot
Stringl : No man iy an island
String2 : No man is a n island

A 2. sorban beemeljOk a STRING . H fejlécállományt, amely tartalmazza az strcpy ()


függvény prototípusát. Az strcpy () bemenetként két karakte rtö mböt vár, melyek kö-
zü l az első il másolás c;(:lja, és a második a forrlIs. Ügyeljünk amI is, hogy ha a forrns
több adatot tartalmaz, mint nmennyi a célpufferben elfér, akkor az strcpy () egyszerű­
en túlírja azt.

Ennek a lchel,>éges hibának a kiküszöbölésére a szabványos könyvtár tartalmaz egy


strncpy () nevű változatot is, amelynek a másolni kívánt kamkte rck maximális számlIt
is mcg lehet adni. A fü ggvé ny az első null karakterig másol, vagy nmíg el nem é ri ezt
az e l ő re rögzíLeu számot. Az strncpy () függvé ny használa tM szemlélteti a 15.9. Lista .

15.9. Lista - Az strncpyO függvény ha""lata (usingstrncpy.epp)


o: II 15 . 9 . Lista Az strncpy() fo.ggvény használata
1 : öinc1ude <iostream>
2 : *lnclude <stri ng.h>
) .
4 : int main()
5: {
6 : const int MaxLength : 80;
7 : char Stringl [] : "No man is an island";
I
286 IV. rész • H~ad6 eszkÖ2Ök

8 , char String2[MaxLength+l1;
9.
10 : strncpy(String2,Stringl,MaxLength);
ll: String2[strlen(Stringll] ' \0'; II add a null to the end
12 : std : : cout «'Stringl : <<:String! « std : :endl;
13 : s t d :: cout« 'String2 : «String2« std : : endl;
14 : return O;
15 :

Stringl : No IMn is an island


String2 : No man is an island

A 10. sorban az strcpy () -l lecseréltlik egy s t rncpy () -rc, amelynek ezért t!gy h,moa-
dik parnmételt is meg kelJ adnunk: a másolni kívánt karakterek maximális számát.
A String2 nevt1 puffert MaxLength .. ! hosszúság(mak dekl:trálruk. Az a bizonyos
plusz egy hely lermészelest!n megint a záró null karaklemck kell.

Karakterlánc-osztályok
Az ANSI/ISO szabványnak 1ll<:!gfelcl6 C++ fordít6programokhoz minden esetben tarlO-
zik egy szabványos könyvt5r, amely rengeteg adatkezelés! és adattárolási osztályt tar-
talmaz. Ennek kötelez6 eleme egy String nevű osztály is, amely nevének megfelel6en
a karakter!áncok kezc::lésérc szolgál.

A C++ ugyan örökölte a C nyelvt61 a null karakterrel 7Jirt karakterláncokat, illerve azo-
kat a könyvtári függvényeket , amelyek ezek kezelésére szolgálnak, ám al. objektum-
orientált keretrendszerbe ezeket nem emelte át. Ehelyeu a String osztály az, amely
az objektumközpontú szemléletnek megfelelően elrejti magukat a tárolt adatokm a fel-
használó e161, és csak a megfelelő met6dusokon keresztül engedi kezelni azokat.

Ha az olvasó nem azt a fordítóprogramot használja, am it ehhez a könyvhöz melléke\-


tek, akkor megeshet, hogy az általa használt rendszer nem tartalmazza a String osz-
tályt. Mi több az is megeshet, hogy megtalálja ugyan, de az nem a szabványoknak
megfelelően működik , így kénytelen újm megírni.

Afféle szolgáltatási minimumkém a String osztálynak képesnek kell lennie áthidaini


a karakterláncok tömb je ll egéből adódó problémákat. Ilyen probléma például hogy
a karakterláncok, mint minden tömb statikusak: meg kell mondanunk a méretükel. Az-
tán ha megmondruk, akkor azt a helyet mindenképpen lefoglalják a memóriában, akkor
is, ha nem Ls használjuk. Ha pedig túlírunk egy tömb végén, az eredmény kamsztr6fa.
15. óra • Tömbök 287

Nos, ezek azok a dolgok, amiket egy jól megírt. String osztály biztosan kiküszöböl:
csak a ténylegesen szükséges tárhelyet fogalja le, azt viszont mindig, akármekkora ka-
rakterláncot akarunk is létrehozni. Ha pedig egyszeruen nem lehet lefoglalni a szüksé-
ges memóriát, az osztály jelzi ezl a felhaszmil6nak.

Kérdések és válaszok
Kérdés: Mi törten/k, Ita be/cirok egy 25. elemel egy amIigy 24 elemi_I tömbbel

Válasz: Igazából nem lehet megmondani. Annyi biztos, hogy olyan mem6riatcriiletre
írunk, ami nem a tömbhöz tartozik, az eredm(:ny pedig katasztrófa is lehet.

Kérdés: Mil nevezül1k il/icializólat/al1 1ö/llbe/emnek?

Válasz: Ez egy tömbnek egy olyan eleme, amely még soha nem kapmt értékel. ilyen-
kor is van benne persze vallmi , de azt nem lehet megmondani, hogy mi. Ami éppen
aZon a mem6riateri.iJeten VOll, amelyen a tömb helyet kapott, az lesz az inicializálás
d6u az elemekben. Ha tehát kezdeti érték megadása nélkül kezdünk használni egy
tömbclemet, annak beláthatatlan következményei lehelnek.

Kérdés: wllet tómhóket kombillállli, vagyis egymásba ágyaz/li öket?

l 'á lasz: Igen. A közönséges tömböket az 6ket címző mutatók segítségévellehel össze-
fűzni egy új, nagyobb tömbbé. Kardkterláncok esetében az összefűzésre akár kö nyvliíri
függvé nyeket is használhatunk. Ilyen például az strcat ().

Gyakorlatok
Eltöltöttünk tehát egy teljes órát azzal, hogya tömbök kel ismerkedjünk. ItI aZ ideje,
hOb'Y tudásunk ellen6rzése és gyakorlatba való átemelése végett megválaszoljunk né-
há ny kérdést és megoldju nk néhány feladatot.

Kvfz
1. Mekkora a legkisebb és legnagyobb használható tömbindex egy adott tömbnél?
2. Mi történik, ha egy tömbben több ad,ttot próbálunk elhelyezni, mint amennyi
a deklarációja szerint elfér benne, vagyis a maximálisnál nagyobb indexe t hasz-
nálunk?
3. Milyen el őnyökkel jár a tömbök használata?
4. Honnan szerezhetünk több információt a C++ String osztályával kapcsolatban?
288 [V, rész • Haladó eszközök

Feladatok
1. Módosítsuk úgy a 15.4. Listában bemutatott kódot (arrayonheap ,cpp), hogy
nem kelljen benne használnunk a pCat muuuót.
2. Módosítsuk úgy a 15.4. Listában bemutatott kódot (arrayonheap . cpp), hogy
minden olyan helyen, ahol fölbukkan benne az 500 mint konstans, on használ-
juk az ugyanerre az értékre beállított MAXSIZE állandót. Próbáljuk ügy is lefunal-
ni a programot, hogy csökkenljük ezt a számot. Mi történik, ha eltávolítjuk
a const ku1cssz6t? A C++-han a tömbök lefoglalása nem dinamilmsan történik!
3. Mi történne, ha a 2. gyakorlatban MAXSIZE értékeként 200-at adnánk meg, de
nem írnánk át 3i'. 500-as érték minden egyes eloforclulását MAXSIZE:-ra?
-1 . MódosÍlsu k a 15.9. Listában bemutatott kódot (usi ngstrncpy . cpp) úgy, hog)'
a 10. sorban a MaxLength értéke 5 legyen. rordítsuk Je és futtassuk a progra-
mol, majd hasonlítsuk össze a kimenetét az eredeliével. Ez a változtatás kiválóan
mutatja az strncpy () függvény igazi erősségét: képes ti teljes kamkterláncnál
kevesebbet is másolni.

Válaszok a kvízkérdésekre
1. Vtlllmenny; tö mb indexeléS(! nullával kezd5clik. Az utolsó elem (v:lgyis az utol-
só használható index) é rtéke mindig eggyel kisebb, mint a tömb elemszáma.
Ha tehát egy tö mb dekhlrációjában az [50J szerepel, akkor az utolsó használha-
tó index a 49.
2. Nehéz megmondani. Ha szerencsénk van, akkor pont egy o lya ll memóriaterület-
re fogunk rászaladni, amit az operációs rendszer ellenőri z, így azonn:ll valami-
lyen hib:llizenetet k:lpunk. Ha nem, akkor megvá lrozhat például egy másik vál-
tozÓ értéke, ami rendszcrinr nehezen felderíthet6 hibához vezet. Én egyszer egy
teljes szombmomat eltöltöttem azzal, hogy egy ilyen hibát keresgéltem, és még
csak nem is a saját k6dom volt..
3. A tömbök használatának legfobb c10nye az, hogy általuk egyetlen néven keresz-
tül érhetjük el logikailag összetartozó adatok cgy egész h:ll mazát.
4. Az első hely, amit érdemes megnézni tlZ általunk ll:l.~ znált fordítóprog ram súgó-
ja. Ha a könyvhöz mellékelt Borland fordít6l használjuk, kattintsunk a Help me-
nüpontra, majd válasszu k onnan a Help Torks pontot. Krmints unk a Reference
opci6ra, majd keressük ki a <string> pontot. (Ügyeljünk r5, hogy ez ut6bbi
nem azonos a <string . h> ponttal, am; a C szabványos kö nyvtárában található,
:l k:lrakterláncokkal kapcsolatos dolgokat írja le.).
v. RÉSZ
..
Oröklődés és többalakúság
16. óra Az öröklödés
17. óra A többalakúság és a származtatott osztályok
18. óra A többalakúság kifinomult használata
19. óra láncolt listák
16. ÓRA

Az öröklődés

Ebben az órában a következ6kr611esz szó:


• Mi az az örökl6dés
• Hogyan származIassunk egy osztályt egy másikból
• Hogyan lehet elérni az alapmetódusokat a származtatott osztályokból
• Hogyan !ehet felOlírni az alapmct6dusokat
• Mi az a védett elérés, és hogyan lehet használni

Mi az az öröklődés?
Az emberi értelem :dapvcl6 igénye, hogy a kü l ö nböző foga lmak közt kapcsolatokat ke-
ressen, felismerjen és létrehozzon, Különböző hierarchikus, táblázatos, hM6zatszcn1 é,~
egyéb intellektuális mcx.lcllckct építünk fel, hOh'Y megértsük, hogyan működnek egyült
a dolgok. A C++ nyelv az örökl6dési hierarchiában próbálja mindezt lTlegr::tgadni.

Mi az a kutya? Amikor kedvenc háziállatunkra nézünk, mi jut eszünkbe? Az állalolVos


különféle szervek összehangolt há16zatállátja, a fizikus különböző atomok és erők köl·
csÖnhatás.1r<l gondol, a biológus a canine domeslicl/s egy szép példányát veszi észre,
éde'ianyám pedig kutyasz6rt és nyálat emleget.
292 V. rész • Örökl6dés és többalakúság

Bennünket most a biológus nézőpontja érdekel; az Ő szempontjából egy kutyafélével


van dolgunk, tágabb énelemben egyemlóssel, ami e.gy állat. A biológus felosztja
az élőv ilágm, és a maga szempontjai szerint minden élől ényt besorol az ország, [Ör7$,
osztály, rend, nemzeL<;ég, nem, faj és alfaj valamelyikébe.

A biológus által felállítoLL hierarchiának minden kapcsolata ~részl.'!' jeJlega, A kutya em-
lősá llat, Látjuk a háttérben a része relációt: A Toyota az egyfajta gépkocsi, amely egy
jánnú. A Tibi csoki egyfajta édesség, amely élelmiszer.

Mit is értünk az alatt, hogy "valami egyfajta másvalami»? Azt, hogy annak a másvalarni-
m:k a speciális esete. Azaz a gépkocsi a járm űvek egy sped{ili.<; esete. A gépkocsik is,
az autóbuszok is j:í rmúvek. Van valami specia litásuk, amely gépkocsivá vagy autó-
busszá teszi őket, de mindkét típust egyformán megilleti a .já rmű" besorolás. Osztoz-
nak ebben a jellemzőjükhen.

Öröklődés és származtatás
A imtya örökli (azaz automatikusa n megkapja) az eml6sáll atok valam<:!nnyi nllajdonsá-
gát. Emlősálbt VOIt.,1 [lál fogva képes a hclyváltoztató mozg{lsra és a l e v egő beléJegzésé-
re - minthogy minden e m16sá lbt definíció szerint képes ezekre . A kutya speeia litása
mtg HZ ugatás, farokcsóválás stb., úgyhogy ezek II tulajdonságok még hozz:'ijönnek
az előző ddlníci6hoz. A nkutyas.1g» specializált jellemz5, míg az nem16sség" által:mos,ln
igaz minden eml6sállatra.

A kuty{lkat kisebb esoporlokra is feJoszth:lljuk, például vaclászkulyákra és terrie rekre


stb.; a terriereket is csopon osítl1atjuk: vannak Yorkshire Terrierek, Dandie Dinmont
T<:!rnerek stb. A Yorkshire Terri<:!r egyfajta terrier, így egyfajta klllya is, emiatt e ml ősá llat
is, azaz állal, és ily m6don é l ől é ny . A hierarchiát a 16. 1 ábra mutatja.

16.1 ábra
Az álla/ok hir:m,., hiája
16. óra • Az 293

A CH úgy pr6bál meg efféle hierarchiákat leképezni , hogy az egyik osztá lyt a má.<;ik~
ból származtatja. A származtatás j61 kifejezi a . része~ reláci6t. Az emlos osztályból s7.ár-
mazhat az új kutya osztály. Ekkor már nem kell kifejezetlen megmondani , hogy a ku~
tyák tudnak mozogni, hiszen ezt a tulajdons{lgukat már az emlősöktrn öröklik Mivel
a kutya-t az eml os-b61 származtattuk, automatikusan képes a mozgásra.

Azt mondjuk, hogy ha egy oszt'ii.ly új jellemzőket ad egy másik osztály tulajdonságai-
hoz, akkor azt cbb6l származt:lttuk. Az eredeti osztályt hívjuk az új osztály bázisosztá-
lyának, vagyalaposztályának.

Ha a kutya osztály az emlo~-b61 szá rmazik, akkor az emlos a kutya osztály bázisosz-
tálya. A származtatott osztályok az eredeti bá:.::isosztálynál g:lzdagabb struktúrát alkor-
nak Ahogy a kutya fogalma is gazdagabb az eml6sállaténál, úgy a kutya Q.'iztály is
g'l:.::dagabb néhány mel6dussal vagy adauagga! az emlos osztálynál.

A bázisoszt:í.lyoknak álraláb:m van néhány származtatott os:.::t:'i lyuk. Ahogya kutyák,


macskák és lovak különböz6 fajtll em l ősá ll mok, a nekik mcgfclcltetell osztály származ-
hat az emlos osztálybóL
,
Modellezzük az öröklődést az Allatok Országával
Ebben a részben különmle állatosztályokat képzelünk el a7. örökl6dés és :L származta-
tás tárgyalás.1hoz. Képzeljük el, hogy egy játék teTVezése a feladatunk - ~gy állatfannOl
szeretnénk szimulál ni gycrmekek számám.

EI6ször mindenfC:le állatot képzelünk el, lovlIkal, teheneket, kutyál, macskM. birkál stb.
Különböz6 met6dusokat is tervezünk a sz.1mukr-.l, hogy úgy viselkedh~ssenek, ahogy
ilZ a gyerekek kép7.dctében él, de ill most csak egy kiírásr.:t csupaszílSllk le ezek L'll1almát.

Egy függvény lecsllpaszítása vagyelnagyolása (sLllbbing) azt jelenti, hogy csak annyit
íru nk meg be161e, hogy Játsszon , hogy meg lett hívva - a r{!szletek megírását pedig ké-
sőbbre hagyjuk. Évekig gyártonam JecsupllszítQ[[ függvényt!ket, várva a dicsőséges
napra, amikor több időm lesz rájuk. Íme az egyik el6nye II felülről való tervezésnek-
a munka a legfelső szinten kezdhető, és fokozatosan egyre mélyebbre ha1:Ldva folytat-
ható. Munka közben nem kell egyből kidolgozni a problém:l minden részl<:! tétj a "cson-
kok" jó térkitölt6ként működ nek. Ez egyben azt is jelenti, hogy akár mások is befoltoz-
hatják a lyukakat, kidoJgozhalják a részletekct, amíg az e l ső progmmozó már más ré-
szekkel foglalkozik.

Ha valakinek több ideje van, nyugodtan kibővítheti ennek a fejezetnek minimális kócl-
részleteit, hogy még élethűbben viselkedjenek legyenek az állatok.
294 . rész • Örökl6dés

A származtatás szintaxisa
Egy osztály deklaciciójakor megadható. hogy melyik másik osztályból származzon.
Az osztály saját nevét kövct() kettőspont után következik a származtatás Lípusa (például
public) és a bázisosztály neve. Egyelőre csak a public (nyilvá nos) tírust fogjuk hasz-
nálni. Íme egy péld,,:
CldSS Dog : public Mammal

A származtatás tipU5.'Ír3 még visszatérünk ebben a fejezetben. A bázisosztálynak már


deklarálva kell lennie, kü lönben fordítási hibát kapunk. A 16.1 Lista bemutatja, hogy
hogyan lehel a Dog (lmlya) osztályt a Mammal (emlős) osztályból szárma;;:talni.

16.1 Usta - Az agyszerü öröklődés (simpleinherit.cpp)


0 , 1/16.1 Lista II Egyszerű OrOkl6dés
1, #include <iostrcam>
2,
3 , enum BREEO { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAS J;
.... II Fajt6k
4,
S: class Mammal
6, (
7: public:
8 : II Konstruktorok
9 : Mammal();
10: -Mammal() ;
ll:
12 : II Hozzáfér6 rOggvények
13 : int Geti\ge () const;
14: void SetAge(inl);
15: int G€'!tWeight() const;
16: void SetWeight();
17:
18: II Más tagfo.ggvények
19: void Spe",k{);
20 : void Sleep{);
21 :
22 :
23: protected :
24 : int itsAgc;
25 : int itsweight;
26 , } ;
27 :
28 : class Dog public Hammal
29 :
30 , public :
31 : /1 Konstruktorok
32 , Dog() ;
33 : -Dog();
34 :
16. óra • Az örökl6dés 295

35 : II Hozzáfér6 függvények; breed=fajta


36 , BREED GetB reed() const ;
37 : void SetBreed(BREED) ;
38,
39, II Más tagfüggvények
40, /1 WagTail(); /I ParokcsÓvó.1ás
41 , /1 BegPorPood(); II KajáértKuncsorgás
42 :
43 : protceted ,
44 : BREED itsBreed;
45 : );
46 : int main()
47: (
48: return O:
49:

A programnak nincs kimencIc, mert itt csak az osztá lyok deklarnciói láthatók a mcgva·
16síl:is nélkül. Ennek ellenére van mil né:mi rajta, sót még le is fordu l.

AZ 5-26. osztályb:Ln dt!khuflljuk a Hamma1 osztályt. Figyeljük meg, hogy a Mammal osz-
tá lynak nincs b:ízisosztálya, nem szármilztattllk semm iből. A val6 világban ez r1cm így
van, :IZ »emI6sök" az "á llatok" egy fa jtája. C++ programjainkban az eredeti gondolat·
körnek csak e~,.y elvonalkoztatását képezzük le. A v••lós,íg túlságosan részletgazdag ah·
hoz, hogy egy progr.r.m mindent megval6sítson. A C++ programokban ábrázolt hierar·
ehia a meglév6 adatoknak csak egy haszn.Hja. A jó tClVczés ismélVc, hogy oly m6don
képezi le kérdéses területeket, hogy ,IZ ~sszen1en, hitelesen megfeleltethct6 a val6ság
adott szeleténck

A hierarchiának valahol el keH kezd6clnic j ebben a progt:lmban ez ti Mamma1 oszt{i[y.


E döntts miatt lehetséges, hogy lesznek olYlm tagváltozók, melyeket egy magasabb
szintG osztályban kellene deklarálni, mi mégis ilt tesszük meg. Pl!ldául bizonyára min·
den állatnak van kom és súlya; így, ha az AnimaI (állat) osztályból szármaZOlI II Hammal
osztály, akkor sl."imíthalnánk arra, hogy ezek a tulajdonságok onnan fognak örökl6dni.
Jelen esetben azonban ezek a jellemzők csak a Mamma1 osztá lyban jelennek meg.

A program ésszerű keretek közön tartása érdekében csak hal metódus kelÜll a Mamrnal
osztályba - négy hozzáfér6 függvény , valamint a Speak () (beszélj) és a Sleep ()
(al/telj).

A Dog osztálya Manuna1·ból szánnazik, ahogy azt a 28. sor mutatja. Minden Dog objek-
nLmnak h,írom tagváltozója lesz, i tsAge, itsWeight és itsBreed (/...'Ora, sú{).'a ,fajlá-
ja). Figyeljük meg, hogy a Dog osztály-deklarációban nem szerepel az i t sAge és
az itsWeight, mert ezeket a Marnmal osztályból örökli az összes többi metódussal
együtt, kivéve a konstruktort, a másoló konstruktort és a destruktort.
296 1V. rész • Örö~ódés és többalakúság

Privát vagy védett?


Talán felfigyelt rá az olvasó, hogya 16. 1 Lista 23. és 43. sorában egy új kulcsszó került
e16, a protected. Korábban az osztály saját tagváltoz6il a private, azaz privát kulcs-
szóval adtuk meg. Az ilyen adanagok azonban nem elérhetőek a származtatott osztá 4

Jyokban. Nyilvánossá (public) leheme lenni a szübéges adaltagokat (it sAge és


itsWeight), de igazából nem ez a célunk; nem szeretnénk, ha más osztályokból eze-
ket közvel1enül ellehctne érni.

Mi cgy olyan megjelölésre vágyu nk, amely arra utal, hogy ezen osztály tagok legyenek
elérhetőck ebben az osztályban és ennek származtatott osztályaiban. Erre szolgál
a protected (v{'(lefl). A védett adattagok és függvények Caz aclott osztályon kívül)
csak a szármn.talott osztályokbar, látszanak, a többiek s:.:::ím:ír:l láthatatlanok, mim
a privát változ6k.

1\ védett vához6k elérhet6ségc tehát valahogy fé lúton van a privát és a nyilvános kö-
zÖu. Kevésbé szigorú a védelme, mint a privfité, de azért ne m annyira laza, mim
a r'yilvános.

Ezzel megismenük mindhúrom létez6 hozzMérés-szabályozó módosít6sz6t: nyilvános,


védett és privát. I-la egy függvényhen példányosítunk egy osztályt, akkor a függvény
ennek az osztálynak csak a nyilvános t.1gvállozóit és tagfOggvényeit mdja elérni. Egy
oszuíly saját tagrüggvényei azonhan ezeken kívül a privát tagválloz6knt és tagfüggvé-
nyeket is Mtjúk.

igy :1 Dog: : WagTail () függvény e léri az itsBreed privát adatait, valamint a Hammal
osztály védett adatait is.

J la netán más osztá lyokat is dekl,uá lnánk a Hanunal és a Dog közé (példáu l olyat, hogy
DomesticAnimal, luíziálll/t), akkor is elérné a Dog osztálya Hammal védett adatait, fel-
téve, hogy ezen köztes osztályok is mind nyilv{mos ö rökl6dést h:lsználnak (mim a Dog).

1\ 16.2 List.1 szemlélti:!Li, hogy hogyan lehet Dog típusú objektumokallétrehozni, és


a hozzá tartozó adatokat és függvénye ke t használni.

16.2 lista - Egy származtatott osztály Iderivedobjectcpp)


o: /1 16.2 Lista /1 ogy származtatott oszt3ly használata

,.
1: *include <iostream>

3 : enum BREED { YORKIE, CAIRN , DANDIE. SHETLAND, DOBERMAN, L~B };


4.

,.
5 : class Mammal

7 : public :
B: II Konstruktorok
9 : Mammalj) : itsAge(2), ilsWeight(51 {}
10 : -Manunal()(}
11 :
12 : II Hozzáfér6 függvények
13 : int GetAge()const { return itsAgc ; }
14 : void SetAge(int age) { itsAge = age:
15 : int GetWeight{) const { return itsWcight:
16 : void SetWcight(int weight) { i t sWeight = weight:
17 :
lB : II Más tagfüggvények
19 : void Speak{)const ( std : : cout « "Manunal sound ! \n" ; )
... II Em16sá11athang
20 : void Slccp() conGt o t d : : cout« " shhh . I ' m sleeping . \n " ;
.... II Alszom
21:
22 :
23 : p r otected:
24 : Lnt itsAgc ;
25 : i nt itsWeight ;
26 : };

27 :
28 : class Oog : public Hammal
29 : (
30 : public :
31 : II Konstruktorok
32 : [log j) : itsBreed(YORKIE) {}
33 : -Dog () ()
34 :
35, II Hozzáfér6 függvények
36 : BREED GetBreed() const { retllrn itsBreed: }
37 : void SetBrccd(BREED breed) ( itsBreed = breed;
3B ,
39 : II Más tagfüggvények
40 : void WagTail() ( std :: cout« 'Tai! wagg i ng ... \n ': )
.... II Farokcs6válás
41 : void BegForFood() ( std :: cout« "Segging for f ood . .. \n' ;
.... II Kajáé r t Ku n csorgás
42 :
43 : priv atc :
44 , BRE ED itsBreeo ;
45 : l ;
46 :
47 : i m . mai n {)
48 :
49 , DOg fido ;
50 : fioo . Speak() :
51 : fi d o . WagTail () :
52 : std : : cout « 'Fido is " « fido . Ge t Age {) « " ye a rs old\n ";
53 : return O:
54 :
296 1V. rész • Örö~ódés és többalakúság

menet
Mammal sound!
TaU wagging ..
Fido is 2 years old

Az S-26. sorlxm deklaráljuk a Hamma! osztályt (:1 helytakarékosság kedvéért minclen


függvény he lyben kifejtett). A 28-45. sorban deklaráljuk a Dog osztályt, mint a Manunal
osztály lcszármazottját. Ily módon nlinden kutyának (Dog) van kora , súlya és faj tája.

A 49. sorban létrejön Fido kutya. Minden kutya- (és így eml ős-) ltllajdonsággal rendel-
kezik Azaz tud pélcl:hil farkat csóválni, .beszélni" és aludni (WagTail, Speak, Sleep) .

Konstruktorok és destruktorok
A kutyák em16sök. Ez a lényege a ,részd' reláci6nak. Amikor Fido létrejön, először
tibázisosztály konstru ktora hívódik meg (az em16söké), majd a kurya oszl:í ly
konstruktora fejezi be a kutya létrehozását. Mivel nem adtunk Fido-nak semmilyen pa-
ramétert, mindkét esetben az alapértelmezen ko nstruktor hív6doll meg. Fido addig
a pillanatig nem létezik, amíg teljesen meg nem "konstruá16dik n , aza z az eml6s része
és a kutya része is el nem kf:szül. Mindkét konstruktornak le kell tehát futnia.

Amikor Fido befejezi I::lcLpály{lj{ll, el6ször a kutyák a esLruktora hívódik meg, és csa k
ezut:ín az em[6sállatok deslrlJktora. Minde n desLruktornak megvan a lehet6sége, hogy
takarítson maga után a memóriában. Mi se fe[edkezzüllk el tehát takarítani kuty{lOk
után! A 16.3 Lista ezt mutatja be.

16.3 Usta - A konstruktorok és destruktorok hivása (conanddest.cpp)


o: II 16.3 II KonstruktoroK és destruktorok hívása
1: .inc1ud~ <iostream>
2.
3 : enum BREED { YORKIE , CArIDl, DANDIE, SHETLAND , OOBERMAN, LAS l ;
4.
5 : class MallUnal
6.
7 : public :
B: II KonstruktoroK
9 , Marrunal () ;
10 , -Hamma l (l;
ll ,
12 , II Hozzáfér6 fOggvények
13 , int GetAge() const { return itsAge; }
14 , void SetAge{int age) ( i tsAge = age; l
15 : int GetWeight() const { r eturn itsWeight ;
16, 6ra • Az örökl6dés

16: void SetWeight(int weightl { itsWeight weight; }


17 :
18 : II Más tagfüggvények
19 : void Speak() const { std::cout« 'Manunal sound!\n' ; }
20 : void Sleep() const { std : :cout « 'shhh. I'm sleeping . \n';
21 :
22: protected:
23 : int itsAgc;
24 : int itsWeight;
25 : l;
26 :
27 : class Oog : public Manunal
28 : (
29 : public :
30 : II Konstruktorok
31 : Dog() ;
32 : -Dog() ;
33 :
34 : II Hozzáfé~6 fOggvények
35 : BREED Ge tBreed() const ( return itsBreed ; )
36 : void SetBrcod{BREED brood) ( itsBreed = breed;
37 :
38 : II Más tagfüggvények
39 : void WagTail() (std::cout« 'TaH wagging ... \n" ; )
40: void BcgForFood(} { atd :: cout « 'Begging for food .. . \n' ; }
41:
42 : private:
43: BREED itsSreed;
44 : };
45:
46 : HaIl\JTlllI: :HaTMUll() ,
47, itsAge(l),
48 : itsWeight(5}
49, (
50 : std, , cout « "Mammal constructor ... \n ';
51 : )
52 :
53 : Hammal :: ~Mall\JTllll ()
54 : (
55 : std :: cout« "Hammal dcstructor ... \n';
56 :
57 :
58 : Dog: : Dog() :
59 : itsBreed(YORKIE)
60 : {
61 : std :: cout« 'Oog conSLructor . .. \n" ;
62 : }
63 :
64 : Dog : : ~Dog(l

65 : {
66 : std :: cout« 'Oog destructor . .. \n ';
67 : }
68 :
I
300 V. rész • Önl~6dés és többalakúság

69: int main()


70 : {
71: Dog fido;/1 a Kutya születése
72 :fido. Speak () ;
73: fldo.WagTail();
74 : std :: cout« "Fido i s · «fido . GetAge() « • ycars old\n" ;
75: return O,
76 :

Maromal constructor ...


Dog constructor ...
Mummul sound !
Tail wagging ...
Fido is 1 ycars old
Dog destructor ...
Mununul destructor ...

A program szinte megegyezik lIZ elózóvel, azzal a különbséggel, hogy most


ilkonstrukrorok és destn.lktorok kiírnak egy nyomjelz6 (Izenetet. Amikor Fido létre-
jön , e16ször 3z eml6sök konslrukloid hívódik meg, majd ezek ut1i.n a kutyák/!. Innent61
kezdve már teljes mértékben létezik a kUlya , és tagfüggvényei meghívhat6ak. Amikor
Fido hatókörön kívülre k erűl, először a kutyák deslruktora í~a ki nyomjelz6 Ozenelét,
majd az eml6sök destruktom,

Paraméterek átadása a báziskonstruktornak


Megv:m arra is a le h etőség, hogy a konstnlktorokat túlterhcljük; az cml6sállatnak pél-
dául meg szeretnénk határozn i az induló kor.it vah'Y a kutya fajtáját. Hogyan lehet meg-
oldani, hogy ti kor ~s sú ly kezdóértékek a megfelel6 konslruktormlk ad6cljanak át? Mi
'\\...~~'-.~'\...~~""lS..\.~~i;:,.~",,~~~~~'ts..\.~~ ...~'~L~~~~~~~~w..
ftdtétlenül?

...-.-
Egy bá:lisosztály inicial izációja úgy is történhet, hogy megadtu" .a
I'fm)a. zt\rí:»I:\ol:n ',fL. a'\;<I.'" "V7l.ft p .. t .. m.~\\Ct\C,,",,>::\. , anOS'j >e'L a \.~."""-"._ . '_ _

o: II 16 ,4 /1 Konstruktorok túlterhelése a származ~a


1 : 'include <ios tream>
2,
16. 6ra • Az öröij6dés 301

7 , public :
8 : /1 Konstruktorok
9: Hamma l () ;
10 : Marnmul(int age);
ll , -Mammal();
12 :
13: 1/ Hozzáfér6 fuggvények
14 : int GetAgc{) const { ret.urn itsAge ; }
15 : void Setllge(int age) { itsAge '" age ; }
16 : int GctWeight() const ( ret.urn itsWeight. ;
17 : void Setwelght(lnt wcight) ( itsWcight = weight;
18 :
19 : II Más ta9fü9gv~nyek
20 : void Speak!) const ( std :: cout« "Hammal sound ! \n "; )
21 : voi d Sleep () const. ( std :: cout « ·shhh. I ' ro sleeping . \n· ;
22 :
23 : protected :
24 : i n t i e sAga ;
25 : int itnWeight;
26 : );
27 :
28 : class OOg public Hammal
29 :
30 : public :
31 : II Konstruktorok
32: Dog();
33: Dog(int age);
34 : Dog(Jnt age, int wcight.);
35: Dog{int age, BREEO breed);
36 : Dog(int 4gC, int wcight, BREEO brccd);
37 : -Dog();
]8 :
39 : /1 Hozzáfér6 függvények
40: BREED GetBrced{) const ( rcturn itsBreed ; )
41 : void SetBreed(I3RRF.O breed) { itsI3reed = breed;
42 :
43 : II Más tagfOggvényck
44 : void WagTail {} ( .!ltd :: cout « "Tail wagging ... \n" ; )
45 : void BegForFood{) { std :: cout« " Begging for food ... \n" ;
46 :
47 : private :
48: BRF:RD i taBrm'ld ;
49 : };
50 :
51 : Hammal: : Marnmal () :
52 : itsA.gc{11,
53 : itsWeight(5)
54 : {
55 , std : : cout « "Hammal constructor ... \n" ;
56 :
57 ,
58 : Hammal::Mammal(int age} :
59 , itsllge(age) ,
60 : itswcight (5)
30z 1V.,ész • Örö~6dés és többalakúság

61 : {
62 : std :: cout« "Mammal{intl constructor .. . \n" ;
63 :
64 :
65 : Mammal : : -Mammal ()
66 : (
67 : std : : cout « "Mammal destructor ... \n' ;
68 :
69 :
70 : Dog : : Dog () :
71 : Mamma l () ,
72 : itsBreed(YORKIEI
73 : {
14 : std : :cout « "Dog constructor ... \n";
15 :
16 :
11 : Dog :: Dog(int age) :
78 : Mammal (age),
79 : itsBreed (YORKIE)
80 : {
81 : std : : cout « "OOg (int) constructor .. . \n' ;
82 :
8] :
84, Dog : :Dog(int age, int weightl :
85: Mammal(age),
86, itsBreed(YORKIE)
87, {
88 : itsWeight :: weight;
89 : std : : cout « " Oog (int, int) cons t ructor ... \n' ;
90 :
91 :
92 : Oog : : oog(int age, int weight, i3REED breed) :
93 : Mamma1 (age) ,
94 : itsBrccd (breed)
95 , (
96 : itsWeight weight;
97: std::cout« "Oog(int, int, BREED) constructor ... \n";
98 :
99 :
100 , Dog : , Dog(int age, BREED breed) :
101 , Mammal (age) ,
102 : itsBreed(breed)
103 : {
104 : std : , cout « "Dog (int, BREED) constructor . . . \n" ;
105 :
106 :
107 , Dog , , -Dog()
108 : (
109: std :: cout « "Oog destructor ... \n";
110 :
III ,
112 : int main ()
113 ,
114 : Dog fido ;
16. óra • f>J. örö~6dés 303

115 : Dogrove r (5) ;


116 : Dog bustcr{6 , 8) ;
117 : Dog yorkie (3,YORKIE);
118 : Dog dobbie (4 ,20, OOBERMAN) :
119 : fido.Speak();
120 : rover .WagTail () ;
121 : std : : cout « 'Yorkie is
122 : « yor ki e . GetAge () « • years old\ n':
123 : s td: : CQut « "Dobbic wcighs •
124 : « dobbi e. GetWeight() « • pou nds \n';
125 : return o:
126 :

Apropó Sormmok akimenetben


Az. elemzés utalásainak megkönnyrtéséhez sorszámokkalláttuk el a kimenetet. Fu-
tás közben ezek nem jelennek meg a képernvön.

1 : Mammal constructor ...


:2: Dog constructor ...
3 : Mammal (int) constructor ...
4: Dog (int ) constructor ...
5 : Mammal(int) cons t ruc t or .. .
6 : Dog (int , i nt ) constructor .. .
7 : Marruna l (int) constructor .
8: Dog (int, BREED) constructor ....
9: Mammal(int) constructor ...
10: Dog(int, int, BREED) constructor. ..
ll : Hammal sound !
1," : Tail wagging ...
13 : Yorkie is 3 ycars old
14 : Dobbic wc i ghs 20 Dounds
15 : Dog destructor . . .
16 : Hamma! destructor . ..
17 : Dog destructor ...
18 : Hammal destructor ...
19 : Dog destructor . ..
20 : Mammal d estructor. ..
21 : Dog destruct o r ..
22 : Hammal destructor ...
23 : Dog destructor ...

-
24 : Hammal dcstructor . ..

A 16.4 Lista 10. sorában megjelenik egy túlterhelt konstruktor: a Mammal elfogadja para-
méterkénl az em16s korát. Az 58-63. sorban találhaló megval6sítás az itsAge tagvá!cQ-
z6t a konstruktornak átadott érték alapján inicializálja, a paraméterrel nem jellemzett
itsWeight énékét pedig 5-re állítja.
304 1V. rész • Örö~6dés és többalakúság

A Dog számára öt tülterhelt konstnlktor áll rendelkezésre (32-36. sor). Az e lső az a lap-
énelmezelt változat, a másodiknak az á llat korát adhat juk meg (csak úgy, mint
a Mammal esetében). A harmadik ko nstruktor a kort és a súlyt kéri, a negyedig a kon
és a fajtát, az ötödik pedig a kort, a súlyt és a fajtát.

Figyeljük meg a 71. son , ahol a kutyák alapértelmezett konslruklOr.l az e ml ősök alap-
értelmezett konstruktorát hívja meg. Bár ez nem lenne kötelez6, ez egyben azt is doku-
memálja, bogy szándékosan a paraméter nélküli a lapértelmezett báziskonstmkto rt hív-
juk meg. A bázisko nstmktor meghívása egyébként is megtörténne, dc így még nyilván-
valóbban látszik erre vonatkozó szándékunk.

A 77-82 . sorban van az a kutyakonstruktor, amely egy paramé tert vár, éspedig az é let-
kon. A kezdőért~klistában (78-79. sor) a Dog iniciaJizá lja saját bázisosztá lyát, ii tadva
neki a kor par.'lmélelt, majd a fa jta inicializálása köve tkezik.

A 84-90. sorban találunk egy újabb kutyakonstnlktort, amely két paramételt vár. Megint
csak a saját bázisosztály inicializáci6j:lval k ezdőd ik az értékad61ista (a megfele le>
konstmktor meghívásáva!), de ezúttal a fajt<! be{dlítása ut:ín a bázisosztályb61 SZ{t.rmazó
itsWeight (stíly) tagvá llozót is inicia lizáljuk a weight paraméter révén. Érdekes,
hogyabázisosztály tagvállozóját nem lehet az é rtékadó1istálxm inicializálni. Vagyis
nem írhat juk ezt,
Dog: :Dog(int age, int waight) :
MarrulIal (age) ,
itsBreed{YORKIE),
itsWeight(weight) II hibás!
(
std :: cout « 'Dog{int, int) constructor ... \n";
)

mivel :l bázisoszlfi ly saját változóját tilos in inicia1i;cilni. Hasonlóképpen ezt sem írhat-
juk le:
Dog : : Dog(inL age , int weightl :
Mammal(age, weight), II hibás !
i t sBreed(YORKIE)
(
s t d :: cout « "Dog( i nt . i nt ) con6tructor . . . \n":

mivel a ManunalOSztálynak nincs súlyparamélert elfogadó konstruktora. Ezt az érték-


adást a Dog konstmktomnak törzsében lehet csak megtenni.
Dog : : Dog{int age, int weight) :
Hrurunal(age) , II báziskonstruktor
itsBreed{YORKIE) II inicializáció
(
itsWeight = weight : II értékadás
std :: cout« "Dog{int, int) constructoz:: ... \n" :
)
16. 6ra • Az örö~6dés 305

Nézzük végig a további konstruktorokat, és győződjünk meg arról, hogy értjük a mll-
ködésüket. Figyeljük meg, mi az, amit inicializálhatunk a kezdőértéklistában, és mit va-
gyunk kénytelenek a konstruktor tör/..sében megoldani.

A kimenet számozása megkönnyíti az elemzést Az első két sorban az alapértelmezeu


konstruktor ad hírt magáról: Fido létrehozásáról tanúskodik.

A kimenet 3. és 4. surd Rover létrejöttét mutatja; az S-6. sorban Buster szülelik meg,
mégpedig az egypar.unéleres Marnmal és a kétparamétercs Dog konslruktor j6voltából.

Miután minden állat eI6(tllt, működésük befejezése után hatókörőn kívül kerülnek.
Ahogy sorban szűnnek mcg lélezni, minden kutyára először a kutyadestruktor hívódik
meg, m;Jjd ezután az cmJősdestruktor; mindez összesen ötször 05-24. sor).

Remek példát láttlmk a szánuaztatott osztá lyok alnpmetódusainak tülterhd6s6rc.

Függvények felülbfrálata
A kutya objektumok elérik a Hammal bázisoszlály tagfüggvényeit és persze a saját Dog
tagfüggvényeiket is, amilyen például a WagTail () (faro!..'CSóválás). Ez utÓbbiak akár
felül is írhatják a bázisoSZlály függvényeil, ami azt jelenti, hogya s7.ármaztatOll osztály
új megval6sítást írhat II bázisosztály val:lmely függvényére. A származtatott osztályba
tartozó objektum ilyenkor a számánl írt függvényt fogja használni.

Pontos,lbban megfogalmazva: felülírásnak vagy felülbír-.ílatnak nevezzük azt, amikor


egy szármntatotl osztály olyan függvényt valósít meg, melynek mind a neve, mind
a paraméter-szignatúrája, mind a visszatérési értéke megegyezik egy bázisosztálybeli
13gfüggvényéveJ.

A paraméter-szignatúra, azaz a függvény prolotíp usa többet tartalmaz, mint egy vissza-
térési érték, mert (a paramétereken kívül) láthat6 belőle a függvény neve és al. esetle-
gesen használt const kulcsszó is.

A paraméter.szignatúra tehát a függvény neve, paraméterei (és azok típusai), esetleg


a c:onst kulcsszó, de nem értendő bele a függvény visszatérési értéke.

A 16.5 Lista bemutatja, hogyan lehet a kutyák számára felülírni az ernl6söknél általában
használt beszédet (Speak). Ezekből az osztály-dekladciókból helytakarékosságb61 ki-
maradtak a hozzáfér6 függvények.
306 V. rész • Öröld6dés és

1&.5Ii1ta- A - . - , .............ot
~
o: /1 16 . 5 1/ Báz i sosz tál y tagfuggvény éne k fe l u l í rása
.. egy származtatott os ztályba n
1 : #include <los t rea m>
2,
] : enum BREED ( YQRKIE, CAIRN, DANDIE, SHETLAND , OOBERMAN, LAS) ;
4.
5 , class M.limnal
6: (
7 , public :
8 , II Konstruktorok
9 : Hammal () { std :: cout « 'Marrunal constructor ... \0' ; }
10 : -Hamma l () { !:ltd :: cout « "Hamma l destructor ... \n' : }
11 ,
12 : II Más tagtüggvények
13 : void Speak()const { std :: cout« 'Ma mma l sQu nd l \n' ; }
14 : void Sleep()conat ( std :: cout« " shhh . I 'm s!eeping . \n ";
15 :
16 : protected :
17 : int itsAge ;
IB : int itsWeight;
19 : l ;
20 :
21 : class Dog public Mammal
22 :
23 : public :
24 : II Kons t ruktorok
25 : Dog()! std :: cou t « 'Oog cons t ructor ... \n';
26 : ~Dog(){ std :: cout« 'Oog des t ructor ... \n' ;
27 :
28 : II Más tagföggvények
29 : void WagTail() ( std : : cout« "Tail wagging .. . \n" ; )
30 : void BegForFood() ( std :: cout« "Begging (or food . .. \n" ; )
31 : void Speak()const ( std :: cout« "Woof!\o" ; )
32 :
33 : pr ivate :
34 : BREED itsBreed ;
35 : } ;
36 :
37 : int main()
38 :
39 : Mammal bigAnirnal ;
40 : Dog fido;
41 : bigAnirnal.Speak.();
42 : fido. Speak () ;
43 : return O;
44 :
16. óra • Az öröklődés 307

Mammal const ructor ..


Mamrna l construct o r .. .
Dog const ruc t or ...
Mamma l sound!
Woof !
Dog desLructo r . .
Mam.mal des true tor .
Mnmmal dcstructor ...

A 31. sorban a Dog osztályban felülirjuk a Speak () met6dust, aminek a hatásár.!. a ku-
tyák azt fogják mondani, hogy" IVOOp'. A 39. sorban egy bigl'l.nlma l (nagytífla/) ncvű
em l6s{J llalot hozunk létre; kOllstruktoriÍnak köszönhet6 a kimenet els6 sora. A 40. sor-
ban jön létre fi do kUlya, emiatt kapjuk a kimenet következ6 két sorát, ~Ihol az eml6s-
és a kutyakonsu1Jklor ad hírt magáról.

A 41. sorban megszó lal a biglulimal, majd a 42. sorban fido kutya is. A kimc netb61
látszik, hogy mindkét állat a saját Speak () met6dusát hívta meg. Végül :1 kt:t objektum
hatökö rön kívül kerül, és meghívódnak a destnIktorok.

Túlterhelés vagy felülfrás?


E kél fogalom nem áll messze egymástól, azonba n alapvet6en e lt ~r6 n tartalmuk. Ami-
ko r túlterhelü nk egy függvényt, akkor ann:tk több viÍltozatál készítjÜk el (tlgyanabban
az osztályban) azonos néven, de más-más paraméter-szignatúrftva1. Amikor viszont fe-
lülínlnk egy függvényt , akkor a szármaZL.1ton oszlályban ugyano lyan ncvCí és 118)'al1-
o l)'a/I paraméter-szignatúrájú függvényt deklarálunk, mint mni a báúsosztályban is
megvan.

A bázisosztály met6dusainak elfedése


AZ előz6 progmmba n a Dog osztály Speak () mel6dusa elfedte a b!izisosztály azonos
nevű Jllctódusát. Ez volt a slándékunk, azonban váratlan mellékhatáSOli is le hetnek e
lehetőségnek. Ha az emlősöknek van egy mozgást kifejez6 túlter11elt metódusa, és
a kutyák számára ezt felülírjuk , akkor ezzel az összes ilyen nev(i metódusváltozatot fe-
lülínuk.

Ha a Ma mmal túlterheli a Move () függvényt, és három változatot valósít meg belőle (az
egyik paraméter nélküli , a másik egy egész paramétert, a harmadik egy egész és egy
irány jcllegCí paramétert vár), és a Dog ezek közül csak a paraméter nélküli változatot
írja felül, akkor nem lesz egyszeru a Dog objekrumok számára a másik két Move ( ) -
változat elé rése. Ezt a 16.6 Listában liÍlhatjuk.
30a l V. rész • Örö~ódés és többalakúság

16.6 lista - Metódusok eHedése (hidingbase.cpp)


o: 1/ 1 6 . 6 II Metódusok el fedése
1.
2 : #include <l ost r cam>
3.
4: c l ass Hammal
5: (
G: public :
'I : void Hove!) const ( std : : cout « 'Hammal move one step\n' ;
8 : voi d Move{int distance) const
9 : { std :: cout « 'Marnmal move • « distancc «~ O stcp s . \n '; }
10 : protected :
11 : int i tsAgc;
12 : in t itsWeight;
13 : };
1 4:
15 : c l ass Dog public Ma mmal
16 :
17 : public :
18 : void Move() const { std :: cou t« 'Oog move 5 stcps . \n' ; }
19 : l ; II A jobb forditóprogramok figyelmeztp.tnok , hogy el fogunk fedni
.. egy fOggvényt !
20 :
21 : int main()
22 : (
23 : Hammal blgAnimal ;
24 : Dog fido ;
25 : bigAn i mal ,Movfl ( ) ;
26 : bigAni ma l.Mov e (2) ;
27 : fido . Move() ;
28 , II fido . Move(lO) ;
29 : return O;
30 :

Kimenet
Mammal move one stcp
MaJnlUal move 2 Gt ops .
Dog move 5 s l epl:l .

Be s

Minden fölösleges adatoLés függvényr cltünlettünk az osztátyokból. A 7. és 8. sorban


deklaráljuk a Mammal osztály tú herhelt Move () függvényeit. A 18. sorban a Dog feWlírja
a Move () függvény paraméter nélküli változatát. A 25-27. sorban hívjuk meg e függvé-
nyeket, melyek végrehajtását a kimenet mutatja. A 28. sor azonban megjegyzésbe van
téve, mert fordítá si hib{1l okozna. Ha nem történt volna felülírás, akkor a Dog péJd,lny
meghívhatná a Move ( egész) szignatúrájú változatot, de mivel felÜlírtl.lk az eredeti
függvényváltozatok egyikét, így valamennyi változatot újra kell fogalmazni, ha használ-
ni akarjuk őke t. Ez emlékeztet arra a s7,ab{t1yra, hogy ha megadun k saját konstruktort,
akkor a fordítóprognlm már nem t,ímogalja az alapértelmezett konstruktor hívását.

Gyakori hib-d., hogy függvénye k felülírásakor elfelejtjük a const kulcssZÓl, amely pedig
a paraméter-szignatúrn része. Igy a paraméter-szignatúra megváltoztatásával cs.lk elfed-
jük a bázisosztály eredeti függvényét, nem pedig felülírjuk

Egyes fordít6programok figyelmeztetnek az elfedésre valahol ;1 15-19. sor környékén.


A Borland fordító nem teszi ezt. Más fordítóprogrdmok úgy ítélik meg, hogy - bár van
lehetőség a metódusok elfedésére - cz gyakran tévedésb61 bkad, így inkáhh küldenek
egy figyelmeztetést, ha ilyent észlelnek.

A bázismetódus meghfvása
lia felül is írtunk egy biízismetódust, megvan rá a lehetőség , hogy m~gis azt hívjuk
meg, csak meg kell adni a teljes nevél. Ez a bázisoszt,'ily nevéb6I, kél kett6spontb61,
végül a függvény nevél>61 áll, például:
Mnmmal: :Move()

A 28. sort ennek megfelel6en átlehetlle fogalmazni, hogy lcfordu ljon a 16.6 Lista :
28 : fido.Mammal::Move(10);

Ez kifejezeuen a Mammal saját metódus:'ít hívja mcg. A 16.7 Lista ezt a lehct6séget nlU-
taLja.

, 6.7 Usta - A bázismetódu5 meghívása egy felülirt melódusból (callingbase.cpp)


o: II 16 . 7 II 11 bázismet6dus meghívása a felal!rL met6duGbó l
1 : j inc lude <iostrcam>
2:
] : cl ass Marranal
4, (
5 : public :
6: void Move () cons t ( std : : cout « 'Marrunal move one step\n"; )
7: void Move(int distance) const
8: ( std : : cout « "Hammal move
9: « d istance « ' steps . \n"; )
10: protected:
11 : int i tsAge;
12 , int itsWeight ;
13 : l;
14:
15: class Dog public Mamma l
16 :
17 : public !
18: void Move ()const ;
19 : };
I
310 V. rész • Örökl6dés és 1öbbalakús!g

20 ,
21: void Dog : : Move() const
22: (
23 : std: : cou t « 'In dog move . .. \n' ;
24 : Hammal :: Move (3) ;
25 : }
26 :
27 : int main()
28 :
29 : Manunlll b igAnimai ;
30 : Dog (ido ;
31 : bighnima l . Move{2) ;
32 : fido . Mammal :: Move{6);
33 : return O;
34 :

menet
Mammal move 2 step~ .
Mammal move 6 stcps .

Bamzás

A 29. sOl'ban létrejön egy bigAnimai (nagyl/ lIat) nevű cml6s{lllal, majd a 30. sorb:11l
fido, a kutya. A 31. sorban [[Ilható metód ushívás n e ml6sök Move () függvényét hívja
mcg, amely egy egész szám pardmétert vár.

A programozó a kutya számára is ilyen egyparaméleres Move () függvényt szeretne


meghívni, dc ez akadályba ütközik. A Dog osztály felü lírta az eredeli Move () függ-
vényt, de nem vette a fárad ságot II lúlterhelésrc, és nem fogalmazta meg az egypa ra-
méteres vflltozatot. Ezen úgy segíthetünk, hogy kifejezetten a bázisoSZlály Move ()
függvényét nevezzük meg a 32. sorban.

H81y81 H8lytelen

Érdemes kiLerjeszteni a bevMt osztá- Ne essünk abba a hibába , hogy a pa-


lyok fl.lnk cionalitását a szá rmntalott raméter-szignatúra megvá ltoztatásával
osztályokban. véletlenü l elfeclünk egy bázisfügg-
A bázisoszlály metóclusa in:lk felülírá- vényt.
sával érdemes bizonyos s7..lÍnnazta-
totl fiiggvények múködését megvál-
taztatni.
Kérdések és válaszok
Kérdés: Orók/6dncl.!-e több generáción át a származ/atoll osztályok adaltagjai 6függ-
em/ős, és elZ em/ős egy ál/al, akkor örökli-e II kut)'a az állatok-
t-tllyei? ifa a Mu lya egy
ra jellemző ada/okai és jiiggvényeket?

Válasz: Igen. Az öröklődés folyamatában a származlatott osztályok bázjs~ztályuk min-


den adatát és függvényél megkapják.

Kérdés: Álalakífhal-e etv' származtatott osztály egy nyi/vállos (p/lblic) bázisJliggvényl


priuá'ra?

Fá/asz: Igen, de ebben az esetben ez nem fog látszani il további származtatott osztá-
lyokbnJl.

Gyakorlatok
Az elmúlt 6dban megismerkcdtűnk ;iZ örökl6déssel. Most válaszoljunk meg néhány
kérdést és v{:gczzünk cl néhány feladatot tudásunk ellen6rzése és megerosítése végett!

Kvfz
l. Mi a lecsupaszítás célja?
2. Miért érdemes egy osztCtlyl szánnaztatva el őállítani?
3. A fejezel programjaiban (1 6.1-16.5) lalálkozhatott az enum ut:lsitássa1. Ez mit csi-
nál?
4. Miért lehet é rdemes elrejteni egy bázisfüggvényt a származtatott osztály e l ől
(mint ahogy az a 16.6 listában is történt)?

Feladatok
l. A hidingbase.cpp programban 06.6 Lista) vegye ki a megjegyzésből a 28.
SOlt. Mi tÖl1énik? Hogyan lehet mégis múködőképessé te nni a programot?
2. Módosítsa úgy a derivedobject. cpp programot 06.2 Lista), hogy a fajta
(breed) nyilvántartásám a felsorolásos változó helyett szöveges változót hasz-
mí ljon.
3. A baseoverride . cpp programban (16.5 Lista) csak az emlősök sz.3.mára áll ren-
delkezésre a Sleep () függvény. Módosítsa úgy a programot, hogy bigAnimal
és f ido is hívja meg ezt a mel6dust.
312 1V. rész • Örö~6dés és többalakúság

Válaszok a kvfzkérdésekre
1. A lecsupaszítás révén pillanatok alalt futtathat6vá tehető egy függvény. Nem kell
a feladat megvalósításának apró részletei re koncentrálni; egy kés6bbi időpont­
ban is be lehet fejezni a vázlatosan elkí!szült függvényt.
2. Egy osztály származtatása gyakran sokkal egyszenThb, mint a semmiből megírni.
Ha már néhány vonatkOzásban jól működik , miél1 kezdenénk elöl ről?
3. Az enum lehet6vé teszi (a mesterkélt és nehezen élthel6 számk6dok helyett)
a beszédes szimb61umnevek használat.ál, lrunt például a YORKIE.
4. A sz..~rmaztaton osztály viselkedése i dőnként erősen eltér a bázisosZtályét61, oly-
annyira, hogy néhány bázisf'Üggvény már haszná lhatatlan. Mivel nem mindig
van arra lehetőség, hogya bázisoszrályhoz hozzányúljunk (péld:lul mert nincs is
meg a forrása), ezt a megoldásT érdemes haszn{llnL
17. ÓRA

A többalakúság és a származtatott
osztályok
Ebben az órában a következőkrlíllesz szó:

• Mik azok a viltu:'Ílis függvények


• Hogyan használjuk a virtu:'Ílis dcslruktorokat és másoló konstmktorokat
• Miként teszik leher6vé a virtuális függvények a bázisosztályok többalakúsng:'Ít
• Milyen költségei és veszélyei vannak a virtuális függvényeknek

Avirtuális függvénye kkel megvalósított többalakúság


Az előző fejezet azt hangsúlyozta , hogya kutya típusú objekrum egyben emlős lípusú
objektum is. Eleddig cz pusztán .lZl jelentene, hogy a kmya objektum örökölt minden
Tulajdonságot (adatot) és képességet (fOggvényr) a bázisosztályát61. A C++-ban azon-
ban a "részt!' reláció ennél méJyebb összefüggést takar.
314 rész ·

A többalakúság lehetővé teszi, hogy úgy bánjunk egy szá rmaztatott osztállyal, mintha
az a bázisosztály objektuma lenne. Tegyiik fel például , hogy létrehozunk néhány spe-
cializált eml6stj kutyát, macskát, lovat stb. Mindezek az emlős osZtályból szá rmaznak,
így az emlős-tagfüggvények is elérhetőek a sÚmukra. Az egyik ilyen lehet a Speak () .
Minden emlősállat ad ki valamilyen hangol.

Meg szeretnénk tanítani minden származtatott állatfajt a specializált hangadásra. A ku-


tya ugasson, a macska nyávogjon stb. Minden osztályban felül kell írnunk a Speak ()
met6dust.

Ugyanakkor, ha már van egy sereg állatunk (például egy farmnyi kutya, macska, ló és tc-
hén objektum), akkor szeretnénk farm játt:kunkban tudatni ezekkel az állatokka l, hogy
jó vol na , ha beszélnének (SfX'ak), de azzal nem kívánun k foglalkozni, hogy miként is va-
16sítj:ík meg eZl a képességüket a saját Speak () metódusukkaL Amikor ezeket az állato--
,I
kat cgysz(,;!rú(,;!!1 <::1l116söknek (Mammal) tekintj ük, és meghívjuk r!ljuk Marranal. Speak ()
metódl.ls[, ,Ikkor ezzel a löbbBIBkúságot valósítjuk meg. Angolul ez a "polymorph" tulaj-
dons.'íg; a szó elemzéséve\ is a ~többalakúság" fogalmálloz jutunk. VaI6b,lll , ,IZ e ml ősök­
kel fog lalkozunk, annak többtele megjelenési formájában.

Lehet például egy eml6s objektl.lmra vonatkozó mutatót deklarálni, majd értékül adni
neki egy kutya objektum címét a dinamikus memóriában. Mivel a kutya ~ltSZl!' az em-
lősök halmazfinak, ez teljesen sL1bályos:

Mammal* pMammal = new Dog:

Ezzel a mutat6val aztán az eml6sök tctsz61cges lagfüggvénye meghívható. Igazából azt


szeretnénk, hogy ha egy függvényt felülírtu nk a kutya osztályában, akkor az hív6djon
meg alka lmas pillanatban. A virtuális függvények ezt teszik lehetőv(: . Ha több:!lakúnak
tekintjük ezeket az objektumokal, akkor elegendő meghívni az eml6s mutatóm vonat-
kozó tagfüggvényt, és nem kell a konkrét származtatott objektum mibenlétére koncent-
cllni (hogy ez mOSt épp egy kutya), vagy hogy miként is van megval6sítva a ha.szn:í-
landó függvény.

A 17."1 Lista szem l ~ lteti II vimlális függvényck által megva l6sítható többalakúságot.

17.1 U... - Virtu6lil flIggrinyek használata (virtmeltlod.cpp,


o : 1117 . 1 Lista - Virtuális függvény ek használata
l: 8include <iostream>
2,

,,
3: class Mammal

S: public:
6: Mallll\al() : itsAge(l) { std " cout« 'Ma!ll1\ll;l constructor ... \n· ;
7: -Marnmal{) { std:,cout« "Marnmal destructor ... \n'; }
8: void Hove() const ( std: : cout « "Mammal move one step\n" ;
9, virtual void Speak { ) const { std: :cout « "Harnmal speak! \n":
.6ra • 315

10 :
11: Dcotected :
12 : int itsAge;
13: };
14 :
15 : class Dog : public Mamma1
16 : (
17 , public :
18: Dog{) ( std:: cout « "Dog constructor ... \n "; )
19 : -Dog() { std " cout« "Dog destructor ... \n" ; )
20 : void WagTail() {std :: cout« "Wagging Tail ... \n" ;
21 , void Speak{)const I std :: cout « "Woof!\n "; }
22 : void Move()const { std :: cout« "Dog moves 5 steps ... \n" ;
23: };
24 :
25 , int main(}
26 :
27 , Mamrna l *pDog .. n ew Dog ;
28 , pDog->Move();
29 : pDog->Speak{) ;
30 : return O;
31 :

Hammal constructor ...


Dog Constructor ...
Mammal move one step
woof!

A 9. sorban az eml6s oszt.ály biztosit egy Speak () nevű vinuális függvényt. Az oszt.ály
meglcrvczőjc ezzcl kifejezi azt a sz.'indékát, hogy cz az osztály más osztályok bázisosz-
lálya legyen. A származulloll osztályokban valószínűleg felül fogják irni ezt a függvényt.

A 27. sorbHn létrejön egy eml6sre ulaló mUlll1Ó, a pDog; ehhez egy új kutya objekmm
címét rendeljük hozzá. Mivel a kutya eml6s, ez a hozzárendelés helyes. Ezzel a mutaló-
val hívjuk meg azután a Move () függvényt. A fordítóprogram úgy tudja, hogya pDog
egy eml ős mutatója, így az em16söknél ke resi a Move () metódust.

A 29. sorban a mutat6val a Speak () függvényre hivatkozunk. Mivel a Speak () vimIá-


lis függvény, a kurya osztálya által felülírt Speak () metódus hívódik meg.

Ez már-már bűvészmutatvány, legalábbis abban a vonatkozásban, hogya hívó függ -


vénynek csak egy emlős mutat6ja van, ám ill mégis egy kmya met6dusllud meghívni.
Ha egy egész lömbnyi emlős mutatónk lenne, és mindegyik valamilyen emlős -leszár-
316

mazOllrd hivatkozna, akkor is mind-mind sorban a neki szánt függvényt hívná meg.
A 17.2 Lista ezt mUlalja be.

0 , II 17 . 2 Lista - TObb virtuális függvény egymás utáni meghívása


1: 'include <iostream>
2,
]: class Mamma!
4,
5: public :
6: Mammal() : itsAge(l)
7; -Mammal( ) { )
8: virtual void Speak() cons t ( sta : : cout « "Mammal speak!\n";
9: protected :
10 : int itsAge;
ll : );
12 :
l] : c l ass Dog publ ic Mammal
14 :
15: public:
16: void Speak () const ( std :: cout « "Woof! \n " ; )
17: );
18:
19 :
20: class Cat public Hammal
21: {
22: public:
2]: void Speak()const { std::cout « "Meow!\n"; }
24 : );
25 :
26 :
27: class Horse public Hammal
28: (
29: public:
30: void Speak () const ( std :: cout « 'winnie! \n" ; )
31: J;
32 :
]] : class Pig public Hamma l
34 :
35 : public :
36 : void Sp eakO c onst ( f;td :: cout « 'Oink l \n' ; )
37 : );
38 :
39 : int main()
40 :
41 : Mammal * theArray(5J ;
42 : Mammal* pt r;
43: int choice, i ;
44 : for ( i " O; i <5; iH )
45 : {
46 : std : : cout « '(l)dog (2)cat (3)horse (4)pig : ' .
47 : std : : cin » choice ;
és a származtatott 317

48 , switch (choice)
49 : {
50 , case 1 ,
51 : ptr '" new Dog :
52 , break :
53 , case 2 ,
54 : ptr ,... new eat ;
55 , break ;
56 : case 3 :
57 : ptr = new Horse ;
58 , break :
59 : case <1 :
60 : ptr :: new Pig :
61 , br eak :
62 : default :
63 , pLr = new Ma lTunal :
64 : bre ak:
65 :
66 : the Array [ i] '" p tr :
67 ,
68 : tor (i-0 ; i<5 ; i .... )
69 : theA r ray f 1 J ->Speak () ;
70 , return O:
71 :

Kimenet
(1 )dog(2)cat (31 horse (4)pig : l
(l) dog (3)horse
(2)cat (4)plg : 2
{1 ) dog
(2)cat (3)horse (4)pig : 3
(l)dog (2)cat (3)horse
(l)dog (2)cat (3)horse
(4)pig :
( 4 ) picjj:

5
Woof !
Meow!
Wi nnie !
Oink !
Hamma l Speak

• S

Ez a végsőkig leegyszerűsített program, amdy minden osztálynál csak a legszerényebb


funkcionalitást valósítja meg, a virtuális tagfüggvényeket a lehet6 legtisztább formájuk-
ban mutatja be. Négy osztályt deklarálunk: kutyát, macskát, lovat és disznót, mindegyi-
ket az emlósból származtatva.

A 8. sorban az eml6s S p eak () függvényét virtuálisnak deklaráljuk. Később a 16, 23,


30. és 36. sorban a származlatott osztályok felülírják a Speak () függvény eredeti meg-
val6sításáL
A felhasználót arra szólítja föl a program, hogy válasszon, melyik objektu mot hozzuk
létre, majd a kiválasztott mutatók bekeriiInek egy tömbbe a 44-65. sorban.

Apropó Dinamikus kötés


Igen érdekes az, hogy fordítási idöben még nem lehet tudni, mely objektumokat kell
majd létrehozni, és így mely Speak () metódus meghívására lesz szukség. A pt r
mutató objeklumhoz kötése csak futási idöben történik meg. Ezt hívják dinamikus
kötésnek (vagy futásidejú kötésnek), ami a statikus kötés (vagy fordltásidejú kötés)
ellentéte.

Hogyan mílködnek a virtuális függvények


Amikor egy származtatott objektum, például egy kutY:1 (oog) létrejön, clóször a b{IZis-
oszt~íl y konslnlktora, majd a sz~rmaztatot! osztií ly ko nstruktora hívódik mcg. A '17. 1
ábra nwtatja, hogy hogyan épül fel a kutya objeklum II létrehozása un'in. Figyeljük
meg, hogya memóriában az eml6s "rész" szá rnám lefoglalt helynek a folytatása a kutya
"réSZ" helye.

, -----,,,

~
Em", "" I--_E_m_"_,_-.j
Kutya objektum

Kutya

17.1 i b",
KI/f)vl objektulII (/ létrel/OzáStI utáll

Amikor egy objektumban virtuális függvényt deklarálunk, az objektumnak nyomon


kell követnie ennek a függvénynek a később i szá rmazékail. Több fordítóprogram ké-
szír e célból egy virtuális függvény táblázatot, úgynevezctl v-táblázatot. Ez minden vir-
tu::llis típu s számára létrejön. Minden ilyen típusú objektum tarta lmaz egy virtuális táb-
lázat mutat6t, v-mulal6t (vptr), amely a virtuális táblázatra mutat

Bár a megval6sítások különböz6ek lehelnek, minden fordítóprogramnak meg kell 01-


dania ezt a feladatot; nem téved nagyot, aki ezt a vázlatot képzeli el.

Minden objektum vptr mutatója hivatkozik egy v-táblázalra, amelyben minden virtuá-
lis tagfüggvényre egy mUlató UlaI. Amikor a kutya eml6s . resze" U:lrejön, a vptr úgy
iniciali7..álódik, hogy a7. eml6s osztály virtuális függvényeire mUlasson 07.2 ábra).
VPTR
12 & Move

& Speak
Mamm al

17.2 ábra
Egy e/lllős /)-Ió",áztllt/

Amikor egy kutya konstruktor meghív6dik és elkészül az objektum kutya r(:sze is, ak-
kor a vptr ügy korrigál6dik, hogy a megfelelő mutatók a kutya objektum felülírt tag-
rüggvényeire mlltass:mak (ha vannak ilyenek) , Ezt mutlllja a 17.3 ábra.

VPTA -
l? & Mammal: Move ()
""""" & Dog: Speak ( l

""
17.3 ábra
Egy Imt)'fI v-túbláz(l/t/

Amikor egy emlős mutat6t használunk , II vptr továbbra is a helyes függvényre fog
mulami, az objeknun konkrét típusától függ6en. így, amikor meghívjuk a Speak () -et,
a helyes függvé ny indul el.

Nem juthatsz innen amoda


Ha egy kutyának van olyan metódusa (wagTai l ()), amely nincs meg az emlőS osztály-
ban, akkor azt nem lehet emlős mutat6val elérni (hacsak nem használunk típuskonver-
ziót kutya mutató,d). MinLhogy a wagTail () nem virtuális függvény , és nincs meg
az emlős osztály példányaiban, nem é rhető el kutya objektum (vagy leg.tlábbis egy er-
re való tipuskonverzió) nélkül.

Bár át lehet alakítani az emlős mutatót kutya mUL1tóvá, vannak ennél sokkal jobb és
biztonságosabb megoldástok a WagTail () elérésére. A CH nem nézi jó szemmel
320 rész • Örö~6dés és

;iZ explicit típuskonverziót, nlivel hibalehetőségeket rejt. A téma részletesen el6keru..


még a 18. órában, amikor a többszörös örökloof:srő1 beszélünk, illetve a sablonokn'
szól624. órában is.

Szeletelés (slicing)
Figyelem, a virtuális függvények búvészlllutatványa C5.1 k mutat6kr.t vagy hivatko~
ra működik! Egy objektum érték szerint való elküldése nem okoz virtuális
hívást. A 17.3 Lista ezt mutatja be.

11.3 Ulti - Az adatol< 1...._,"" ódák szerinti plllm6te,6tedú ko, fllicl ..."",
0 , /1 17.3 Lista - AdaLszeletelés az érLék szeri nt va16
... paraméterátadáskor
l: ~i nclude <ios t ream>
2.
3 : clasA Mammsl
4: (
5 : public :
6 : Hamme1() : itsAge{l)
7 : ~Mammul() ( )
8 : virtuel void Speak () const { std : :cout « "Hammal speak! \0";
9: protected :
10: int itsAgc;
ll : };
12 :
13 : class Dog public Mammal
14 : {
15 : public :
16 : void Speak()const ( std , : cout « 'Woof!\n'; )
17 : );
18 :
19 : class Cat : public Hamma!
20 :
21 , public:
22 : void Speak()const { std :: cout« 'Meow!\n'; }
23 , } ;
24 :
25: void ValueFun c tion (Hamma!) ;
26: v oid PtrFunction (Hammal');
27: void ReíFunction (Mammal&);
28 :
29 : int ma in()
30 : {
31 : ptr=O;
Mammal~
32 : int ehoiee ;
33 : while (1)
34 : I
35 : bool fQuit = fa!se ;
36 : std :, eout « "(1)d09 (2) eat (O)Qu it: ' ;
37 : std : : ein » choiec ;
38 : switch leho i ee)
a szánnaztatott

39 ;
40 : case O;
41 ; fQuit = true;
42 : break;
43 ; case 1 ;
44 ; ptr = new Dog;
45 ; break;
46 ; case 2 ;
47: ptr = new Cat;
48 ; break;
49 ; default ;
50 : ptr - new Hammal;
51 ; break;
52 : }
53 : i f (fQult)
54 : break ;
55 : PtrFunction(ptr) ;
56 : RefFunction(*ptr) ;
57 : ValueFunction (*ptr) ;
58 : )
59 : re t urn O;
60 :
61:
62 : void ValueFunction (Mammal HammalValue)
.. II Ez a függvény hívódik meg utoljára
6] :
64 : MammalValue.Speak{);
65 : }
66 :
67 : void PtrFunction (Hammal " pMarorna.l)
68 :
69 : pMammal->Speak(l;
70: }
71 :
72 : void RefFunction (Mammal & rMammal)
7]: {
74 : rMammal . Speak ();
75 : )

Kimenet
(l)dog (2) cat (O)Quit : 1
Woof
Woof
MilIMlal Speak!
(l) dog (2lcat (O)Quit : 2
Meow!
Meow!
Hammal Speak!
(ll dog (2)cat (O)Quit : O
322 1V. rész· Öröklődés és többalakúság

A 3-23. sorban az em lős, kutya és macska oszlályok leeb'Yszerusíleu változatait dekla-


ráljuk. Három függvényt is deklarálunk, PtrFunc t ion (). Re fFun ct i on (), és
Va lue Func tion (l nt'!ven. Ezek egy eml6s mutatót, e ml6s hivatkozást illetve eb'Y em-
l ős objektumot várnak par-améterkénl. Mindhárom függvény ugyanntteszi: meghívja
a Speak () metódusl.

A fel hasznflló választhat kutya és macska közötti választásának megfelel6en készül el


egy mulató a megfelelő típus!".! a 38-52. sorban.

A kimenet első sora szerint a felhasználó a kutyát v{L!aszlja. A kutya objektum a dinami-
kus mem6riatertlleten készül el ti 44 . sorban. Ez a kutya azlán mut;ltóként, hivatkozás-
ként és érték szerint keiii i átadásra <l három függv(:nynek. A mlllat6 és a hivatkozás
virtuális tagfüggvényeket hívnak meg, így végső soron a Dog->Speak () függvény ke-
lill meghívásra. Ez látszik a felhasználó első választása utáni két sorban.

A feloldott mutatót már érték szerint adjuk át. A függvény egy eml5s objektumot vár (er-
r61 tanúskodik a paraméter-szignatúrája), így a fordítóprogram lccsonkítja a kutya objek-
tumOt llZ emlős . részére". Ily m6don <I Z emlősök Speak () met6duS<1 kerül meghívásra ,
ahogy azt a klmenetben a felhasználó választ<Ís.'l utáni h;lrmadik sorb<ln láthatjuk.

ÁllatkírerlenllUcet a macskával is végrehajt juk, h:lsonló eredmé nnyel.

Virtuális destruktorok
Gyakori megoldás, hogy egy származtatott objektum mutatóját adjuk át Oll, ahol a bá-
zisosztály mUlatóját várja a program. Mi történik egy ilyen mulató törlésekor?
Ha a destruktor virmális (ahogy annak lennie kel!), akkor a megfe l e l ő dolog történik:
a származtatott osztály destmktora hívódik meg. Mivel a szárma zt:Holt osztály
destruktora automatikusan meghívja a bázisosztály destruktorát is, ezért a teljes objek-
tum megfelelő módon törlődik.

A levonható tanulság: ha osztályunkban akad akár csak egyetlen virtuális függvény is,
akkor a destrl.lktornak is virtuálisnak kell lennie .

Virtuális másoló konstruktorok


Ahogy korábban már említettük, a konstnlktorok nem lehetnek virtuálisak. Ennek elle-
nére vannak helyzetek, anlikor programunk kétségbeesetten próbál beszuszakolni va-
lahova egy bázisosztáJybeli objektumot címző mutatóI, és próbálja megszerezni a létre-
jöv6 származtatott objektum másolatát. Ennek a problémának jól bevált megoldása,
ho b')' egy másoló mel6dusl hozunk lélre a bázisosztályban, majd virtuálissá tesszük.
A másoló metódus létrehozza az aktuális objektum másolatál, és ezt adja vissza VissZ.1 -
térési értékként.

Mivel minden származtatott osztály felülírja a másoló metódust, a szánnazlatoU osztá-


lyok példányai jönnek létre ennek használatával. A 17.4 lista mutatja , hogya n.

11.4 Usta - Virtuális másoló konstruktorok (virtualcopy.cpp)


o: II 17.4 Lista - Virtuálio máo016 konotruktor
1, ~include <iostream>
2,
3: cl ass Hammal
1,
5: public :
6: MammalO :i tElAge{ l ) ( std : : cout « 'Mamrnal constructor . . . \n "; l
7: vir.tual -Hammal() ( std :: cout« 'Mammal dcstructor ... \n '; )
8, Mammal (const Mammal & rhs) ;
9: virtual void Speak () const ( std :: cout « ' MaJ~!nal spp.akl \n" ;
10: virtual Mamma1* Clone{) ( return new Mammal{*thisl; )
11 : int GetAge()const ( return itsAge; )
12 :
13: protected:
14 : int itsAgc;
15: );
16:
17: Mammal::Hammal (const Hammal & rhs) : itsAge{rhs . GetAge{»)
18: (
19: std : :cout « 'Hammal Copy Constructor ... \n";
20:
21 :
22 : class Dog public Mammal
23 :
24 : public :
25 : Dog() { std: : cout « 'Oog constructor ... \n"; }
26 : virtual -Dog() ( std : : cout « 'Dog destructor . .. \n"; )
27 : Dog (const Dog & rhs) ;
28 : void Speak()const ( std : : cout« 'Woof!\n ' ; )
29 : virtual Mamma l* Clone() ( retur n new Dog( *this) ;
30 : l;
31 :
32 : Dog : : Oog(const Dog & rha) :
33 : Hammal (rhs)
34 : {
35 : otd :: cout« 'Oog copy constructor ... \n" ;
36 :
37 :
38 : class Cat public Marnmal
39 : (
40 : public :
41 : Cat() std : : cout « "Cat constructor ... \n"; )
42 : virtual -Cat () { std :: cout « "Cat destructor . .. \n"; }
324 1V. rész • Örö~6dés és többalakúság

43 : COlt (const Cat &) :


44 : void Spcak()const ( std :: cout« "Meow!\n' ; l
45 : virtual Mammal* Clone() { return new Cat {*this ) ;
46 : };
47 :
48 : Ca t : : eüt (const COlt: & rhs) :
49 : Mammal(rhs)
50 : {
51 : std : : couL « "Cat copy constructor ... \n' :
52 :
53 :
54 : enum ANIMALS { MAMMAL, DOG, CAT};
55 : const int NumAnima1Typcs = 3;
56 : int main()
57 :
58 : Mamma1 ·theArray[NumAnimalTypes];
59 : MaITunal" ptr ;
60 : int choicc ,i;
61 : for (1 = O; i<Numl'u1ima1Types ; i ll )
62 : (
63 : std :: cout « '(l)dog (2)cat (3)Ma=1 : ",
64 : std: :cin » choicc ;
65: switch (choice)
66: {
67 : case DOG:
68: ptr = new Dog;
69 : break;
70 : case CAT:
71 : ptr = new Cat ;
72 : break ;
73 : default :
74 : ptr = new Mamma1:
75 : break;
76:
77: theArray ri J = ptr;
78:
79: Manunal ·OthcrArray [NumAnirna1Types) ;
80 : for (i=O; i<NumAnima1Types; iH)
81 : (
82 : theArray[il >Speak() ;
83 : OtherArray [il '" t hp.Array [i j - >C l one () ;
84 :
85 : f or (i=O;i<NumAnima lTypcs ; i ++)
86 : OtherArray [ i]->Speak() ;
87 : return O;
88:

-
1 : (l)dog (2)cat (3)Mammal: 1
2 : Hammal constructor .
3 : Dog Constructor . ,
17. 6ra· A és a szánnaztBtott

4: (l)dog (2)cat (3) Mammal : 2


5 : Hamma1 const ructor ...
6 : Cat constructor ...
7: (ljdog (2)cut (3)Mammal: 3
8: Mammal constructor ..
9 : Woof!
10: Mumma1 copy constructor ...
11: Dog copy constructor . .
12 : Meow!
13: Mamma! copy constructor . .
14 : Cat copy constructor . .
15: ManI/nal speak!
16: Mamma! copy constructor ..
17: Woof!
18: Meow!
19: Mamma! speak!

EI.

A 17.4 Lista igen hasonlít az előző kett6höz, attól eltekintve, hogy egy új virtuá lis t3g-
függvény is megjelem az emlős osztályban: a clone () . Ez egy mutat6t ad vissza t:gy
újonnan létrehozott emlős objekLUmr:a, miközben meghiv6dik a másol6 konstmktor,
aminek önmagát ("thiS) ;ldja át konstans 1livatkozáskénl.

A kutya és a macska is felülírja a clone () met6duSl, mclyben önmaguk másolatát ad·


ják át másoló konstruktoruknak. Mivel a clone () vinuális függvény, ennek hatásár..!
ténylegesen létrejön egy virtuális másoló konstnlktor, ahogyez:I 83. sorban látszik.

A felhaszná lónak Iehet6sége van kutya, macska vagy emt6s közül vfllasztani, melyek
létre is jönnek a 65·76. sorban. Minden választ{lsnak megfelel egy mulató, melyet a 77.
sorb~11l látható tőmbben tárol unk.

Ahogya program sorban végiglépclel az egyes iterációs lépésekben a tömbön, minden


objektumra meghívja a Speak () (82. sor) és a clone () met6dust (83. sor). A clone ()
meghívásának eredménye egy mUI,lló lesz, amely az eredeti objekmm m.ísolmára mu·
tat. Ez egy újabb tőrnbben tárolódik a 83. sor szerint.

A kimenet c1s6 sorában a felhasználó a kutya létrehozását választja (vagyis az l-est).


Az emlős és a kutya konstruktorai elvégzik teendőjüket Ugyanez megismétl6dik ma(.:5-
kára és eml6sállatra is a kimenet 4-8. soráb.m.

A kimenet 9. som mutatja a7. első objektum, a kutya megsz6lalását (az első for ciklus-
ban, a 82 . sorban). 1\ virtuális Speak () függvé nyt hívjuk meg, és a m egfelelő Speak ()
mcgvalósítás fut le. Ezek után a clone () függvény következik, és mivel ez szintén vir-
tuális, a kutya klónozó met6dusa lép munkába. Először az emlősök másoló
konsrruktom, majd a kUlyák másoló konstruktora indul el.
326 1V. resz • Örö~6dés és !Öbbalakúság

Ugyanez ismétl6dik a macskára (kimenet 12-14. sora) majd az emlósre (kimenet 15-16.
sora), Végül végigjárjuk frissen keletkezett tömbünket (kimenet 17-'19. sora, a program
85-86. SOI<.l), és megsz61altatjuk új objektLlmainkat

Az a f6 különbség e megközelítés és a másoló konstruktor használata közön, hogy itt


a progmmoz6nak kifejezetlen meg kell hívnia a c l one () függvényt. A másoló
konstruktor 3momatikusan meghívódik, amikor lemásolódik egy objektum . J6 tudni,
hogy minden származtatott oszt{ilyua n megírhatjuk saját felülíró c lone () függvényOn-
ket, bár ez a megközelítés sokat ronl a helyzet n!galrnasságán,

A virtuális függvények költségei


Mivel :1 virtuális függvényeket tartalmaz6 objektumoknak gondoskodniuk kell egy v-
t:íblázat karbantarcásáról , ennek va n némi többlet erőfo rrásigénye. Ha kis oszt:.'l lyról
van szó, amelyből várhatólag nem fognak m(ls osztályok örökl6dni, r,e rn é rde mes vir-
tuális függvényekbe bonyolódni .

Ha bármilyen t:lgfüggvényt virtuálisnak deklarálunk, ezzel már meg is fizettük a v-


táblázat költségeinek nagy részét (habá r minden tov:'ibbi megvalósítás foglal még némi
memóri:!terii letet). Innent61 kezdve va l ószínűl eg virtu~liské nt szeretnénk majd dekb-
r{llni a destruktorc is, sőt feltételezhető, hogy várhatólag minden egyéb tagfüggvény is
virtu~lis les?.. Vessünk szigorú pillantást minden nem-virtuális lagfüggvényre, és gon-
doljunk erőse n bele, hogy mivel indokolható, hogy ez nem virtuális.

Helyet Holytel..
Haszná ljunk virtuális függ;v€:nyeket , Ne jelöljük a konstruktorokat virtuá-
ha arm számítunk, hogy osztályu nk- lisnak.
ból ezek ö rök l őd ni fognak.
Használjunk virtuális dest11.lktorokat,
ha m:.'lr van virtuális függvényOnk.

Kérdések és válaszok
Kérdés: Miél111e neveznénk ki minden tagfilgguiin)'t vi11uálisnak?

Válasz: Az első virtuális függvény létrehoúsára komoly költségterhet ró a v-táblázat


előállítása.Az ezt követő többJetkö ltségek már elhanyagolhatóak. Sok C++ programo-
zó úgy véli, hogy ha már van egy virtuális fuggvé nyünk, akkor már mindegyiket annak
érdemes kinevezni. Más progmmozók ezzel szembe n úgy vélik, hogy csak indokolt
esetben szabad virtuális függvényt deklar.'ilni.
17.6... A ás a származtatott 327

Kérdés: Tegyük fel, IIogy báZ/sOSZlál)'llllkban van egy SomeFunc () nemi viltuális fi/gg-
lJéIl)', me~)lCt fúlterhelttink, azaz elfogad egy vagy kél egesz paraméle/1. Egy származ-
ta/ott osztálybanfclt1/ítj/lk az ''8J'JXlnlméteres változatot. Mi ló/tél/ik, ha a származta-
tott osztály egJl muta/ója meghívja CI ketparam(2teres változatol?

Válasz: Az egyp;mullétcres változat fe lülírása elfedi a bázisosztály összes ilyen nevű


függvényét. Ily módon fordítási hibát kapunk, amely aITÓl fog panaszkodni , hogya hi-
básan meghívoll függvény csak egy egész paramétert vár.

Gyakorlatok
Ebben az órában ;1 többalakúsággal és a származtatott osztályok szépségeivel ismer-
kedhetlük meg. Válaszoljtmk most meg néhány kérdést és végezzünk CI néhány fe l-
adataI ludf\sunk ell enőrzése végett!

Kvfz
1. l..e hcl-e úgy módosílani a slicing . epp progrdmOt 07.3 Lista), hogy
a Value~unction () felülírásával hívjuk meg a származtatott Speak ( )
függvényt?
2, Honnan mdja a program, hogy melyik virtuális függvényt kell mcghívni, ha
:lZ objeknunok egy a báziso.~ztá l yhoz tartozó változóban vannak?
3. Milyen típusú mel6dus nem lehet virtuális?
4. ~'I i II többalakúság?

Feladatok
L Mi történik, ha a sz!Írmaztatott osztály nem írja fölü l a bázisosztály valamelyik
virtuális függvényét? Tegye megjegyzésbc a virtmethod. epp program 07.1 Lis-
ta) 2l. sorát, melyben a kutya osztály Speak () függvényének deklarációja álL El
md képzelni olyan példát, ahol ennek van értelme?
2. Mi történne, ha:\ s licing . epp program (17.3 Lista) 8. sorából kitörölné
a virtual szót a bázisosztály Speak () függvényének tlcfiníciója e161? Miért
nem hív6dnak meg sohasem a felülíró függvények? (ScgíL<;ég: érdemes megnéz-
ni az els6 kérdést.)
3. Próbálja futtatni a fejezet minden egyes példaprogramját. Prób{lljon ki a könyv-
ben olvashat.ótól cltér6 értékeket is, hogy jobban érezze, hogyan is működnek
a fenti programk6clok.
32s 1V.rész • Örö~ódés és többalakúság

Válaszok a kvízkérdésekre
1. Közvetlenü! nem. Bár létre lehet hozni a függvé nyb61 egy kutya (oog) vá ltoza-
Lot, eZll:Öoha nem hívmí meg a fordítóprogram , mivel a várt paraméterv:.'iltoz6
a bázisosztályból való (emI6s; Mammal). A Val ue Function-nek pedig sajnos
csak az a változat;! kerül meghívásra, amelynek emlős a paramétere.
2. A y-t..í.blázat tartja nyilván ezeket az információkaL Ennek a táblázatnak ,IZ adm i-
nisztrációja jelenti II virtuál is függvények löbbletkölL'>égét a normál fOggvények-
hez képest.
3. A konstruktor (beleértve a má5016 konstruktort is),
4. A löbba1akús.'ig szerint úgy tekintjük a sz:.'irmntaloU oszLályok példányait. mint-
ha azok a bázisosztályb61 volnának. Mintha ugyanannak az osztálynak fóbb
megjelenési formája lenne.
18. ÓRA

A többalakúság kifinomult
használata
Ebben az 6rában a követk.ezőkrőllesz szó:

• Mi a típusvá ltás (casting down) és mikor é rdemes használni


• Mik az elvont adattípusok
• Mik az üres virtuá lis függvények

Problémák az egyszeres örökJődéssel


Az előző leckék során nl<1r beszéltünk a származtatott objektumok é~ alaposztályaik
többalakú kczclésér61. Uíthattuk, ho&'Y ha az alaposztálynak van egy Speak () tagfügg-
vénye, amelyet a származt,ltott osztályban felüldefiniálunk, akkor az alaposzÚIly tipusára
felkészített mutatóban, amely a származtatott osztályra mutat, helyesen a származtatott
osztályban felüldefmiáJt ragfüggvény jm élVén yre. A 18.1. lista ezt az esetet mllt.'l~a.
330 Iv. rész • Öriild6dés és többalakúság

18.1. Lilla - VirldlllIItIfiiIIriaJeI ( - . . " ,


o: /1 18 . 1 Lista - virtuális t a g füg gv é ny e k
1, #include <iostream>
2;
3: c l ass Ma nunal
4.
5: public :
6: Mammal () : itsAge(l) ( std : : cout « "Hamma l (eml6 s )
.. konst.ruktor ... \n" ; l
7: v i rt u al -Mammal() { st d: : cout « "Mammal (emI6a)
.. destruktor ... \n" ; }
8: virtual void Speak() const { std : : cout « " Egy Marm\al beszél!\n" ; J
9: p rotected ,
10 : i nt itsAge ;
ll : };
12 :
13 : class Ca t: pu blic Mumrnal
14 :
15 : p ublic :
16 : Cat{) ( std :: cout« 'Cat (macska) konstruktor ... \n ";
17 , -Cat!) {std :: cout« · Car. (macska) destruktor ... \ n " ;
18 : void Speak()const. I std:: cout« 'M ij~ú ! \n '; }
19 : };
20 :
21 , int ma i nl)
22 :
23 : Mamma1 " pCat", new Ca t ;
24 : pCa t ->Speak( );
::lS : r eturn O;
26 :

Hammal (eml6s) konstruktor


Car. (macskalkonstruktor
Mijáú !

A 8. sorban a Spea k () tagfüggvf!nyl virtuálisként határOZluk meg . A 18. sorban rdül-


definiáltuk, majd a 24. sorban meghívtuk. Ne feledjük, hogy a pCat mUlatót a Marnma l
osztályhoz vezettük be, dc egy Cat típusú objektum címét rendeltük hozzá. Ahogyan
a 17. leckében (Többahlkúság és származtatott D..';zrályok) már elmondtuk, ez a több-
alakúság lényege.

De mi történik akkor, ha olyan tagfüggvénnyel b6víljük a e at (macska) osztályt, amely


nem alkalmazható a Mamma l (emlős) osztály példányaira? Tegyi.ik fel, hogy egy Purr ()
(dorombolás) neV"Ú tagfüggvf!nnyel bővítjük a e at osztályt. A macskák dorombol nak,
de más emlősök nem , Az osztályunkat ekkor ekép p határoznánk meg :
18. óra • kifinomult használata

class Cat : publ i c Hammal


(
public :
Cat() { std :: cout« 'Cat (macs ka ) const ructor ... \ n';
-Cat () ( std : : cout « 'Cat (macska) destructor ... \ n';
void Speak () const { std :: cout « 'Mijáú! \ n'; }
void Purr () const ( std :: cout « 'rrrrrrrrrrrrrrrr\n' ; )
};

A probléma a következő: ha most meghívjuk a Purr () lagfüggvényt egy Mammal .típu-


sú" mutatÓn keresztül, fordítási hibát kapnánk. A Pun () hívása kor egy Borland fordí-
tó valami ilyesmivel válaszolna:
'vi r t uülmethode. c pp ' , E23l6 ' Purr ' is not a membe r o f 'Marruuul'
in function ma in () at l ine 50

Természetesen mfts rordítók 111fts hibaüzenettel állnának e l ő. Például valal11i ily\:!smivt::l:


error C2039 : 'Purr' : is not a member of 'Hammal '

Amikor a fo rdítÓ megpróbálja feloldani a Purr () tagfüggvényt a Marrunal-hoz renddt


virtuális tábláza.t alapján, nem fog ilyen bejegyzést találni. Ánehetjük a tagfüggvényt
az aJaposztályba, dc ez nagyon rossz megoldás lenne. Habár mint megoldás, k&L<;(:gte-
lenül mQ"köclik , llZ aJ:lposztá lyok felhígítá s.'l a sZllrmaztatott oszlályokra jellemz(5 metó-
dusokkal igen rossz progl'l.lmozói gyakorlat, és egyenes út a nehezen k:lrbanta rth:u6
kód készítése felé.

Az egész prohléma valójában a hibás tervezés eredménye. Általánossflgb:m az al aposz-


tályra felkészített, de a származtaton osztály példányával összerendelt mutatÓ azt jelen-
ti, hogy többalakú osztályként szándékozunk h'l.<;znál ni, és ebben az esetben nem kel-
le ne olyan tagfüggvények hívásával prÓbálkoznu nk, amelyek valamilyen szá rmaztatott
osztályra vonatkoznak.

Vagyis nem az a baj, hogya származtatoll osZl1Uyokra jellemz6 ragfüggvér1yekkcJ dol-


gozunk, hanem az, ha ezeket al aJaposltályhoz létrehozott mutatón keresztül prÓbáI-
juk meg elérni. Egy tökéletes világban, ha. ilyen mutatóink vannak, nem kellene
az ilye n tagfüggvények elérésére pr6bálkm:nunk.

De természetesen nem egy tökéletes világban élünk. Id6nként azon kapjuk magunkat,
hogy van egy csomÓ alapobjektumunk, például egy állatkert tele eml6sökkel. Egyszer
csak észrevesszük, hogy van egy macskánk (Ca t típusú objektum), amelyik a fejébe
vette azl az átkozott doromholásl. Ilyenkor általában csak egyetlen dolgaI lehetünk:
csalunk.

Csalni pedig úgy lehet , hogy az alaposztály ~tí pusú n mutatónk típusát megváltoztatjuk
(casting) és . típusazonosítjuk n a származlatott osztállyaI. Azt mondjuk a fordít6nak:
~Nézd Fordít6! Programozó vagyok, és pontosan tudom, hogy ez valójában egy macs-
332 1V. rész • Önl~6dés és többalakiJSág

ka, úgyhogy eredj, és csináld azt, amit mondok." Ez úgy hangzik, mintha egy gengszter
mondaná, mivel eközben úgy is viselkedünk; kier6szakoljuk egy macska (Cat) viselke-
dését egy emlős (Manuna l) lípusú elemre hivatkozó mulalóból.

A feladat végrehajtásához a dynamic_ ca s t operátort fogjuk használni. Ez az operátor


gondoskodik arról, hogy amikor típust válrunk, biztonságosan tegyi.ik azl. Segít továb-
bá gyorsan megtalálni azokat a részeket a kódban, ahol éltünk ezzel a lehet&éggcl,
így azonnal eltávolíthatjuk őket, a mint megjött a józan eszünk.

A dolog II következőképp mO"ködik: ha van egy olyan aJaposztály típusú mutat6nk,


mint II Manun",!, és ehhez a mural6hoz eb')' o lyan származtatott osztá ly példányát ren-
deljük, mint a Cat, akkor a Mammal mutatót, mint többalakú elemet haszn;ílhatjuk a vir-
tuá lis tagfüggvények elérésére. Ekkor, ha egy Cat típusú objekmmot akarunk rajra ke-
resztül elérni, annak is kifejezeuen a purr () tagfOggvényél, csinfilnunk kell egy Cat tí-
pusú lllulatót a dynamic_ cast operátor segítségével. Az alaposzt{lly mut:lt6 ellen6rzé-
se futásid 6bcn történik. Ha az átalakítás lehetséges, az új Cat típusú mutatónk létrejön.
Ha viszont az átalakítás nem lehetséges, vé gül mégsem Ca t típusú obje kturnra hivat-
kozmnk, az új mutató nk értéke null lesz. A J8.2. Lista ezt mutatja be.

18.2. Uoto-Dl....... ~ (aooIIoa1. mB --IG.........-


(dynallllccalt.cpp l
O, /I 18.2 . Lista - Dinamikus t ípus konverzió
l : 'include <i.ostrcam>

,.
2 , u!:ling std : : cout ; II a program használja az std : :cout hivást

4: class Mammal
5: (
6~ pubU c :
7: Mammal(l : itsAge{l) ( cou t « "Mammal (em1 6s )
konstru ktor ... \n ";
wo
8: virtual -Marnmaol () ( cout « "Marnmaol (emI6s)
wo destruktor ... \n" ;
9: virtual void Speak() const ( cout « "Egy Manunal
wo beszél ! \n "; )
10 : pro t ec t ed :
ll : int it sAge ;
12 : );
1) :
14 : class Cat: pub lic Mammal
15 :
16 : public :
17 : Cat () ( cout « "Cat (macska) konstruktor ... \n' ;
18 : _Ca t {) { cout « "Cat (macs ka) des trukt or ... \ " " ;
19: void Spea k () const { cou t « "Mij áú ! \n' ; }
20: void Purr() const { cout « "rr r r r rrrrrr\n ' ; }
21 : };
22 :
18. óra • A többalakClság kifinomult hasmálata 1333

23 : class Dog : public Hammal


24 : (
25 : public:
26 : OOg{) ( cout « (kutya) konstruktor ... \n";
'OOg
27 : -Dog() { cout «"Dog (kutya) d e s t ruktor ... \n';
28 : void Speak () cons t { cout « 'Vaú! \n" ; }
29: );
30 :
31:
32: int main(}
33: {
34: const int NumberMamma1a ,. 3;
35: Hammal- Zoo[NumbcrMammalsJ:
36: Mammal- pMammal;
37: int choice,i:
38: for (i=O; i<NumberMamma1s: i++)
39: {
40: cou t « "( l)Kutys (2)Macoka : ",
41: std : : cin » c hoicc ;
42: i f (choice "" l)
43: pMammsl = new Dog:
44: else
45: pMarnmal " new C<lt;
46:
47: Zoo[i] " pMalMlal;
48:
49:
50: cout « '\n';
51 :
52 : for (i=O ; i <NumbcrMammals ; iH)
53 : (
54 : Zoo[i]->Spcak{) :
55:
56: Cat *pRealCat" dynsmic_cast<Cat *> (Zoo[ij);
57 :
58: if {pRealCat}
59: pRealCat->Purr() ;
60: else
61 : cout« 'Húha, ez nem macska!\n';
6í! :
63 : delete Zoo [ i] ;
64 : cout « "\ n";
65 :
66 :
67: rcturn O;
68 :

IGmenet
(I)Kutya (2)Hacs ka : l
Hammal (eml6s) konstruktor.
Dog (kutya) konstru ktor . . .
334 1V. rész • Öröij6dés és többalakúság

(llKu t y a (2) Mac!lka : 2


Hammal (em16s1 kons truktor .. .
Cat (macska) konstru ktor .. .
(l)Kutya (2)Macska : 1
Mammal (emlos) konstruktor ...
Dog (kutya) konstru ktor ...

Vaú !
Húha, ez nem macska !
Hammal (cm16s) destruktor ..

Mijáú
rrr r r rrrrrr
Hammal (em16s1 deHtru ktor ...

Vaú !
Húha , ~Z n em maCSkll !
Mamma l (em16s) des t ru kt o r .

EI. S

A 38.·48. sorokb:Ln :l felhasználó választása szerint adunk hozzá Cat vagy Dog objekru-
mokat a Mammal típusú mutatók tömbjéhez. Az 52. sorban végigmegyünk a tömb ek....
mein, majd az 54. sorban mindegyik objektum speak () mCl6c\usát meghívjuk. A tag-
függvények többaJ:.kúként viselkedve különböz6 eredményekkeltérnek vissza: A ku-
tyák ugalnak, :I m:,cskák nyávognak.

Az 59. sorban egy Cat típusú objektumon szeretném meghívni ;1 purr () (dorombolás
tagfüggvé nyt , dc nem szeretném abban az esel!)en, ha Dog típusú objektummal van
do lgom. Az s6. sorban has7.0áll dynamic_cast operátor gondoskodik r6 1a, hogy
az objektum, aminek a purr () tagfüggvényét hívju k, biztosan Cat típusú legyen. És in
található a kulcsmomentum: Ha eat lípusú , a rnutató ~néke nem null, így megfelel
az 58. sorb:m találhmó fe ltételvizsgálatnak.

Elvont adattfpusok
Gyakr:m készítünk osztályhierarchiákat. Ha például létrehozunk egy Shape (síkidom)
osztályt alaposzlály gyanánt, majd ebből származlatjuk a Rectangle Úéglal:.1p) és
Circle (kör) osztályokat, máris hierarchia keletkezett, ha nem is valami bonyolult Ez-
után a Rectangle osztályból tovább származtathatjuk a Square (négyzet) osztályt,
nunt a négyszögek egyik speciális képvisel6jél.

Mindegyik sZiÍ rmaztatOlt osztály felüldefmiálja a Draw () (rajzo!), és a getArea () (teru-


letszámítás) metódusokat és még másokat. A 18.3. Lista a Shape osztály lecsupaszíton
megval6sítását mutatja a szánnaztaton Cir c1e és Rectangle osztályokkal.
18. óra • A kifinomult használata 335

18.3. Ut.. - SIkldom autítyok (.h.pecl....cpp)


O, II 18 . 3 . Lista - Sikidom osztályok
1, lincludc <iostream>
2,
3: class Shape
4: (
5, public :
6: Shapc() (l
7: virtual -Shape(){)
8: virtual long GetArea() { return -1 ; } II hiba
9: virtual long GetPcrim{) { return -1; }
10 : virtual void Draw() {)
11 : );
12 :
13 : class Circle public Shape
14 :
15 : public :
16 : Circlc (int radius) : i tsR!!Idius {radiusl (l
17 : -Ci rel e () {}
18 : long GetArea{) ( return 3 • itsRadius itaRadius ;
19 : long GetPerim() { return 9 * itsRadius; }
20 : void Draw ( ) ;
21 : privata:
22 : int i tsRadiu$ ;
23 : int itsCircumference ;
24 : };
25 :
26 : void Circ!e: :Draw()
27 : (
28 : std: : cout « "KOr rajzolását végz6 utasítások helye! \n";
29 :
30 :
31:
32 : class Rectangle public Shape
33 : (
34 : public :
35 : Rcctangle(int len, int width) :
36 : itsLcngth (len), itswidth(widt h) ()
37 : virtual -Rec tangle{) {}
38 : virtual long Ge tArea () ( return itsLength * i tsWidth; )
39 : virtual long GetPerim() (return 2 *i tsLength + 2 *i tsWidth;
40 : v irt ual int GetLength{) ( return itsLength;
41 : virtual int Gctwidth() { return itsWidth; }
42: virtual void Draw ( ) ;
43 : private :
44 : int itsWidth ;
45 : int itsLength ;
46 : );
47 :
4B : void Rectangle : : Draw()
49 :
50 , for (int i =' O; i<itsLength; i++)
V. rész • Öröklődés és

51 :
52 : for (int j = O; j<itsWi dth ; jH)
53 : std : : cout « "x ";
54 :
55 : std : : cout« "\ n " ;
56 :
57 :
58 :
59 : class Squace : public Rectangle
60 : {
61 : public :
62 : Square{int len) ;
63 : Square(int len, int width) ;
64 : -Square(){)
65 : long GetPerim() (return 4 * GetLcngth() ; )
66 : };
67 :
68 : Squarc : : Squace (i nL len ):
69 : Rectangle{len , l en)
70 : II
71 :
72 : Square : :Square(int len , int wldthl :
73 : Rectanglellen,width)
74 :
75 : if (CetLenoth() != GetWidthi»
76 : std : : cout « "Hiba, ez nem egy négyzet ...
... inkÁbb egy Téglalap?\n" ;
77 :
78 :
79 : int main ( )
80 :
81 : int choice ;
82 : bool fQuit = false ;
83 : Shape " sp ;
84 :
85 : while (1)
86 : {
87 : std :: cout« "(l)KOr (2)Téglalap (3)Négyzet (O)Kilépés : ",
88 : sLd : : ein » cho i ce ;
89 :
90 : s witch (choice)
91 : (
92 : case l :
93 : sp = new Circle(5) ;
94 : break ;
95 : case 2 :
96 : sp = new Rcctang1eI4.6);
97 : break;
98 : case 3 :
99 : sp = new Square (5) ;
100 : break ;
101 : def.ault :
102 : fQui t = t ru e ;
l B. óra •

103 : break ;
104 :
1 05 : if (fQuit.)
106 : break ;
107 :
108 : sp->Draw() ;
109 : std :: cout « "\n" ;
110 :
111 : r eturn O;
112 :

(l)KOr (2) 'régl a lap {3)Négyzct (O ) Kil é p és : 2


xxxxxx
xxxxxx
xxxxxx
xxxxxx
( l )KOr (2) Téqla l ap {3) Négyzet ( O)Ki lépés : 3
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
(l)KOr (2)Téglalap (3) Négyzet (O)Kilépés : O

A 3-11 . serok között határoztuk meg a Shape (síkidom) osztályt. A getArea () (terület)
és getPerim () (kerület) met6dusok egyelőre hibával térnek vissza, a Draw () (kirajzol)
tagfüggvény pedig nem csinál semmit. Végeredményben nem is tudjuk pontosan, hogy
milyen síkidomr6l V:1n szó . Csak a síkidomok különböző típusait (kör, tégla lap) nldjuk
kirajzol ni, a síkidom, mint elvont fogalom önmagában nem lerajzolhat6.

A eire l e (kör) osztálya Shape leszármazottja, amely felüldefiniálja a fent említett há-
rom ragfüggvényt. Figyeljük meg, hogy nincs szükségünk újabb vir t ual ku1c:ssz6m,
mivel ezt is örökölik HZ :t laposztályt61. Ám annak sincs semmilyen negatív következmé-
nye, ha máském járunk el, ahogy tesszük ezt a Ree tang l e (t6g1alap) osztá ly megh:Háro-
zásakor a 38-42. sorokban. Hasznos dolog szerepeltetni a vi r t ual kulcssz6t, egyfajta
doku mentádókéOl hadd emlékeztessen bennünket, hogy a tagfüggvény virtuális.

A Square (négyzet) a Rectang l e osztályból származik, és ebben is felüldefiniáljuk


a getPerim() metódust, a többit pedig változatlanul örököljük a Reetangle osztályból.
I
338 V, rész • Örö~6dés és többalakúság

Eléggé zavaró, hogy ól Shape osztályt is tudjuk példányosítani , jó volna, ha ezt mega ka-
dályozhatnánk. A Shape osztály csak azért van, hogy felületet adjo n a bcl61e származ-
latotl osztályok számám. Mint olyan, egy elvonl adattíp/lsr61 ( abstract data type) van
sz6, vagy rövidítve ADT-ról.

Az absztmkl adauípusok egy-egy fogalmat jelentenek (mint pé ldául a síkidom), és nem


o bje ktumo kat (mint például a kör) . C++-ban egy ADT mindig más sz<"irmaztatott osztá-
lyok alapjául szolgáló osztály, az ADT-k példányosítása é rvé nytelen mllvelel. Azaz, ha
a Shape osztályt ADT-ként határoztuk meg, nincs lehet6ségünk annak obje ktumként
tö né n6 példá nyosítására .

Üres virtuális függvények


A CH támogatj:l ;lZ elvont osztá lyokban az üres virtuális rüggvé nyck létre hozását.
Az üres virt1.lális fü ggvény olyan virtuális függvény , amely kötel ez ően felüldefiniáland6
a származtato tt osztályo kban. Egy virtuális függvé nyt úgy határozhatlmk meg üreské nt,
ha O kczd6érté kkellátjllk e l, mint a követk e ző példában is:
virtual void Draw() ~ O;

Bá rme ly osztály, amely ta rtlllm<lz egy vagy tö bb üres virtuális függv é nyt, val6 jában
ADT, és az A!rr-k példányosít..1sa é rvé nyte len művel et. Ha mégis ilyesmivel pró bálko-
zunk, annak ere dménye egy fo rdítási hiba. Üres virtuális függvény sze re peltetése egy
osztályban ké t do lgol jele nt a felh asználásra nézve:

• Ne péklá nyosíL<;uk az o bjektumot, szánnaztaSSlmk be16 1e.


• Ne fe ledjük fe lüldefiniálni az üres virtuális függvényeke t.

Mindegyik osztály, amely ADT-ból származik, Oreské nl ö rö kli aZ üres virtuális függvé-
nye ket, azaz Fe ltétlenül felül kell defmiálnunk ezeket, ha példányosítani sze re tné nk
az osztályunkat. Ennél fogva, ha a Rectang l e osztálya háro m üres virtuális függvé nyt
tartalmazó Shape osztályból származik, a Rectangle osztályban mindhárma! fe lül kell
definiálni, különbe n a Rec t a ngle maga is egy ADT lesz. A 18.4 . Lista elvo nt adattípus-
ké nt írja újra:l Shape osztályt. Cseréljük le a 18.3. Lista 3-11. soraib:m talá lható Sha pe
osztály meghatározását a 18.4. listában található osztálydefinícióra (ezzel a 18.5. Listát
k:lpjllk ere dményOl), majd futtassuk le újra a progf""dffiOl.

18.4. Ua.. - Elvo.. adatlip•• ok (adtcpp)


l : class Shape
2: (
3: public :
4: Shape(){}
5: virtual -Shape () {}
6: virtual long Ge t ll rea () O·
1: virtual long Ge tPe rim{) = O;
18. óra • A .

8, virtual void Draw() O;


9 : private :
10 : l;

Figye!em! Az adt.cpp nincs lefordfthat6 állapotban!


Ne veszódjünk a 18.4. lista !eforditásáva! - ez csak a változásokat tartalmazza
a 18.3. listához képest. A 18.5. lista (amely a 18.3. és a 18.4.listát összefésülé·
sével keletkezett) az, amelyik leforditható, próbálkozzunk azzal.

18.5. Lista - A Sh.p. _ y b.mutIIáo••Ivont .dottfpusk6nt 1.d1ah.pecl....cpp)


O: /1 18 . 5 . Lista - Síkidom osztályok
1: lIincludc <iostream>
2.
3: ClASS Shllpe
4: (
5: public:
6: ShaDe () {}
7: virtual ~ShaDe () (l
8: virtual long GetArea () • O;
9: virtual long GetPerim() =- O;
10: virtual void Draw () = O;
ll: };
12 :
1] : class Circ1c , public Shape
14 : {
IS: public:
16 : CI rclo (int radius) : itsRadius (radiusl {}
17: -Circle(){}
18 : long GetArea I) ( return 3 * itsRadius ,. itsRadius;
19 : long GetPerim() ( return 9 .. itsRadius;
20 : void Draw();
21: private :
22 , int itsRadius ;
23 : int itsCircumference;
24 : };
25 :
26 : void Circle: : Draw()
27 :
28 , std : : cout « 'Kör raj zolását végz6 utas ítások helye! \0' ;
29 :
30 :
31 :
32 : class Rectaog!e public: Shape
33 : (
34 : public :
35 : Rectanglelint Ion, int wid t h) :
36 : itsLength (len), itsWidth (width) l}
37 , virtual _Rectaoglol)l}
38 : virtual long Getl\rea(} I return itsLength * itsWidth; l
39 : virtual long GetPerim() (return 2*itsLength + 2"itswidth;
40 : virtual int GetLength() I return itsLength;
I
340 V. rész' Örökl6dés és többalakúság

41 : virtual int Ge tWid th () ( ret ur n itsWid th ; )


42 : virtual void Draw () ;
4] : private:
44 : int itsWidth ;
45 : int itsLeng th;
46 : l;
47 :
48 : void Rect ang !e : : Draw ()
49 :
50 , for (int i O; i<itsLength; i++)
51 : (
52 : (or (int j " O; j<itsWidth; j ! . )
5] : std : : cout « 'x ' .
54 :
55 : std :: c(')ut« "\n ";
56 :
57 :
58:
!.l9 : class square public Rectangle
60 : (
61, public :
62 : Square(int; len ) ;
63 : Squarc(lnt len, int width) ;
64: -Square () (l
65 : long GetPerim{) (return 4 • GetLength() ; }
66 : } ,
67 :
68: Square: : Square(int len) :
69 : Rectangle(lcn,len)
70: ()
71:
72 : Square : : Square(int l en , int width) :
73 : Rectangle(len,width)
74 :
75: i f (Get Length () !. GetWidth () )
76 : std: : cout <:< "Hiba, ez nem egy négyzet ...
... inkább egy Téglalap?\n" ;
77 :
78 :
79 : int main{)
80 :
81 : int cho i cc;
82 : bool f QuiL '" f alse;
83 ; Shapc " sp;
84 :
85 : while (1 )
86 : {
87 : std : : cout « "{ljKOr (2)Téglalap (3)Négyzet {O)Kilé pés: ".
88 : s t d: : ci n » cho ic e ;
89 :
90 , switch (choice)
91 ; (
92 : case 1 ;
93 : sp = new Circle(5) ;
18. óra· A többalakúság kifinomult használata 1341

94 : break ;
95: case 2 :
96: sp '" new Rectangle{ 4, 6);
97 , break ;
98 : case 3 :
99 : sp - new Square{5);
100, break;
101 : default :
102: fQuit '" true ;
103 : break ;
104 :
105 : i f (fQuit)
106 , break;
107 :
108 : sp->Oraw{) ;
109, std : :cout « "'n ";
110 :
111 : re t urn O;
112:

(l)KOr (2)Téqlalap {3)Négyzet (O)Kilépés : 2


xxxxxx
xxxxxx
xxxxxx
xxxxxx
(1)K6r (2)T6qlalap (3)Néqyzct (O)Kilépés : 3
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
(l)Kör (2)Tóqlalap (3)Négyzct (O)Kilépés : O

Látható, hogya program múködéséhez nem nyúlrunk. Az egyetlen különbség, hogy


most nem lehetséges a Shape osztály pl!ldftnyosítása .

Apropó Elvont adattfpusok


Az elvont adanlpusok meghatározása úgy történik, hogy az adott osztály definíciójá·
ban szerepeltetünk egy vagy több üres virtuális tagfüggvényt. Üres virtuális függvé·
nyek meghatározásához pedig írjunk ", O-t a függvény deklarációja után. Például:
class Shape
{
v ir tual void Draw () = O; II üres vi rtuális függvény
} ,
342 1V. rész • Örökl6dés és több,l,köság

Az üres virtuális függvénye k megvalósítása


jellemz6en, az elvont alaposztályokban szerep16 üres virtuális függvényeket nem imp-
lementáljuk. Mivel ilyen tipusú objektumot soha nem hozunk létre, semmi szükség rá,
hO!:,'Y megva l6sításl adjunk rá. Az ADT tisztán egy felület meghatározása kém működik ,
amelyből küJönböz6 osztályokat s7..:Írmaztathatunk.

Ugyanakkor adhatunk megvalósítást az üres virtuális függvények nek. A függvény ek-


kor az ebból az osztályból származtatott objektu mok tagfüggvényeiként hívhar6k, s
többnyire valamilyen általános működést biztosít annak a sZ:lrmazt:ltolt osztályokban
felüldefiniált függvények számára. A 18.6. Lista ic másolja a 18.3-ast, ezúttal a Shape
osztályt, mint ADT-t fe lruházva a Dr a w( ) üres virtuális függvény megval6sílásával.
A c i rcle osztály, mivel számára ez kÖlelez6 , felüldefiniálja a Dr a w ( ) lagfüggvényt', dc
ami a további rn t1veJ etek~l ill eti , visszahivaLkozik fe lrelé az alaposztály függv ényére.

Ebben a példában a további művelet nem más, mint egy újabb üzenet kiíratása a ké p-
erny6re, dc ne m nehéz elké pzelni, hogy az alaposzlá ly egy általános rajzolási lépést
osszon meg a származtatott oszlályok számár.!, l:>éldiiul egy olyan ablak létrehozását,
a hová az összes síkidom kirajzolódik.

18.6. lista - Üres virtuális fOggvénvek meghatérozása {implement.cpp )


0, 1/ 18 . 6 . Lista - Ores vi r tuális föggvény ek meghatározása
l , Iinclude <lostretun>
2,
], class Shape
4, (
5, p ublic !
6, Shape () {}
7, vi rtua l -Shape () ( )
8: virt ual long GetArea () = O;
9, virtual long GetPe rim()= O:
10 , virtual void Draw!) : O:
ll , private :
12 : };
l]:
14 : v o id Sha pe : , Dra w ( )
15 :
16 : st d: , cout « "El v ont ra jzolási lépések! \n" ;
17 :
18 :
19 : class Circ l e public Shape
20 : {
21 : p ublic :
22 : Circle ( int rad i us) : i t sRadius ( radius} {}
23 : _Ci rcle( ) l )
24, long GetAr e a () ( re t u rn ] * itsRa dius * i t sRad iu s;
25 , long GetPe rim() { r eturn 9 • itsRadius; }
18. óra • Atöbbalakúság kifinomult használata 1343

26: void Draw{) ;


27 : private :
28 : int itsRadius;
29 : int itsCircumference ;
30 : };
31 :
32 : void Circle : :Draw()
33 :
34 : std : : cout « 'KOr rajzolását végz6 utasitások helye!\n';
35 : Shape : : Oraw();
36 :
37 :
]8 : cIa!>!. Rectangle public Shape
39 :
40 : public :
41: Rectanglelint len, int wi d th) :
42 : itsLcngth( l e n), i ts Width(width ) ( J
43 : virtual -Rectang l e () {}
44: long Ga tArea ( ) ( eetu !:'n its Length * itsWid th;
45 : lo ng Ge t Perim() (return 2 "' its Length + 2 "it 9Wid th ;
46 : virtual int GetLength() ( rcturn its Length ; )
47: virtual int GetWidth() ( return itsWid th ; J
48 : void Oraw() ;
49 : privata:
50 : int itsWidth ;
51 : int itsLength:
52 : };
53 :
54 : void Rectanole : :Oraw!)
55 : {
56 : for (int 1 '" O; l<itsLength; i++)
57 , {
58 : for (int j '" O; jdtswidth ; j++l
59 : std : :cout « "x ";
60 ,
61 : std : : cout « "'n";
62 :
6] : Shape :: Draw() ;
64 :
65 :
66 : class Square public Roc tangle
67 : (
68 : pu blic :
69 : Squ are( int l en) ;
70 : Square(i n t l en , int width) ;
71 : -Square ( ) ( )
72 : long GetPcrim() (return 4" Ge tLength() ; )
7] : J;
74 :
75 : Squar e : : Square(int len) :
76 : Rectangle(len,lcnl
77 : {}
78 :
79 : Square , : Square(int len , int wi dth ) :
344 1V. rész • Örö~ódés és többalakúság

80 : Rectangl e(l en,width)


81 :
82 : i f (Get.Length (j !" Ge tWidth () )
83 : std : : cout « "Hiba, ez nem egy négyzet ...
.. inkább egy Té glalap?\n" ;
84 :
85 :
86 : int main()
87 :
88 : int choice ;
89 : bool fQuit :: false:
90 : Shape * sp ;
91 :
92 : while (1)
93 : {
94 : std :: cout « "(l)KOr (2)Tóglal<lp (3)Négyzet (O) Kilépés : ";
95 : std : : cin » choice :
96 :
97 : s witch (chojce)
98 : (
99 : case l ,
100 : sp - new Circle(5) ;
101 : break;
102: case 2 :
103 : sp:: new Rect a nglc (d,6);
104 : break ;
105 , case 3 :
106 : sp = new Square (5) ;
107 : break ;
108 : default :
109 : fQuit ,.. true;
110 : break :
lll :
112 : i f (fQuit)
113 : break ;
114 : sp->Draw () ;
115 : std :: cout« "\n' :
116 :
117 : return O;
118:

menet
(l)KOr (2)Téglalap (3)Négyzet (O)Kilépés : 2
XXXXXl(
xxxxxx
xxxxx x
xxxxxx
Elvont rajzolási lépések !
(l)KOr (2)Téglalap (3)Négyzet (O) Kilépés : 3
xxxxx
xxxxx
xxxxx
XXXXX
==x
Elvont raj zolás i lépések!
( l )Kőr (2)Téglalap (3) Négyz et (O) Kilépés: O

A 3-12. sorokban, meghalt'irozluk a Shape osztályt egy elvont adauípusként, mindhá-


rom tagfüggvényét üres virtuálisként megadva. Ne feledjük, hogy ez nem szükséges.
Ha csak egyet is üres virluáliskétl1 adnánk meg, az osztályunk ugyanúgy ADT lenne.

A ge tArea ( ) (:s ge LPerim() ragfüggvényeket nem valósÍtottuk meg, de a Draw ( )


függvényt igen. A Circ l e és Rectang l e osztályok mindketten felüldefini{llj~Ik
a Draw () 11lgfüggvényt, és mindketten vissza hivatkoznak felfelé az alaposzLály mel6-
dusára, kihasznMva :IZ elosztott möködésbe>l eredő előnyöket.

Az elvonatkoztatás összetett hierarchiája


Valahányszor cgy ADT-ból egy másik ADT-t sL1.rmazt.'mmk, el6jön, hogy bizonyos örö-
költ üres virtuális függv~nyeket megval6sítsunk, másokat viszont vált07.<lthmul hagyjunk.

Ha létrehozunk egy AnimaI (állatok) osztályt, szerepelni fognak benne az Eat: ()


(eszik), Sleep() (alszik), Move () (mozog) és a Reproduce () (sL1porodik) üres virtUlí-
lis fOggvények. Aztán ebből származtathat juk mondjuk a Marnma l (emJ6s) és Pish (hal)
osztályokat.

A tervezés során megállapíthatjuk, hogy minden Hammal ugyanúgy szaporodik , azaz


a Hammal : : Reproduce () nt:m üres lesz, míg az Eat: (), Sl eep (), és Move () maradnak
üres virtuális függvények.

A Mcllnmal osztályból származtaLhaljuk a Dog (kutya) osztályt, amelynek felül kel! defi-
niálni" mindhárom megmaradl üres virtuális függvényt, ezt követően példányosíthatjuk
az oS:d{l lyt.

Tervezők6nc ilyenkor azt határozzuk meg, hogy az Állatok és az E mlősök osztá lya nem
példányosílhatÓ, de minden eml6s örökli a szaporodásra vonatkozó működési eleme-
ket, azaz már nem kell felüldefiniálni.

A 18.7. Lista a fenti elgondolást mutatja be az oszlályok lecsupaszított megvalósítá.sával.


346 1V. rész • Örö~6dés és többalakúság

1B.7. Usta - ADT-k származtatása más ADT-kb61 {darivingadt.cpp)


o: II 18.7 Lista
l : II ADT-k szárma z tatása más ADT-kb61
2: #include <iostream>
3,
4: enum COLOR { Hed, Green , Blue, Yellow, White, Black, Brown} ;
5,
6, class Animal 1/ közös alaposztály a lovak és madarak számára
7, (
8, public :
9, AnimaI (int) ;
10 : virtual -Animat() ( std :: cout « "Anima! destruktor .. . \n"; )
ll : virtual int GetAge() const ( return itsAge; )
12 : virtual void SetAge{int age) ( itsAge = age ; )
13 : virtual void Sleep() const z O;
14 : virtual void Est() const = O;
15 : virtual void Reproduce() const = O;
16: virtual void Move() const .. O;
17 : virtual void Speak{) const = O;
18 : private :
19 : int itsAge;
20:
21 :
22 : Animal: :hnimal{int age) :
23: itsAge(agcl
24 : (
25 : std : : cout « 'Animal ko nstruktor . . . \n";
26 :
27 :
28 : class Mammal public Animal
29 :
30 : public :
31: Manunal(int age) : Animal (agel
]2 : { std :: cout « "Mammal konstruktor ... \n" ; }
3]: virtual -Hammal () { std :: cout « "Harranal destruktor ... \n";}
]4 : virtual void Reproducc() const
]5 : ( std: : cout « "Az eml6s0k szaporodásának leírása . .. \n";
36: ):
37 :
]8 : class FiSh public An imaI
39 :
40: public :
41 : Fish(int agc) : AnimaI (a ge)
42: ( std :: cout« 'Fish konstruktor ... \n" ;)
43 : virtual -Fish ()
44 : (std :: cout« 'Fish destruktor ... \n";
45 : virtual void Sleep() const
46 : (std :: cout« "a halak hortyogása . . . \n" ;
47 : virtual void Eat{) cons t
48 : ( std ,: cout« " a halak étkezési szokása .. . \n· ;
49 : virtual void Rcprodu ce{ ) const
18. ó.. oA kifinomult használata 347

50 : ( std :: cout « 'a halak i k rara kási szokása . . . \n" ;


51 : virtual void Move () const
52 : { std: : cout« "a halak ú szá s i technikája ... \n";
53 : virtual void SpealcO const { }
54 : };
55 :
56 : clnss Horse public Manunal
57: (
58 : public :
59 : Horse(int age, COLOR color) :
60: Mammal(agc) , itsColor(color)
61 : {std :: cout« 'Horse konstruktor ... \n"; }
62 : virtual -Horse()
63 : { std : : cout « 'Horse destruktor . .. \n'; }
64 : virtual void Speak () const
65 : (l:ItCl :: cout« 'Ny i haha! ... \n ";
66 : virtual COLOR GetltsColor() const
67 : { roturn itsColor ; }
68 : vir t ual void Sleep() const
69 : ( std : : cout « OA l ovak alvási szokásai ... \n' ; )
70: virtual void Eat() const
71: ( std : :cout « "A lovak étkezési szokásai .. . \n" ;
72: virtual void Move!) const
73: ( std , :cout« 'A 16 futni szokott ... \n";)
74,
75, protectod,
76: COLOR itsColor;
77: };
18 ,
19: class Dog public Hammal
80,
81: public:
82: Dog(int age, COLOR color) ,
83: Manm~l(age), itsColor(color)
81\, { std : :cout« "DOg konstruktor . .. \n"; }
85: virtual -Dog()
86 : ( std: : cout « "Oog destruktor . .. \n'; )
81: virtual void Speak()const
88 : ( std: : cout« ·Vaú ! .. . \ n ' ; l
89: virtual void Sloop() const
90 : ( std : : cou t « 'A kutyák alvás i szok ása i ... \n' ; )
91: virtual void Eat () const
92 : ( std : : cout « "A k utyák étk ezési szokásai ... \ n';
93 : virtual void Move() con s t
94 : { std :: cout « "A ku t ya fu t ni szokot t ... \n " ; }
95 : virtual void Reproduce() cons t
96 : ( std :: cout « "A kutyák szaporodása . .. \n" ; )
97 :
9S , protected :
99 : COLOR itsColor;
100 : l:
101 :
102 , int main ()
103 :
348 Iv. rész • Örö~6dés és többalakúság
104 : AnimaI *pAnimal =O:
105 : int choice ;
106 : bool fQuit = false;
107 :
108 : while (1)
109 : {
110 : std : : cout « • (l)Kutya (2)L6 (3)Hal (O)Kilépés : ';
111 : std : : cin » choice ;
112 :
113 : s witch (cho ice )
114 : (
115 : case 1 :
116 : pAnima l new Dog(5 , Brown) ;
117 : Ql;"eak ;
118 : case 2 :
119 : pAnimal new Horse(4, Black) ;
120 : break;
12 1: c<l~e 3 :
122 : pAnimal new Fish (5);
123 : break ;
124 , default :
125 : f Quit = true:
126 : break;
127:
128 : i f (fQuit)
129 : break ;
130 :
131 : pAnimal->Speak() ;
132 : pAnimal->Eat () ;
133 : pAnimal->Reproduce() ;
13 4: pAnimal->Move() ;
135 : pAnimal->Sleep() ;
136 : delete pAnimal ;
137 : std : : cout « "\n";
138 :
139 : return O;
140 :

menet
(l) Ku tya (2) L6 (3)Hal (O)KiIópós: 1
AnimaI konst r ukto r .
Mamma! konstruktor .
Dog konstruktor ...
Vaú ! ...
A kutyák étkezési szokásai ...
A kutyák szaporodása .
A kutya futni szokot t . . .
A kutyák a lvás i szokásai .
Dog destru k tor ...
Mamma! destru ktor . . .
AnimaI destruk tor . . .
(1)Oog (2)Horse (3)Bird (O)Quit: O
349

A 6-20. sorok közön adtuk meg az Animal osztály, mint elvont adattipus meghatározá-
sál. Tartalmaz egy i tsAge () (kora) nem űres virtuális függvényt , valamint öt másik
ürese!: Sleep (l, Eat (), Reproduce (), Move (), és Speak ().

A Hammalosztály az Animal-b61 származik, a 28-36. sarok között található a meghatá-


rmása. Nem hoz létre új tulajdonságokat, de felüldefiniálja a ReprOduce () függvényt,
általános formában leirva a sznporodásl az összes eml6sre vonatkozóan. A Fish osz-
tálynak (elül kell clefinb'ílnia :l Reproduce () tagfüggvényt, mivel az közvetlenül
az Animal OSZtá lyból szá rmazik, és nem tudja felhasználni a Mamma! osztály szaporo-
dására vonatkozó !llliveleteket (ami egyébként így helyes).

A Mammal-b61SZ!lrma7.Latoll osztályoknak nem keJl felü ldefini5lni II Reproduce () függ-


vényt, de meglehetik, ha afra nn szükség (mint a Dog osztá ly esetében a 95. és 96.
sorban). A Fish, a Horse és Dog osztályok mind felüldefiniálják az összes maradék
tagfüggvényt, amelynek következtében ezek az oszt,1]yok példányosíthat6k.

A program tönsében egy Anim!lltípusú ml..nat6t használunk a különböző szá rmazta-


tott objektumok e lé résé hez. Amikor a virtuális ragfüggvényckcl meghívjuk,
a futásid6beni hozzárendeléseknek megfelel6en mindig a kívánt osztályok tagfüggvé-
nyei futnak le.

Az AnimaI és M!lrnrMl osztálYOk példányosítására teu kísérlet fordítási hibához vezetne,


mivel mindkett6 elvont adattípus.

Mely trpusok elvontak?


Az egyik programban az Animal elvont oszttily, a másikban nem az. Mi hat5rozza mcg
azt, hogy mikor kell egy oS7.tályt elvontként meghatároznunk?

A kérdésre adott válasz nem a val6 világ valamely lényeges tényezője <llapján dönthető
cl, ez altól függ, hogya progl"""J.mban mit értünk valódi világ alatt. Ha olyan programot
készítünk, amely egy állatkertet vagy farmot modellez, az Animaltípus minden bi-
zonnyal elvont, míg a Dog egy olyan osztály lesz, amelyet példányosíthatunk .

Egy másik esetben, ha egy kuryaketrecet szimulálunk, a Dog osztályt valószínűleg el-
vont osztályként tartjuk meg, és csak a kutyák fajtáit (retriever, terrier, stb.) fogjuk pél-
dányosítani. Al. elvonatkoztatás szimj&t az határozza meg, hogy milyen finomság mel-
len szeretnénk megkülönböztetni a típusainkat.
I
350 V. rész • Örö~6dés és többalakúság

HoIya

Használjunk elvont osztályokat, hogy Ne próbáljunk elvonl osztályokat


általános működést biz.tosítsunk példányosítani.
egyes kapcsolódó osztályok számára
Definiáljunk fe lül minden üres
vimJális függvényt.
Uresként határozzuk meg azokat a
függvényeket, amelyeket kötelezőe n
felül akarunk definiáltatni.

Kérdések és válaszok
Kérdés: Miko,. beszéhi.nk a ,, 1I117k6désjelszivárogfCIttisár61''l

Válasz: Arra az elképzelC:sre vonatkozik, hogya megoszlOlt funktiókat mozgassuk ~fel­


fele egy közös ll laposztályba. Ha egynéllöbb osztály osztozik ugyanazon a mtlködési
módon, ajfmlatos egy közös ahlposzlályt találni a számára, ahol az adott függvényt
mcgva l6síthatjllk.

Kérdés: A IIl1ik6dés fc/szivároglattisa minden esetbell jó dolog?

"álasz: Igen , ha mindig több osztály által használt funkciókat sziv.1rogtatunk felfe lé,
nem jó dolog azonban, ha amit mozgarunk, az valójában egy fe lület. Az..'lZ , ha ne m tudja
nünden származtatott osztály használni az adon tagfüggvényt, a kkor hibás lépés közös
alaposztályba tenni azt. Ha mégis ezt tesszük, futásid6bc n kell majd váltanunk atípusok
között, és eldönteni, hogy hívharjuk-e az adon metódust az adott mUlatón keresztül.

Kérdés: Miér' rossz dolog cl dinamikus /ípusvállás (dynamie eas/il/g)?

l/Ci/asz: A virtuális függvények lényege, hogya virtuális táblázat döntse el f'l.ltásid6ben


a programozó helyett, bogy mi a típusa az adott objektumnak.

Kérdés: Miérl bajládjllnk elvon / adallípusok létrehozásával? Miért nem htllározzuk


meg őket nem elvon/kél/ t, és csak elkerüljük CI példá llyosí/tísát?

Válasz: A legtöbb C++-ban bevezetett szabály, konvenció célja, hogyafordítóra bíz-


zuk a hibák kiderítését, hogy eJkeriiljük a futásidőben bekövetkez6 hibákat, s a vásár-
lók a hibátlan programot kapják meg. Egy osztályelvontként történő meghatározása
1a.6ra- kifinomult használata

(azaz, üres virtuális függvények szerepeltetése a meghatározásban) a fordító számára


azt jelenti , hogy jelölje meg az összes objektumot, amelyeket elvont osztály példányo-
sításával akartak létrehozni, és geneciljon fordítási hibát. Megvan továbbá az az előnye
is, hogy felhasználhatjuk az osztályokat más al kalmazásokban, vagy átadhatjuk őket
más programozóknak.

Gyakorlatok
Miután megismertük a többalakúságot, vá1:lszoljuk meg a következő néhány kérdést,
illetve oldjuk meg a feladato kat, hogy elmélyíL'iük a témában szerzet! tudásunkat.

Kvfz
1. Mi a különbség egy virtuális függvény és egy felüldefiniált függvény között egy
alaposztályban?
2. Mi II kmönb.'iég az üres és nem üres vimJális függvények között?
3. Az elvontak aZ elvont adattípusok?
4. Mién helytelen az alaposztályokba tenni olyan tagfüggvényekel, amelyek csak
egyetlen származtatott osztály számára hasznosak?

Feladatok·
1. Módosítsuk a 18.7. Listában (derivingadt.cpp) bemutatott programot, úgy,
hogy példányosíL'iUnk A.nimal vagy Mammal típusú osztá lyokat. Mit fog mondani
a fordító, és miél'L?
2. Gondolkodjunk üzleti alkalmazásokban: milyen előnyt adhatnak számunkra
az elvont adattípusok? Megjegyzés: A banksz;í.mlák nagyon hasonlóak, és hason-
ló;:m is viselkednek, de létezik egy csomó banks7.ámlatípus.
3. Mi történne a 18.2. Listában (dynamiccast. cpp) bemutatott progranunal, ha
az 58. sorban elrávolítanánk az i f feltételt Ca 60. és 6 1. sorblIll ugyanúgy). Mely
objektumok ml1köclnének helyesen, és melyek eredrnényeznének hibát?
352 1V. rész • Örö~6dés és többalakúság

Válaszok a kvfzkérdésekre
1. A vimJális függvények esetében arra szánútunk, hogya származtalOlt osztályok-
ban felüldefiniálják őket.
2. A nem üres függvényeket tartalmazó osztályok példányosíthat6k. Ha a vinuális
tagfüggvények üresek, nincs futtatható kód, melynek eredményeképp fordítói
hibát eredménye7., ha megpróbálunk egy ilyen osztályt példányosítani. Az üres
virtuális függvényeket kötelező felüldefmiálni.
3. Valójában azért elvontak, mert vannak üres virtuális függvényeik, és önmaguk-
ban ncm példányosíthat6k. Nem készítbet6 bel61e konkrét példány, származtat-
nunk kell egy osztályt az elvont osztályból a megfelel6 tagfüggvényekkel, és
a szá nnaztatott osztályt példányosíthat juk.
4. Egészen 6szintén gyenge tervezést feltételez. Elméletileg ,IZ abposztály meLódu-
S:1in:1k:1 lehető legtöbb, ha nem az ÖSS:les származtatoLL oszL{J lyra i11cszkcdnie
kell. Az elképzelés, hogya származtatás során eg(:s:lítsük kl az aJapmetóduso-
k:1l, és nem az, hogy ne foglalkozzunk bizonyos r(:szcivcJ.
19. ÓRA

Láncolt listák
Ebben az 6rában a következ6kr611esz szó:

• Mi az II láncollIista
• Hogyan hozh,llu nk létre láncolt listát
• IIogyan zárju k egységbe a funkcio nalitáSl az öröklődés r(:vt:n

Láncolt listák és egyéb struktúrák


A tömbök olyanok, mint II Tupperware edények. Nagyszeru táro16helyet b iZLosít,l11ak,
de megválLozLathat3tlan ti méretü k. Ha túl nagy edényt választunk, akkor fel eslegesen
pazaroljuk erő"forrásainkat. Ha túl kicsit, akkor kiömölhel II tartalma, és óriási káosz
lesz a konyhában.

Az egyik megoldási lehet őség II láncolt lista használata. A láncolt lista o lyan adatszerke-
zet, amelyben kisebb tárol6egységek ~kattannak egymásbaH. Ebben a7. összefüggésben
a tárol6egységek olyan osztályokat jelentenek, melyek a listába szervezendő objektu-
mokat tartalmazzák. Az ötlet lényege abban áll, hogy olyan osztályt készítünk, ami egy
354 1V. rOsz • Örö~ódés és tóbb,lakúság

ad..telemet tartalmaz (pl. egy macskát vagy egy téglabpoü, és rámutat a lista követke-
ző tárolóegységére. Minden tárolandó elemhez készítünk egy táro16egységet, majd
a kívánalmainknak megfelelően összeláncoljuk őket.

Ezeket a kis tárolóegységeket csom6pontoknak hívjuk. Az első csomópont alistafej,


az uw!só a lista rarka. A listáknak három fontosabb változata vanj al egyszerúbbt61
a bonyolultabb felé haladva:

• egyszeresen láncolt listák


• kétszeresen h'incolt listák
• fák

Az egyszeresen láncolt listában minden csomópont a rákövetkező listaelemre mUlal.


Egy adott csomópont megtalMásáhol a fejtől kell indulni, és v~gigjárni az addig vez<::16
Össze.~ csornópontol, mint a kincskeres6 játékba n CA következő lcvf!l a dívány alatt
van~). A kél..Szerescn láncolt listáb,ln mindkét irányban lehet mozogni, előre és vissza-
felé is. A fa egy összetett struktúra, melyben minden csom6pont két (vagy három) má-
sik csom6pomra mutathat.

19.1 ábra
LlÍllcolt listák

A számításludományban más, ezeknél bonyolultabb és .okosabb" s[n.Jktúrákal is hasz-


nálnak; ezek általában egymáshoz kapcsolódó csomópol1lokból állnak.
Esettanulmány a láncolt listák használatáról
Ebben a részben egy esettanu lmány kapcsán alaposan szemügyre veszünk egy láncolt
lislál. Látni fogjuk, hogy miként lehet összetett adatstruktúrák:tl létrehozni, és - ami en-
nél is fontosabb - hogy hogyan használhatók ki az öröklődésben, többrétúségben és
egységbe zárásban rej161ehet6ségek egy nagyobb projektben.

Afelel6sség átruházása
Az objektum-oricntált programozás eb'Yik alapelve, hogy minden objektumnak legyen
egy (j61 definiált) feladata , amil kitűnőcn el tud végezni, és ami nem szigon"ian az 6 h'l-
táskörébe tartozik, azt bízza másra.

Az autó remek pélJ{tj:j c:wn elv megva16sít5sCl11ak. fo. motor dolg:! :l forgal6nyom;lték
biztosítása. Ennek a s:d:tosztása nem az ei feladata; CL már a sebességváltó és a fogas-
kerekek felelőssége. A fordulás lebet6ségét nem a motornak és a sebességváltónak kell
biztosítania: ez a kerekek teend6je.

Egy jól megtervezett gépnek sok kisebb-nagyobb, jól megérten része van. Mindegyik
jól végzi ;1 saját dolgát; egyiHt tudnak rm1ködni eb'Y magasabb cél érdekében. Egy jól
megLervezett program is hasonl6 szerkezetű: minden osztály tudja a helyét, mint:l sze-
mek ti kötötl mintálxm, és ezek együtt kiadnak például egy norvég mintás pul6vert.

Az. alkot6részek
A lfincolt listánk csom6pontokb61 fog állni. A csomópontokat egy-egy absztrakt osztály
jelenti, pontosabban ennek h{lfOm fajtája lcsz: egy oJy:1n, amelY:1 lislafejct képezi, lesz
egy farka a listának (gondoljuk meg, mi lcsz ennek a feladata), és nulla vagy több köz-
tes csomópont, melyek ri listába foglalandó adatokat fogják tartalmazni.

Figyeljünk arra, hogy ri lista és az adatok különooz6 fogalmat takarnak. Elméletileg


mindenfajta adatot listába szervezhetünk. Nem maguk az adatok vannak összekapcsol-
va, hanem a csom6pontok, amelyekben "mellesleg" adatok is vannak.

A program végső soron nem tud a csomópontokr61j ez csak a listával dolgozik. A listá-
nak azonban önmagában nem kell sokat tennie: egyszeruen átadja a munkát a csomó-
pontoknak.

19.1 Lista -láncolt lista használata (linkedlist.cpp)


o: II ********************************* ****** ********
] : II 19 . 1 Lista
2: II
3: II Célja a láncolt lista szemláltetése
4: II
35s 1v. rész • Örö~6dés és többalakúság

5 : 1/
6 : /I
7 : 1/
8 , 1/
9 : 1/ Ez a prog ram a láncol t listát objektum orientált szcmsz6gbdl
10 : II közelíti meg . A lista át r uházza a munkát a csom6pontoknak
ll : II A csomópont egy abszt r akt adattipus . Három fajta csom6pontot
12 : II használunk : fejet , lista f arkat és kOz tes csom6pontokat .
13 : 1/ Csak a kOztes csomópontok tartalmaznak adatokat .
14: 1/
1 5 : 1/ A Pat a os ztá l yt anna k be mu t atására hozzuk létre , hogy a lánco l t
16 : 1/ list ában objektumokut tar t halunk . (Pl . egy nOvekv6 szárnsorozatot . )
17 : 1/
18 : 1/ ~"'."'* ' ** * "*'*.*'**""* . ".* ""* **'***'.'
19 : linclude <iostrcam>
20 ,
21 : e num { kIsSrnal1er , kIst..arger , kIsSame} ;
22 :
23 : II ll. lán co l t listáb!l ágyazandó a d a t osztá l ya
24 : II A láncolt lista minden osztályának kell ismernie k6t metódust :
25 : II Show (megmutatja az értéket) és Comparc
26 : II (visszatérési értéke a relstiv pozíció)
27 : clau Data
28 : (
29 : public :
30 : Datalint val ) :myValuelvnl){)
31 : ~ Dnt(l() {}

32 : int Compare(const Data &) ;


33 : void Show() { std : : c out « myValue « std : : endl ; )
34 : private :
35 : int myValue ;
36 : };
37 :
38 , II A Compare fuggv énnycl dOn th e t 6 cl egy · egy a da t
39 : II elhelyezk edése II 1 istában .
40 : i.nt Data : : Compare{const Data'" theOtherData)
41 : {
42 , if (myVnlue < the0therData . myValue)
43 : return kIsSmaller ;
44 , if (myValue > t he0therData . myValue)
45 : return k I sLargc r;
46 : else
47: reLurn kI sSame ;
48 :
49 :
50 : II további deklarációk
51 : class Node ;
52 : class HeadNod e ;
53 : class Tail Node;
54 : class Interna lNo d e ;
55 :
56 : II Az a lábbiak jeleni tik meg a lista csomópont i objektuma i t
57 : II Minden származtatott osztálynak felül kell i rnia az Insert és
_ a Show me t Ódust .
19. óra· Láncolt li 357

58 : class Node
59 : (
60 : public :
61 : Node () ()
62 : virtual -Node () ()
6] : virtual Node" Inscrt(Data .. theData) ~ O ;

64 : virtual void Show() = O;


65 : private:
66 , );
67 :
68 : II Ez az aktuális objektumot hordozó csomópont .
69 : II Esetunkben az objektum Data tipusú
70 : II K6s6bb, amikor a sablonokról lesz sz6, látni fogjuk,
71 : II hogyan tehct6 meg mindez még általánosabban
72 : class InlernalNodc: public Node
73 , (
7 4: public ,
75 : Inter:"nalNodc(Oata .. theData , Node " next) ;
76 : virtual -Interna l Node() f delete myNcxt ; delete myOata ; )
77 , virtual Node" Insert (Data ' theData) ;
7B : virtual vojti Show()
79 , ( myData >Show() ; rnyNext->Show{) ; } II átruházás!
80 :
81 : private:
82 : Data" myData; II maga az adat
8] : Node ' myNext; II a láncolt lista kövctkez6 csomópontjára mutat
84: };
85:
86 : II A konstruktor csak inicializál
87: InternalNode: : InternalNodefData .. theData, Node" next):
88 : myData(theData),myNcxt(next)
89: (
90: )
91:
92 : II a lista tartalma
9] : 1/ Ha új elemet adunk a lJstához,
94 , 1/ akkor ez egy a csom6ponthoz kerul , ami eldönti, hogy merre
95 : II kell tovább haladnia az új elemnek , hogy végal helyére keruljön.
96 : Node" InternalNode : : Insert(Da t a ~ theData)
97 :
9B :
99 : II Az új tag ki~ebb vagy nagyobb nálam?
100 : int resu l t = myData->Comparc (*thcDat a) ;
101 :
102 :
10] : switch(result)
104 : (
105 : II közmegegyezés szerint az az első vizsgálat, hogy azonos-e
106 : case kIsSame: II átcsorgunk
107 : case kIsLarger: II az új adat elém ke rül
lOB : f
109 : InternalNode * dataNode =
110 : new InternalNode(theData, this) ;
lll , return dataNode;
358 1V. rész • Örö~6dés és többalakúság

112 :
113 :
114 : / / nagyobb nálam, így átadom a következ6 csom6pontnak,
115 : / / kínl6djon meg VELE inkább ő .
116 : case kI sSmaller :
117 : myNext = myNext->Inscrt(theDatu} ;
118 : r eturn thi s ;
119 :
120 : return this ; 1/ a fordítóprogram megnyugodhat
121 :
122 :
123 :
124 : / I A lista farka csak egy őrszem
125 : class TailNode public Node
126 : {
127 : public:
129 : 'l'ailNode () ()
129 : virtual -TailNode() (J
130 : virtual Node * Insert(Oata * theData) ;
131 : virtual void Show() { }
132 : private:
133 : l;
134 :
135 : 1/ Ha hozzám érkezik egy adat. akkor annak elém kell kerl11nie.
136 : /1 mert én vagyok a vég, és SEMMI scm jöhet utánam
137 : Node ~ TailNode :: Insűrt (Data * theData l
138 : (
139 : InternalNode • dataNode = new Interna1Node(theData, this) ;
140 : rcturn dataNocle ;
141. :
142 :
143 : II A fej nem tartalmaz adatot, hanem rámutat
14 4: II az ada tlista elejére
145 : class HeadNode : public Node
146 : (
147 : public :
148 : HeaclNode();
149 : virtual _HeadNodc() ( delete myNcxt ; I
150 : virtual Nod ű * Insert(Data * the~ta);
151 : virtual void Show() { myNext->Show(); }
152 : pr ivate :
153 : Node * myNcxt;
154: } ;
155 :
156 : 1/ Mihelyt kész a fej .
157 : 1/ létrehozza a farkat
158 : HeadNodc, : HeadNade ( )
159 : {
160 : myNext .,. new TailNode ;
161 : )
162:
163 : 1/ Semmi nem állhat a (ej előtt, igy továbbadja a z adatot
164 : 1/ a köv e tk ező csom6pon tnak
165 : Node * HeadNode :, I nscrt( Data * theData)
19. 6ra-

166 :
167 : myNext = myNext-> Inser t(theData) ;
168 : re turn thi s ;
169 :
170 :
171 : II Minden hatalmat én kaptam, és én nem fogok dolgozni
172: class LinkedList
173: (
174 : public:
175: LinkedList();
176 : _Li nkedList () { delete myHead; }
177 : void Insert (Data * theData) !
178: void ShowAl1{) ( myHead->Show{) ;
179: private ,
180: HeadNode ~ myHead;
181, };
182:
183 : II Születésemkor létrehozom a lista(ejet
18 4: 1/ Az megalkotja a farkat
185: II igy az üres lista mutatójo a fejre mutat,
186: II ez pedig a farokra . és közt.ük nincs sellVlli.
187: LinkedList: : LinkedList()
188: (
189: myHead = new HeadNode;
190 : )
191 :
192 : II Átruházás, átruházás , átruházás
193 : void LinkedList : : Insert(Data • pDaLa)
194: (
195 : myHcad->Insert(pData);
196: )
197 :
198 : II teszteloproqram
199 : int main()
200 : (
201 , Data * pDat.a ;
202, int val;
203 : LinkedList. ll;
204,
205 : II Kérjünk a fe1haszná16tól néhány értéket
206 : 1/ és t együk ezeket a listába
207 , for (;;)
208 : {
209 : std : : cout « -What value? (O to stop) : - ;
210 : std : : cin » val;
211 : i f (lva!)
212 , break;
213 : pDat a = new Data(val) ;
214 , ll . Insert (p Data) ;
215 : }
216 :
I
360 V. rész' Öröklődés és többalakúság

21 7 : II Most j árjuk be a listát és mutogass uk meg az ada tokat


21 8 : 11 . ShowAl 1() ;
219 : return O; 1/ II hatókör ön kivűl ke rű l és mcgs zan i k
220 : }

menet
Wh ot value? (o to stop) : 5
What value? (o to s t op) : a
Whac valuc? (o to stop) : 3
Wh ot value7 (o to !S t op) : 9
What value? (o to s t op) : 2
What va luc? 10 to stop) : 10
Wh ot value? (o to s t op) : O
2
J

,,
5

10

EI. s

konst~n!i három értékkel:


Az els6, ami a szemünkbe ötlik , az egy felsorolásos tTpusú
kIsSmaller, kIsLa.rger, és kisSame (kisebb, nagyobb, egyel/ló), A lista minden ob-
jektumának ismemie kell a Compar e () függvényl; ezek .1 felwrolásos vfiltoi'.ók ennek
a függvénynek a visszatérC!si értékei.

A 27-36. sorban létrejön az illusztrációs célú Data osztály, ennek Compare () metódusa
a 40-48. sorban készül eL A Da t a osztálynak egyetlen stiÍmC:rtékc van, amely össze
tudja magát hasonlílllni ( compa re) egy másik Data osztá ly száml!rtékével, illetve V'1n
egy Show() tagfüggvénye is, mellyel ki lehet íratni a számértéke!.

A láncolt lista megértésének legegyszeníbb mMja, ha végign&zzük példánkat, amely azt


haszn:ílja. A 199. sorb:m kezdód ik a főprogram. A 201. sorban egy Dat a objektum"l irá-
nyul6 mutatól hozunk lérre, a 203. sorban ped ig egy helyi láncolt listát defjni:1Iunk.

A láncolt lista létrehozásakor a 187. sor konstruktora indul cl. Ez csak annyillesz, hogy
elhelyez a memóriában egy HeadNode (lis/aJf!) csom6jJ()//t) ubjektumot, és ennek d mét
átadja annak a myHead mutat6nak, ami a láncolt lista fe jét tartalmazza (180. sor).

A HeadNode elhelyezése meghívja a 158. sorban definiált HeadNode konstruktort,


amelynek felad<lta a Ta il No de memóriafogl<tlása és címének hozzárendelése a listafej
myNe xt ffiUlatójához. A TailNode létrehozás<l meghívja a 138. sorba n látható
Ta ilNod e konstruktort, amely egy helyben kifejteu (munkál nem végző) függvény.
• láncolt listák 361

ily módon pusztán azzal, hob'Y elhelyezünk egy láncolt listát a veremben, létrejön an~
nak feje és farka is, b kiépülnek a megfele l ő kapcsolatok - ezt mutatja a 19.2 ábra.

Lárlcolllisla
Kezdő csomóporlt Záró csomóporlt

19.2 ábra
A fcí/Jcolllis/a l.!ezdót.ifflljJOla

A 207. sorban egy végtelen ciklus kezdőd ik. A felhasználó bcírll ~lt kül ö nböző számér-
tékeket, melyek a h'inc.:olL lista elemei lesznek. B5 rmeiuoyi értéket he lehet írni ; a O be-
írásával lehel befejezni a l:>evilelt. A kód 211. som éJtékeli ki a bevitt sZ{lmol, és ha ez
O, akkor kilép II ciklusbóJ.

Ha nem O-l írt be a felhasználó, akkor egy új Data objektum jön létre a 213. sorban,
melyet a 214. sorban illesztünk a listiiba. Tegyük fel, hogy 15-öt írt be a fe lhasználó.
Ez meghívja ri 193. sorban található Insert () taglUggvényt.

A láncolt lista azon nni leoszt ja a listarejnek az objektum bcszúr{ls5n:tk fe le!6sségéc,


amely a 165. sorban található Inuert () tagfüggvényt hívja meg. A fej sem viseli sokáig
a munka felelősségé n ek terhét, hanem azonnal ,ítruházza a myNext áltlll mut:llotl cso-
m6pontnak, amely (a legels6 pillanatban) a lista [<I rka. (EnMkezzilnk vissza, hogya fej
létrejönekor :lZonnallétrejött egy farokra mutató kapcsolat.) így véglil a 137. sorban ta-
lálható Insert () tagfüggvény végzi el a piszkos munkál.

A TailNode: : Insert () tudja, hogya kapott objektumot közvetlenül önmaga elé kell
beillesztenie. Azaz az új objektum közvetlenül a lista farka előtli helyre keru!. A 139.
sorban létrehozunk egy új InternalNode (k6ztes cso mójJom) objektumot, ál<luva neki
az adatot és egy mulatót önmagára. Ez meghívja a% In t ernalNode konstruktorát a 87.
sorban.

Az InternalNode konstruktora csak annyit csinál, hogy arm az objektumra inicializálja


a saját myData mutatóját, amelyet Data p<lf".unélerként mcgk<lpou, és a megkapon cso-
mópont címére állítja a myNext mutatóját, amely jelen esetben a lista fa rkát jelenti.
(Emlékezzünk, hogy a lista farka a saját mutatóját adta át pa ra métcrOl.)

Most, hogy elkészült az InternalNode, ennek a köztes csom6pontnak a memória-


címét kapja meg a da taNode mutató a 139. sorban; ez a cím lesz aztán a Ta il-
362 V. rósz • Örö~6dés és .

Node: : Insert () metódus visszatérési értéke. Visszakerülünk a HeadNode : : Insert ()


tagfüggvényhez, amely a 167. sor szerint hozzárendeli a kapott friss InternalNode CÍ-
mel a saját myNext mutatójához. Végül a HeadNode eimét megkapja a láncolt lista,
amely azlán semmit scm csinál vele (195. sor), hiszen 6 már rudja a HeadNode eimét.

Miért bajlódunk akkor ezzel a visszatérési értékkel, amit úgyis eldobunk? Az Insert-ct
a bázisosztályba n (Node) deklarálru k. A visszatérési érték más megval6sítások swmám
fomos. Ha me~:válloztaljuk a HeadNode : : Ins ert () visszatérési értékél, fordítási hibát
kapunk. Érdemesebb egyszerűe n visszaadni a HeadNode-ol és e lhajítani a kapun címet.

Mi is törLénllehát? Bckcrült egy adat a láncolt listába, melyet a lista át.adoll a fejnek.
A fej eZl vakon továbbadta oda, ahová a saját , következő elem" (myNext) mut3lója é p-
pen irányult. Az első alkalomma l a fej é ppen a lista farkára mutatoll, amely meghívása-
kor rögtön létre is hozott. egy köztes csomópontol. Ennek " következő elem"-e a lista
farka lett. A farok viSSzaadta a friss csomópont dmét a(z 6l meghívó) fejnek, amely át-
állította " követ kező elem" mutatóját a friss csomóponLr<I. Éljen! Az adat a m egfe l e l ő
helyre kelült, ahogy aZl a "1 9.3 ábra is mUlatja.

Az első csomópont beszúrása ut{tn a progmmvezérlés visszatér a 209. SOrr.l. Ismét kiér-
tékel6dik a felh:lsználó ált:11 megadott szám. Tegyi.ik fel , hogy cz most a 3. Ennek hatá-
sára l(:trejön egy (lj Data objektum (213. sor) és beil1eszt6dik a listába (2 14. sor).
láncolt lista
Kezdö csomópont Záró csomópont

Belsö csomópont
value ~1 5

myO~la
"'"

19.3 ébra
il láncolt lis/ti (jz első elem />esnírása Illáll

A 195. sorba n a lista újra átadja a fejnek a kapott adatot. A HeadNode: : Insert () most
is annak adja át ezt, akire a myNext mutat. Mint tudjuk, ez most az a csomópont,
amelynek Data része 15-öt tartalmaz. Ez meghívja az InternalNode : : Insert () me-
tódust a 96. sorban .
• láncolt listák 363

A 100. sorban az akmális Int er nalNode ös..<;zehasonlitja a myData állal mulatott szám-
értéket (ami most 15) azzal a számmal, ami a kapott t heData objektumban van (ez
most 3), mégpedig a Compare () tagfüggvénnyel (40. sor).

Az összehasonlítás során az derül ki, hogya 15 (myValue) nagyobb, mint a 3 ( t he -


OtherData . myValue), így a kIsLarge r lesz a visszatérési érték. A vezérlés emiatt
a 107. sor,..1 kerül.

Az új Data objektum számára létrejön egy új I nternalNode csomópont. Ennek nkö-


vetkcz6 eleme az aktuális InternalNode objektumm fog mutatni. A frissen keletke-
zett InternalNode címe lesz az InternalNode : : Insert () visszatérési értéke, melyet
a HeadNode megkap. í~.'y az új csomó po nt, melynek tárolt s7..ámértéke kisebb, mint
mint az akmális csomóponté, beilleszl5dik a listába. Ez most a 19.4 ábrának megfelel6 -
en néz ki.

II
láncolt lista
Kezdő csomópont Záró csomópont

E-- ~~
Bets6 C10l11Ó!lonl
BeIS6 csofTlÓ!lOnt
VOlue .. 1S
Data
myNexl

19,4 ábra
A MI/col/lis/a a második elem lx'Szlírtím uM"

Tegyük fel, hogy a ciklus harmadik meghívása kor a felhasználó B-at üt be . Ez nagyobb
3-nál, de kisebb lS-nél, így a két köztes csomópont közé kell kerülnie. Pontosan úgy
fog minden történni, mim az előzéS példában, azzal a különbséggel , hogya 3-mal való
összehasonlítás Ulán nem a kl sLarger, hanem a kl sSmal ler lesz a Compare ()
visszatérési énéke (azaz az akmális objektumbeli 3 kisebb, mint a friss objekmmbeli 8).

Ez az InternalNode : : I nse rt () tagfüggvény összehasonlító elágaL'lsai közül ez alka-


lommal a 116. sorban lev6 fut Ic. Ahelyett, hogy egy új csomópontot hoznánk lérre (és
szúrnánk be az aktuális elem elé), az Interna lNode továbbadja az új adatot annak
a csom6pontnak , ahová a myNext mutat j ennek Insert () tagfüggvénye hívódik meg.
Esetünkben annak a köztes csomópontnak abeillesztő metódusa kerül meghívásra,
amelynek adaltagja a l5-öt tartalmazza.
Újra összehasonlítódnak az értékek; most már létre kell hoznunk az új csomópontot.
Ennek .rákövet kező elem"-e a 15-öt tartalmazó köztes t:somópontra mulat, és saját cí-
me annak a csom6pontnak kerul átadásra (visszatérési értékként), amelynek adanagja
a 3-at tartalmazza, ahogy ez a 118. sorban látszik.

Mindennek hatására az új csomóponl a lista megfelel6 helyére illesztődik be.

Végs6 eredményké nt pedig egy nagyság szerint. rendezett lista van a kezünkben, füg-
getlenül an6l, hogy milyen sorrendben vittiik be az adatokat.

Mi a láncolt listák tanulsága?


Ahogy Dorothy mondja az "Óz, allagy varázslo'" vége felé: "Ha újm szívem vágyait
követem, nem megyek többé messzebb il hátsó udvaromnál". Ahogy .mindenütt j6, de
legjobb otthon", ugyanígy párja nincs az cljárásközpontú (jJrocedurtífis) programozás-
nak, amelyben egy vezérl6 metódus vizsgálja meg az adatokat és a hfv6 függvényeket.

Ehben az objektum-orientált megközelítésben minden objektumnak megvan a szűk, de


jól definiált felel5sségi köre. A láncolt lista főobjektuma csak a fejérl felel6s, ez viszont
azonnal továbbadja az új adatot annak a csom6ponlnak, .unire mutat, függetlenül att61,
hogy az éppen mi is.

A lista farka , ha adatot kap, azonna l létrehoz számára egy csorn6pontot maga el6tt, és
oda illeszti be. Ez csak ennyit tud: adatot kaptam - beszúrom mag'lm elé.

A köztes csom6pontok alig bonyolultabbak. Saját adattartalmukat hasonlítják össze


a kapott adattal, és ennek eredményét61 függ6en beillesztik vagy továbbadják.

Figyeljük meg, hogy magának a csom6pontnakfo,galma sincsarr61, hogy mikfnl7..ajlik


az összehasonl1tásj ez az objeklumok dolga. A köztes csom6pont csak meg tudja kérni
az objektumokat, hogy hasonlítsák össze magukat, és a három IchcL<;éges V{IIaSZ közül
vár t:!gyel. Az egyiknek :1 hatására beilleszti, a másik kett6 hatására a továbbadja
az adatOL, nem törődve azzal, hogy mindez hogyan fog végzódni.

Akkor hát kit: is a fe lel6sség? Egy jól megtervezctt objektum-orientált programban SGll-
kié. Minden objektum leszi a maga apr6 dolgát, és a gépezel si:'.épen mCíkÖdik.

A láncolt lista szépsége, hogy bármilyen jellegű adat lehel a Data osztályban. Esetünk-
ben ez egy egész szám volt, de ez lehet akár több beépítetl adattípus vagy bármi más
objeknun (akár láncolt lista is).

Egy olY'1Il projektben dolgoztam, melyben különböző fcjczetcket kellett létrehozni,


melyek szöveges bekezdéseket tartalmaztak. Adatbázis tárolta az adatokat, így bárme-
lyik fejezetet e l ő lehetett hívni. Belsőleg a dokumentum fejezetek láncolllislája voll; fe-
19. óra • láncolt li 365

jezetcím, fejezeLS.cim, még ez-az a fejezet ről, majd bekezdések lá ncolt listája kövecke-
zeu. Ez utóbbi minden egyes fejezctr61 lan.almazolt némi információt és magát a be-
kezdés szövegéL

A dinamikus memória használata lehec6vé teszi, hogy a láncolt listák minimális memó-
riát foglaljanak el akkor, amikor a listák kis méretűek, vagy rengeteg memóriát, ha
nagy méretűek. Igen fontos jellemz6jü k, hogya tárolt adatok mellliyiségével arányos
a méretük. Ezzel szemben a tömbök el6re lefoglalnak valamen nyi helyet, ami egyszer-
re paza rlás és korlátoz.-'ís.

Apropó Miért nem használunk mindig láncolt listát?


Joggal velódi k fel a kérdés, hogy ha a láncolt listák ilyen hatékonyak, akkor miért
nem mindig ilyeneket használunk? Azért, mert ennek a hatékonyságnak ára van.
A láncolt listák szekvenciális szerkezetűek, azaz csak az egyik végén lehet nekikez-
deni a keresésnek, és lehet, hogy egészen a lista farkáig végig kell rágnunk magun-
kat a láncon (hogy megtaláljuk a keresett elemet). Ez az elérési mód alkalmanként
lassú lehet.
Gondoljunk a telefonkönyvre. Milyen lassú lenne benne a keresés, ha mindig végig
kellene olvasnunk az e lej étől az összes számol, míg rábukkanunk a miénkre. A legtöb·
ben szeleteléses technikával dolgozunk, azaz valahol a könyv közepe láján kezdjük
a keresést, vagy ott, ahol a me gfe l e l ő kezdóbelút sejtjük; aztán a találattól függóen
valamelyik irányban elmozdulunk, és ezt addig ismételgetjük, amfg meg nem találtuk
a megfeleló nevet. Számítástudományi kifejezéssel élve ez a logaritmikus keresés.

Kérdések és válaszok
Kérdés: Miél1l1oz'/Iánk lé/re láncofllislát, Ita egy tömb is IIIeJifeld a célnak?

Válasz: A tömbök nek rügzítclt ml!relük van, ezzel szemben a láncolt listák mérete di-
namHcusan v{lltozhat futás közben.

Kérdes: MiérI IJl'II6/1í!)/ik el az adatohj ekllllll o/ a cso mópo lltló/.~

Válasz: Ha van egy jól működ6 láncoll listá nk a megfelel6 csomóponlokkal, akkor ez
a program újrahasznosíthat6 bármilyen adatobjektumok számára, melyeket listába sze-
retnénk rendezni.

Kérdés: Ha más objeklumokClt is/cl szere/nén k ve/mi a listába, a kJ.'O r akJ.'O I" líj lista tí-
pust es lij csomópollllípUSI kell defilliálni?

Fáiasz: Egyelőre igen. Szebb megoldást is láthatunk: majd a 23. ór.:í.ban, a sablonok lár·
&'Yalásakor.
366 1V. rész • Örö~6dés és 'öbbalakúság

Gyakorlatok
Ebben az órában egy meglehetősen összeten adatszerkezettel, a láncolt listával ismer-
kedhettünk meg. SZ<1njunk most egy kis időt néhány kérdés megválaszolás:kJ és vé-
gezzünk el néhány feladatot, míg a tanultak frissen élnek emlékezetünkben!

Kviz
1. A Iist:íknak melyik három típusát ismertük meg?
2. Mi a szerepe a fcj mutatójának?
;. Mi a szerepe a "következ6~ mutatónak?
4. Mi kell egy kétszeresen 15 ncolt listához?

Feladatok
l. Fuuassuk le a linkedlist . cpp progr.:lmol (19.1 Lista) nyomkövet6 módban, és
lépegesliünk végig néhány csomópont bcszúrás{tn. Figyeljük meg, ahogy ezek
a mel6dusok egymást hívják, és figyeljük meg a mutatók megfe l elő beállítását is.
2. M6dosítsuk a linkedlist . cpp programot 09.1 Lista) oly módon, hogya Data
osztály az egész helyett egy dupla pontosságú számot (doIIbie) tartalmazzon. Mit
kell álírni?
3. M6dosítsuk tovflbb II linkedlist . cpp programot 09.1 Lista) úgy, hogy a Data
osztály több beépítelllípust Lartalmazzon (ne csak eg(:szet vagy lebeg6ponto-
sat). Mit kell átírni? Ötlet: a megfelelő ..kisebb" és ~nagy()bb~ döntéshez szükség
lesz va1:lmiféle ku lcsmez6re.

Válaszok a kvfzkérdésekre
]. Egyszeresen lflncolt, kétszeresen láncolt listák és a fák.
2. Ahogy II neve is mulat ja, ez II lista fejének (vagy cSÚCSfiiKIk vagy kezdetének) <.:Í-
méL tflrol ja. Igen fontos tudnunk, hogy hol is kezd6dik a lista, hogy elérhessük
az eb'Yes elemeket!
3. Ahogya neve is mutatja, ez a lista következő e lemének <.:Ímél tárolja. Úgy is fel-
foghatjuk, mint a lista maradék részének a fejét. Ez a voltaképpeni láncszem
a láncolt listában!
4. A láncolt listának van fej- és farokmutatója, valamint az egyes csom6ponroknak
~e I 6z6~ és "következő" mutatóik. Ennél fogva bármelyik végen kezdhetjük II ke-
resést, haladhatunk a másik végpont felé.
VI. RÉSZ

Különlegességek
20. óra Különleges osztályok, függvények, mutatók
21. óra Az eldfeldolgozó
22. óra Objektum-orientált elemzés és tervezés
23. óra Sablonok
24. óra Kivételek, hibakezelés és néhány tanács
20. ÓRA

Különleges osztályok, függvénye k,


mutatók
Ebben az 6rában a következőkJőllesz sz6:
• Mik azok a statikus tagváltozók és statikus ragfüggvények
• Hogyan használjuk a statikus tagvá ltoz6kat és statikus tagfüggvényckcl
• Mik azok II barácfüggvények és barátosztályok
• Hogyan használhat juk II barátfüggvényeket speciális problémák megoldására
• Hogyan használjuk a tagfüggvényekre vonatkozó mutatókat

Statikus tagváltozók
Az eddigiekben úgy képzeltük el az objektumok tagváltoz6it, hogy azok egy adon ob-
jektumhoz tartoznak, és SCffilni közük az osztály többi példányához. Ha van például öt
macska objektumunk, akkor mindeb'Yiknek megvan a maga kora, súly,\ és egyéb ada-
tai. Egyiknek II kora nem befolyásolja a másikét.
I
370 VI. rész • KilIönl.gesség.k

Vannak helyzetek, amikor olyan információkal szeretnénk számon tartani , amely egy
osztály több példányától is függ. Például kíváncsiak lehetünk ami, hogy eddig össze-
sen hány macska született, vagy hogy hány van még életben.

A többi t:lgvához6t61 eltérően a statikus (static) tagváltozókat egy osztály minden pél-
dánya elérheti. Átmenetel képviselnek a globális adatok közön, amelyek a progmm
minden résl-ér61 elé rhetőek , valamint az adattagok kÖZÖlt, melyeket csak az egyes ob-
jektumok érhetnek cl.

Gondolhatunk úgy a statikus tagváltoz6kra , mint amelyek nem az objektum hoz tartoz-
nak, hanem az osztályhoz. A normál adattagok objektumonként, a statikus adattagok
oS7.1[llyonkénr léteznek. A 20, l Listában deklarál unk t!gy Cat osZtályt egy statikus adat-
taggal , II HOwManyCats-szel ( Hál1yMacskaVall). Ez a v5 1toz6 kövel i nyomo n II macsk;ík
lélszám5t; a macskák léLrehozásakor növeljük, l ebont~ sa kor pedig csökkent jük
;1 HowManyC<!Its é rtékél.

20.1 Lista - Statikus adattagok használata Istaticmember.cpp)


o: /1 20 .1 Lista Statikus adattagok
,,,
1 : hnclude <iostream>

] class Cat

"5 ,
,. public:
Catlint age = 1) : itsAgelage) (HowManyCats++;
7, virtual -Cat() ( HowManyCats - ; )
8, virtual int GetAge() ( rcturn itsAge;
9, virtual void SetAge(int age) ( itsAge age; )
10: static int HowManyCats;
11 :
12 : private:
13 : int itsAge ;
14 :
15 : };
16 :
17 : int Cat : : HowManyCats - O;
19 :
19 : Lnt main()
20 : (
21 : const int MaxCats = 5 ;
22 : Cat *CatHollse [MaxCats ] ;
/.3 : int i ;
24: for li = O; i <MaxCats; i ++)
25 : CatHouse [ i) " new Catli) ;
26 :
27 : for li = O; i<MaxCats; i ++)
28 :
29 : std : : cout « "There are ";
30 : std : : cou t « Cat: : HowManyCats ;
31 : std : : ecut « " cats left ! \n" ;
20. óra • 371

32 : std : :cout « "Deleting the one which is " .


3] : std: :cout« CatHouse[ij ->Ge tAge () ;
34 : std: :cout « " years old\n ";
35 : delete CatHouse[il ;
36 : CatHouse[il = O;
31 :
38 : return O:
39 :

There are 5 cals left!

There ara ,
Deleti.ng the one
cats
which ie O years old
left !

There are ,
Deleting the one
cats
which
left ! "l ycars old

There ara ,
Deleting tho one
catll
which
lef t !
2 years old
",
Deleting the ooe which ycars old
There arc 1 cats
Deleting th. ono
left ! "
which io 4 years old

A 3-15. sorb:1n egy egyszen1sített macska osztályt deklarál unk. A lD.sorban deklará·
lunk egy egész típusú statikus tagváltoz6t, a HowManyCats-C1.

A HowManyCats de klar.'iciója nem foglal le egy egész számnak vlll6 mcmóriahelyer. El-
lentétben a nem-statikus változókkal, semmiféle helyfoglalás nem törté nik egy Cat ob-
jektum példányosulásakor, me rt II HowManyCats tagváltozó nem része az objektumnak.
Emialt külön kell inicjalizálni a 17. sorban.

Gyakori hiba, hogy elfelejtjük definiálni az osztályok statikus tagváltoz6it. Figyeljünk


arra, hogy ez ne történjen meg! Ha netán mégis el6fordulna , a linkcr eZI egy hibaüze-
nettel jelzi, például az alábbi módon Cllorlandnál):
' Er ror : Unresolved external ' Cat : :HowManyCa ts' referenced
from C: \PROJECTS\STATICHEMBER.Oru"
Azaz : feloldatlan kalso ' Cat : : HowManyCats , hivatkozás a ... fá j lb61

Vagy minl a következ6 sor (másfajta linkelt használva):


undefinc~reference toCat : ,HowManyCats
Azaz: hiányzó hivatkozás :

Ezl nem keH meglenni az itsAge számára, mivel ez nem statikus tagválloz6, és mindig
létrejön, amikor egy macska objektum megszületik - in ez a 25. sorban val6sul meg.
372 1VI. resz • Különlegességek

A macska ko nstruktor..! inicializálja a kort, és eggyel megnöveli a statikus tagváltozót


a 6. sorban. A destniklor eggyel lecsökkenti ezt a 7. sorban. így mindig aktuális marad
a létrehozott, de még nem törölt macskák létszá ma a HOwManyCa ts-ben.

A 19-39. sorban a főprogram Öl macskát példányosít, és l>cmkja őket egy CatHouse


( MacskaHáz) tömbbe. Ennek sorá n ötször hív6<lik meg a konstruktor, így a HowMany-
Cats ötször növe1 6dik meg a kezdeti O é rték ről.

A program ezután egy ötös ciklusban nézi végig a tömb öL cle mét , és az aktuális macs-
ka mUlll16 tö rlése előtt rendre kiirja, hogy hány macska él még. A kimcnctb6llátszik,
hogya kezdeti 5-r61 (hiszen végül is 5 macskát hoztunk 161re) minde n törléssel eggyel
csökken" macskák szá ma .

Figyeljük meg, hogy a HowManyC~ts nyilvános válLOZ6, így közvetlenül a r6program


kezelheti. Nincs oku nk arra, hogy ilye n módon tegy(ik ki különr~le veszélyclme k ezt
a tagváltoz6l. Jobb volna priváuá lenni a többi t.1gváltoz6 val együll, és inkább egy nyil-
vános hozzáfér6 függvényt kellene megadni , hiszen úgyis c5.1 k macska példányo kon
keresztül szeretnénk hozzáférni /:!hhez az adathoz. I-Ia netán macsk:! nélkül is szeret-
nénk manipulálni eZl HZ adatot , k é l alte rnalivánk van: nyilvános cl6réslinek fll /:!ghagy-
ni, vagy irni hozzá cgy statikus lagfüggvényl.

Statikus tagfüggvények
A sm(iklls lagfüggvé nyek olyanok, mint a statikus tagváltoz6k: nem objekturnho z tar-
toznak, hanem egy osztályhoz. így anél kül is mcghívhat6ak, hogy lenne példányunk
az adott osztá lyból , ahogy az a 20.2 Listában is látszik.

20.2Usta - Statikus tagfüggvénvek használata (staticfunction.cpp)


0 , 1/ 20 . 2 Lista St.atikus tagfl1ggvények
l : U nclude <iost r e am >
7. :
3: class Ca t
4: (
S: public :
6: Ca t (int ag e '" l) :i t sAge ( age) (HowMünyCa ts ++ ;
7: vi rtual -eat ( ) ( HowMa nyCats-- ; J
8: virt u al int GetAge() { return i tsAge ; }
9: v i r t ual void SetAge( in t age) { it sAgc '" age ; )
10 , static int Ge t HowMany () { return HowMa nyCat s l l
11: priva t e :
12 , int i ts Agei
l]: stat ic int HowManyCats;
14 : };
15 :
16 : int eat : , HowMa nyCat s = Oi
17 :
20. óra· 373

18 : void TclepathicFunction();
19 ,
20 : i nt main()
21 :
22 : const int MaxCats '" 5 ;
23 : Cal *CatHouse[MuxCato) ;
2 4: int i;
25 : for (i '" O; l<MaxCato; l -H·)
26 : (
27 : Ca tHollse{i] = new Cat(i) ;
28: TelepathicFunction();
29 :
30 :
31 : for ( i = O; i<MaxC",t9 ; i • • )
32 : (
33 : deletc CatHouse[i l;
34 : TelepathicFunction();
35 :
36 : return O;
37 :
38 :
39 : void TelepathicFunction ()
40 : {
41: std : : cout « "There are « Cat : :GetHowMany ()
42 : « " cats nlive!\n" ;
43 :

Kimenet
There 1 cats alive
""
,,
'I'hero .ro 2 cats alive
There ar, cats alive
There aro cats alive
There ar, S cats alive
Thcre ar, 4 cats alivc
There ar, 3 cats alive
There are 2 cats alive
There .,.re
1 cats aU ve
There are O cats alive

E~

Ebben a prog'dmban (az elózőve l ellentétben) privál elérés\1nek deklaráltuk a How-
ManyCats statikus tagvállOz6t a Cat dckbrftci6jának a 13. soráb.m. A GetHowMany ()
nyilvános hozzáfénS függvényt viszont stmikusnak és nyilvánosnak adtuk meg a 10 .
.sorban.

Mivel a GetHowMany () nyilvános, bármelyik függvényhől elérhető, és mivel statikus-


nak deklarftlluk, nincs szükség létező macskára a meghívásához. Ezért rt 41. sorban ál-
16 'l'e lepathicFunction () el tudja érni a nyilvános és statikus hozzáfér6 függvényt,
374 1VI. rész • Különlegességek

bár magukhoz a macskákhoz nincs hozzáférése. Te rmészelesen az elé rhető macska


objektumokon keresztül is meg lehetne hívni a Get HowMany () függvényt a főprog mm­
ban, mint ahogy bármely más hozzáféró fü ggvényt is.

Apropó A this mulató


A statikus tagfüggvényeknek nincs thi s mutatójuk, tehát nem is lehet őket kons-
tansként deklarálni . Mivel a t his mutatón keresztül érik el a tagfüggvények is
az adattagokat, a statikus tagfü ggvények csakis statikus adattagokat tudnak elérni.

Egymást tartalmazó osztályok


Mint ahogy az már a korábbi péld5 kon is 1(uszott, az osztályok ada ttagjai lart<1lmazhal-
jii k más osztályok objektumpéldányait. A C+ + programozók úgy mondják, hogya kül-
56 osztály tartalmazza a bels6 osztályt. így például egy Employee ( Alkalmazott) osztály
tartalmazhat karakterlánc objektumokat (az alkalmazottak nevéhez) és egész szá mokat
is Ül fizetés hez) stb. A 20.3 ListMml egy J cc.~ upa szít o lt , mégis hasznos String OSZTályt
é pítünk feJ.

20.3 lista - A String osztály (string.hppl


o: /1 20 . 3 Li sta A St rinQ osztá l y
1 : linc1ude <iostream>
Z : linc1ude <string . h>
3 , class String
., : {
5: public ,
6, /1 Kons truktorok
7, String () ;
8, String{const char "const) ;
Stri ng (cons t St ri ng ... ) ;
"
10 : -S t ring() ;
11 :
lz : 1/ túlterhelt oper átorok
13 : c har & operatorf ] (int of f set) ;
14 : c har operator[ l (int o f fse t) c on st ;
15 : String operator.(const Str ing&) ;
16 : void operator.={const String&) ;
17 , Stri ng & operator; (const Str ing &) ;
IB :
19 , 1 / Elta l ános hoz zá f 6ro fuggvények
20 : i nt GetLen ()con st ( retu r n itsLen;
21 : const c har * GetStr ing () c onst { re tu rn itsStri ng ; }
22 : 1/ stat i c i nt Constr uctorCount ;
23 :
24: pri vate :
375

25 : String(in t) ; II privát kons t ruktor


26 : char * i t sStdng ;
27 : int it5Len ;
28 : );
29 :
30: II az a lapértelmezett konstruktor O bájtos karakterláncot hoz létre
31 : String " String()
32: (
33 : itsString = new char[l) ;
34 : i t sS t ring[O j = ' \0 ';
35 : itsLen =O ;
36: II std :: cout « • \tDefault string constructor\n· ;
37 : II ConstructorCount++ :
38: }
39 :
40: II privát (segéd) konstruktor, csak osztálymet6dusok
41 : II használják meghatározott méretu új karakter láncok
42 : II létrehozására. Nullal töltve.
43 : String : : SLr ing (int len)
44 : (
45: itsString = new char[len+l];
46: int i;
47: for Ci = O; i<_len; 1++]
48: itsString]i] = '\0' ;
49: itsLen=len :
50 : II std : : cout « "\tString(int) constructor\n" ;
51 : II ConstructorCount++:
52 : )
5) :
54 : String : : String (const char * const cString)
55: {
56 : itsLen = strlen(cString);
57 : itsString = new char[itsLen+1] ;
58 : int i;
59 : for ( i = O; i<icsLen ; i ++)
60 : itsString[i] = cString(i];
61: itsString[itsLen] .. '\O';
62 : II std : : cout « • \tString (char*) constructor\n";
6): II ConstructorCount++;
64 : }
65 :
66 : String : : St ring (const String & rhs)
67 : (
68: itsLen=rhs.GetLen() ;
69: itsStri ng '" new char[itsLen+l];
70: int i;
71: for ( i " O; i<itsLen ; i++)
72: itsString[i] = rhs{i) ;
7) : itsS tring[itsLen] = ' \0 ';
74 : I I st d : : cout « • \ t String (Stri ng&) con s tructor\ n · ;
75 : lj Constr uctorCount++ ;
76: )
77 :
376 1VI. rész • Kmönl.gességek

78 : String : o-String ()
79 : {
80 : delete [J itsString ;
8l: itsLen - O;
82 : II std : : cout « "\tString destructor\n";
83 : ,
84 :
85 : II Egyenloség operá t or ; felszabad ítja a lefoglalt memóriát ,
86 : II majd másolja a karakterláncot és a méretet
87 : String& String : : operator=(const String & rhsl
88 : {
89 : i f (this -- &rhsl
90 : return *this ;
91 : delete t J it 5S t ring ;
92 : itfiLcn-rha . GctLcn() ;
93 : itsString " new char[itsLen+l l;
9 4: int 1;
95 : for (i " O; i<i t sLcn ; i ++ )
96 : iLsStri ng [ i.) '" r h p, [ i] ;
97 : itsStrir'\g[itI:lLen] '" ' \0 ';
98 : rcturn *this ;
99 : II st.d :: couL« " \LString operaLor.=\n" ;
100 : )
101 :
102 : Ilnem konstans eltolás o perátor ,
103 : II karakterre vonatkozó hivatkozást ad vissza ,
104: I I {QY megváltoztathat6 !
105 : char & String : : operator[} (int offset)
106 : {
107 : i f (offset> itsLen)
108 , return itsString(itsLen-1) ;
109: else
110 : return itsStr1ng(offset] ;
III :
112 :
113 : II constant offset op erator for use
114 : Ilon const o b jects (see copy constructor ! )
115 : char String : : op e r atorl ) (int o f fse t ) const
116 : (
117 : i f (offset> itsLen)
118 : return itsString[itsLen-l l ;
119 : e l se
120 : return itsString[offGe t] ;
ln :
12 2 :
123 : II új karakterláncot hoz létre, á t adva az ak tuá l i s
124 : I I karakterláncot az dis- nek
125 : String String : : operat or + (const String& rhs)
126 : (
127 : int totalLen = itsLen + rhs . GetJ.en() ;
128 : int i,j;
129 : String temp(tota1Len) ;
130: for (i =. O; i<itsLen ; i ++ )
131 : templi] = itsStr ing[i ] ;
20. óra • mutatók

132 , for (j '" O; j<rhs.GetLcn(); j++, iH}


133 : templi] '" rhs [ j];
134 : temD[totalLen ]= ' \0 ' ;
135 : return temp;
136 ,
137 :
138 : II mcgváltoztatja az aktuális karatkerláncot, és nem ad viooza semmit
139 : void Str ing: :opcrator+"'(eonst St ring& r hs}
140 : (
141 , int rhsLen " rhs.GctLcn();
142 : int totalLen = itsLen + rhsLen ;
143: int L j ;
144 , String temp(tota1Len);
145 : for (i '" O; i<itsLen; i++)
146 : templi] - itsString[i];
147 : for (j '" O; j<rhs . GetLon() ; j++, i++}
146 , templi] " rhs li-itsLenl ;
149 : temp lto t a1Lan ] ='\0';
150 : *thie " temp ;
151 :
152 :
153: II int String ,: ConstructorCount O;

Kimenet

A progra rnnak [lines kimencle,

EI. 8

A 22.sorban deklaráljuk a ConstructorCount statikus tagválto z6t , és a 153. sorban ini-


cializáljuk. Ezt a változ6t nóveljük a karakterláne konstruktorban.

A 20.4 Listában leínrnk egy Employee ( Alko/mozou) osztályt , amely hárorn kamkter-
láne objektumot tartalmaz. Figyeljük meg, hogy néhány sor megjcgyzésl:>e van téve;
ezek a fejezet végi feladatokban kerülnek sz6ba.

Apropó Újrahasznosfthat6ság

A 20.3 lista kódja a string. hpp állományban van. Ha szükségünk van


a String osztályra, beemelhetjük a 20.3 listát egy hnclude paranccsal. A 20.4
lista elején például látható egy lIinclude • string . hpp· sor; ennek révén
használhat juk programunkban a String osztályt.
378 1VI. rész' Különlegességek

20.4 Lista - Az Employe8 osztály és a főprogram lemployeemain.cpp)


o: ~include 'string .hpp ·
1: II Az iostrcam-ct már beemelte a másik állomány
2 : using std : : ccut; /I Ez a fájl std: , cout-ot használ
3,
4,
S, class Employee
6, (
7, public :
8, Employee() ;
9, Emp!oyca (char * char * , char * long) ;
10, ~ Employee() :
ll: Emp loyee{con!:l t Employee&) l
12 : Employee & operat or= (const Employee &) ;
13 :
14 : const String & GetFirst Name(} const ( retu rn i t sFirs tName ;
15: COn!:lC St ring & GetLastName() const { reLurn itsLastNa mo; }
16: const St ring & Ge tAddress( ) const { return itsAddrass ; }
17 : long Get.Salary() const ( .etu rn ltsSalary; )
18:
19 : void SctFirstName(const String & fName)
20 , ( itsFirstName = fName ; l
21 : void SetLastNama(const String & INama)
22 : { itsLastName = IName; }
23 : void SetAddress(const String & address)
2'1 : ( itsAddress = address; )
25 : void SetSalllry(long salary) { itsSellary ol salary; }
26 : private:
27 : String itsF'irstName;
28: String itsLastName;
29 : String itsAddress ;
30 : long itsSalary ;
31: J,
32 :
33 : Employee : : Employae() :
34 : itsFirstName(") ,
35 : i tsLastName(" " ),
36 : i tsMdress ('"),
37 : itsSalary(O)
38 : {}
]9 :
40 : Employee : : Employee (char * f i rstName , char * lastName,
41: char * address, long salary) :
42 : itsFirstName(firs t Name),
4]: itsLastName(las tName),
44 : itsAddress{address),
45: itsSalary(salary)
46 : {I
47 :
48 : Employee ::Employee (const Employee & rhs) :
49 : itsFirstNama(rhs.GetFirstName(») ,
óra •

so : itsLastName(rhs .GetLastName(}) ,
51 : itsAddress (rhs .Get Address () ),
52 : it sSalary(rhs .GetSalary(»
53 : ()
54 :
55 : Employee : :-Emp!oyee() fl
56 :
57 : Ernployee & Employee: : operator: (const Ernp!oyce & rhs)
58 : {
59 : i f (this == &rhs)
60 : reLurrl *this;
61 :
62 : itsFirstName '" rhs.GetFirstName();
6]: itsLastName '" rhs . GetLastNamc (l ;
64: itsAddress e rhs .GetAddrcss();
65: itsSa!ary '" rhs . GetSalary() ;
66 :
67 : return *thi o ;
68 :
69:
70: int main()
71:
72: Employee Edie("Jane","Ooe","1461 Shore Parkway', 20000):
73: Edie.SetSalary(50000);
74: String LsstName('Levine') ;
7S : Ed.Le. Se t LastName (LastNamc) ;
76 : Edie . SetFlrstName ( 'Edythe" ) ;
77 :
78 : "Name: ' ;
79: Edie .GetFirstName() .GetString();
80: • • « Edie.GetLastName() .GctString():
81: •. \nAddress: ':
82 : Edie . GetAddreS9 () . GetString () ;
83 : • . \ n Salary : " :
84 : cout « Edi e . Ge tS alary() ;
85 : return O;
86 :

Name ; Edythe Levine


Address : 1461 Shore Parkway
Salary : 50000

A 20A Lista szemlélteli az Employee osztályt, melyben három karakterlánc objektum


va n: itsF i rstName, itsLastName és itsAddress.
I
380 VI. rész • Különlegességek

A 72. sorban létrejön egy Employee példány, és négy adattal inkializálódik. A 73. SOf-
ban az Employee hozzáfér6 függvénye, a SetSalary () hív6dik meg, az 50000-es
számErtékkeL Egy igazi progmmban ez egy futási időben meghatározott dinantikus ér-
ték vagy egy konstans lenne.

A 74. sorban egy C++ karakterlánc konstanssal inicializál6dik egy String objektum,
ami a SetL"stName() paramétere lesz a 75. sorban.

A 76 , sorban az Employee tagfüggvénye, a SetFirstName () fUlle, szintén egy karak-


terhínc konstanssaL Ha erősen figyelt az olvasó, észrevehette, hogy az Employee-nek
nincs olyan SetFirstName () t:1gfüggvénye, amely karakterláncot fogadna el par.uné-
terkénlj a SetFirstName () konstans karakterJ{mcra való hivatkozo:íst vár.

A fordítóprogram megoldja czt, mert a 20.3 Lista 8. sora alapján már tudja, hogy ho-
gyan csináljon egy konstans karakterláncból String-el.

A tartalmazott osztály tagjainak elérése


Az Employee objekmilloknak nin<_"S speciális hozzMérése a String tagv:íltoz6khoz.
Ha al Edie alkalmazott o bjektum megpr6biilná elérni saját itsFirstName tl.lgváltoz6-
j,1nak itsLen tagváltoz6ját, a z fordítási hibát generálna. Ez igazából nem jelem túl
nagy terhet. A hozzátérő fügsvények megfelel6 kezelőfelületet nyújtanak a String
osztályhoz. Az Employee OSztályban nem kell többel foglal kozmmk a megval6sítás
részleteivel, mint amennyit mondjuk az itsSalary egész változó konkrét infonnáció-
tárolási módja miatt kellene.

A tartalmazott osztályelérésének korlátozása


Figyeljük meg, hogy a String osztály biZtosítja a •+" operátort (operator+).
Az Ernployee teIVez6je azzal akadályozta meg, hogy az operator+ meghívható legyen
az Employee objekmmokra, hogy úgy deklará lt minden karakterlánc hozzáfér6 függ-
vényt, (mint például a Get FirstName () -et is), hogy konstans referenciát adjon vi.ssza.
Mive! az operaton nem (is lehet) konstans függvény, hiszen meg kell változtatnia
a hívott objektumot, a következő .sor hibát adna:

String buffer ~ Edic.GetFirstName() + Edie.GetLastName();

A GetFirstName () cgy konstans $tring-et ad vissza, amire nem lehet az operator ...
műveletet meghívni.

Ez( a GetFirstName () túlterhelésével tudjuk kijavítani.


Ne legyen az új függvény konSlans:
const String & GetFirstName{) const { return itsFirstName;
String & GctFirstName{) { return itsFirstName; }

Figyeljük meg, hogy sem II visszatérési érték, sem maga a ragfüggvény nem konSlans.
A visszatérési érték megváltozlatása még nem elegendő a túlterheléshez; változlarni
kell a függvény konstans vollán is. Azáltal, hogy megadunk egy konstans és egy nem
konslans vállozatot is, a fordítóprogram prnbálja majd hívni a kom;tans vállozatot, ahol
csak lehet (például amikor :IZ ügyff:lprogmm a GetFirstName-et prnbálja hívni), és ha
nincs más lehetőség, meghívja a nem konstans verziói (például az operator+ esetén).

A tartalmazás költsége
Fontos tudn i, hogy al Employee oszt,~ly felhasználója nünden egyes k3rakterlár1C lf:lre-
ho:disána k az árút megfizeti, amikor létrejönnek a karakterláncok, vagy ha másolat ké-
szü l egy Employee péklányr61.

Érték szerinti és hivatkozás szerinti másolás


Amikor érték szerint adunk át egy Employee objektumot, minden egyes tartalmaZOlt
kardkterlánc is lemásol6dik ; eközben pedig másoló konstnlktorokat hívunk meg.
Ez igen költséges, mcm6ria ~s id6 tekintetében.

Ha cím szerint adjuk át az Employee objektumokat (mulatót vllgy hivmkoz.1st használ-


va), mindezt elkerülhetjük. Emiatt keményen dolgoznak a C++ programozók , hogy né-
hány bájtnál nagyobb objekuHnol ne kelljen érték szerint átadni.

Baráti viszonyban levő osztályok


Időnként egy(ill hozunk létre osztályokat, egy csoportba tartozó módon. Ezeknek
az összeterelL oszt.\lyoknak szükségük lehet egymás privát adatlagjaira, ám nem szeret·
nénk ezeket az információkat nyilvánossá tenni.

Ha egy osztály privát adatait egy másik osztá ly rendelkezésére szeretnénk bocsátani,
akkor a fr iend ku1csszóval kell ezt a . barátkozó" osztá lyt deklarálni. Ez kiterjeszti
a másik osztály kezel6felületét, hogy lássa a barátosztályt is.

Megjegyzendő, hogya barátság nem tranzitív. Anól még, hogy Jóska és PiSla a barára-
im, ők egymásnak nem barátai. A barátság nem is öröklődik. Az, hogy egy barálommal
megbeszélem a lilkaimal, nem jelemi azt, hogy az ő gyermekeinek is meg szeretném
mondani.
382 rész •

A barátság még csak nem is szinulletrikus: Az EgyikOsztály-t a MásikOsztály barát-


jának deklarálva még nem lesz a MásikOsztály az EgyikOsztály-nak is barátja. At-
tól , hogy te el szeretnéd mondani nekem a titkaidat, nem biztos, hogy én is elmondom
neked a sajátjaimal.

A barátosztályokat igen nagy körültekintéssel szabad csak használn i. Ha két osztály ki-
bogozhatatlanul összefonódoll, és gyakran kel! egymás adatait manipulálniuk, akkor
megfontolandó a baráL'iág kimondása. De legyünk takarékosak: sokszor ugyanilyen
egyszeru nyilvános hozzáfé rő függvényeket használni. Ez lehetővé teszi, hogy az egyik
osztályt anélkül váhoztassuk meg, hogy a másik:LL újr.t kellene fordítani.

Apropó Egységbezárás
Gyakran hallani kezd ő C+ + programozóktól, hogy arról panaszkodnak, hogya ba-
rátság aláaknáua az objektum-orientált szemléletben oly fontos egységbezárást.
Ez enyhén szólva téves megközelítés. A baráttá deklarálás egyszerüen az osztály
keze l ő fel ül eté hez csatolja a barátosztályt; ez nem ássa jobban alá az egységbezá-
rást, mint mondjuk a nyilvános származtatás.

Baráti viszony függvények között


Előfordu l hlIt, hogy II barátságot nem egy teljes osztálynak szeretnénk megadni, ha nem
csak az osztály egy-kéL tagfüggvényének. Ez meg is teheL6 oly módon, hogy nem
az osztályt deklaráljunk barátnak, hanem a szükséges tagfüggvény(eke)r. Sót, igazából
ennek nem is kell osztályhoz tartozónak lennie; bármilyen függvényt kinevezhetünk
bacitfüggvénynck.

Függvénymutatók
Ahogya tömbök nevei olyan konstans mULatók, melyek a tömb első demére mutat-
nak, a fü ggvények nevei is konstans muLatók magár:l a függvény re. Lehet deklará lni ki-
feje%etlen függvényre vonatkozó mutatót, és ennek segítségéve! meg is hívható lL függ-
vény. Ez nagyon hasznos leheL például olyan program írásához, amely a felhasználó :'iI-
tal adott bemenet alapján hív meg kül önböző függvé nyeket.

A függvénymutatók megénésének nehézsége az, hogy tulajdonképpen milyen típusú


objekrumr.t is mutatunk. Ahogy egy egész számra vonatkozó mutató egy egész típusú
változóra mutat, ugyanígy a függvé nymutatónak egy adott visszatérési típusú , adott pa-
r.tméler-szignatúrájú fűggvényre kell mutatnia.
20. óra • 383

Nézzük a következ6 deklarációt:

long j. func;Pt r) (int) ;

Ebben a funcPtr-l úgy határozzuk meg, hogy ez egy mutató lesz (hiszen ott a • jel
a név el6tt), amely egy egész szá mol elfogad6 és egy h OSSl Ú egészt visszaad6 függ-
vényre vonatkozik. A "· funcP t r" köré zár6jelt kell tennünk , mert egyébként
az (i nt ) " erősebb hatású" [e nne, azaz korCtbban hajt6dm\ végre, mint
a mutat6fc1oldási operátor e). Az első zár6jclpár nélkül ez egy egész számot elfogadó
függvé nyt jelentene, amely egy hosszú egész számra vonatkozó mulalól ad vissza. (Ne
feledjük , hogyaszóközöknek ill nincs jelen tősége ,)

Vi:r.sgáljuk meg az alábbi két deklarációt:


long· Func t ion (int);
long ( * func Ptrl (int) ;

Az els6ben a Punction () egy fi'lggvény, mely egy egész számOLvá r, és egy hosszú
egész sz. \ mra vonatkozó lllutatót ad vissza. A másodikban a funcPtr egy illU/a/ó,
amely egy filggvényre vonatkozik, amely egy egész számot vár (:s egy hosszú egészct
3d vissza .

A függvénymutatók deklarációjában mindig meg kcll adni 3 visszatérési típustj a máso-


dik zárójelben kell felsorolni az esetleges paraméterek típuslistiíj:ít. A 20.5 Lista szem-
lélteti a f'Üggvénymutatók deklarációját és haszniílatát.

20.5listB - Függvénymutatók használata (ptrtofunction.cpp)


o: / / 20.5 Li s t a rOggvénymutat6k használata
1 : Itinclude <iostream>
2:
,.
3, void
void
Squarc (1nt&,int&) ;
Cube (int& , int&) ;
5, void Swap (int&, int & ) ;
6, void GetVals(int&, int&);
7, void PrintVals (i nt, int) ;
8,
9, i n t main ()
10 :
11 : void (* pFuncl (int &, int &l ;
12 : boo1 fQuit ,. fals e;
13 :
14 : int va1One=1 , valTwo~2 ;
15 : int choice ;
16 : whil e (fQuit == fals e)
17 : {
18 : s t d :: cout « " (O)Quit (l)Change Va lues
19 : « " (2l Square (3)CUbc (4)Swap : ";
20 : std: :cin » choice ;
384 1VI. rész • Kmönlegességek
21 , s .... itch (choice)
22 , (
23 , case 1 ,
2 <'\: pFunc = GetVals;
25 : break ;
26 , case 2 ,
27 : p Func = Squa re ;
28 , brea k;
29 : case 3 :
30 : pE"unc = Cube ;
31 : break;
32 : case 4 :
33 , pF\mc = Swap;
34 : break;
35 , default ,
36 : fQuit " true ;
37 : break ;
38 :
39 ,
40 : i f (fQuit)
41: break;
42 :
43 : PrintVals(valOne, valTwo);
44, pFunc(va10ne, valTwo);
45 : PrintVals(valOne, valTwo);
46 ,
47, return O;
48 :
49 ,
50 : void PrintVals(int x, i nt y)
51 , {
52 : std : : cout « ·x: • « x « " y: • « y « std : , endl ;
53 ,
54 :
55: void Square (int & rX, int & rY)
56 , (
57 , rX -= rX;
~8 , rY -- rY;
59 ,
60 ,
61 : void Cube (int & rX , int & rY)
62 , {
63 , int tmp;
64 :
65 : tmp "' rX;
66 : rX ' = rX ;
67 : rX = rX • tmp;
68 ,
69 : tmp = rY;
70 , rY .= rY ;
71 , rY = rY - tmp ;
72 ,
73 :
20. óra • mutatók

74 : void swap(inL & rX, i nt & rY)


75 : (
76: int temp ;
77 : temp" rX;
78: rX = rY;
79 : rY = temp;
80:
81:
82: void GctVals (int & rValOne, int & r ValTwo)
83 :
84: std : : cout « "New value for ValOne : ' ;
85: std : : cin » rValOne;
86 : std :: cout « "New value for ValTwo : .;
87 : std: :cin » rValTwo;
88 :

Kimenet

(OlQuit (llChange Values (2)SQuare (3)Cubc (4)Swap: l


x: l y:2
New valuc for ValOne: 2
New value for Val1'wo : 3
x: 2 y : 3
(O)Quit (l)Change Values (2)Square (3)Cubc (4)Swup : 3
x : 2 y:3
x: 8 y : 27
(OlQuit (l)Change Values (2)Square (J)Cube (4)Swap : 2
x:8y : 27
x:64 y : 729
(O)Quit (l)Change Values (2)Square (3)Cubc (4)Swap : 4
x : 64 y , 729
x , 729 y , 64
(O)Quit (l)Change Values (2)Square (3)Cubc (4)Swap : O

Bends

A 3-6. sorban négy függvényt adunk meg, azonos paraméter-szignatúrával (két hivat-
kozás egészekre) és visszatérési típussal (void).

A ll . sorban deklaráljuk a pFunc mutatót, amely egy olyan függvényre vonatkozik,


ami két egész hivatko7..ást vár és ~semm i t " (void) ad vissza. Az e\6ző1eg megadott
függvényck bármelyikére vonatkozhat ez a pFunc mutató. A fe lhasználónak ismételten
felkínflljuk, hogy válasszon a függvények közül, és ennek megfelelően lesz a pFunc
beá llítva. A 43-45. sorban a két egész szám pilla natnyi értékét kiíratjuk, majd az aktuáli-
sa n beálliton függvény lefultatása után újra megjelennek a sZ<Í mértékek.
386 1VI. rész • Különleges,égek

Rövidftett frásmód
A függvénymUlatót nem kell feloldani , bár erre megvan a lehet6ség. így, ha a pFunc
egy olyan függvénymutató, amely egy egész számot vár és egy hosszú egészet ad
ViSS7..a, és ehhez hozzá is rcndeltünk egy megfelelő füSb'Vényt, akkor ez kétféleképp is
meghívható:

pFunc(x) ;

vagy

(* pFu n cl (x l ;

A két változat egyenértékiI. Az els6 nem más, mim a második róvidített írásmódja.

Függvénymutatók tömbje
Ahogy például egészekb61 összeállíthamnk egy tömböt, ugyanúgy deklarálhatunk
(adoll viSSZ;lté rési típusú és paraméter·szignat(Hiijú) függ:vé nymutat6kból is . (L;\5d
a 20.6 Lislát)

o: II 20 . 6 Lista Filggvénymutat6k tOmbje


1 : 'include <iostream>
2.
3: void Square (i nt& , int&) ;
<l: void eube (int&: , int &) ;
5: void Swap (int&: , int &Ol ;
6: void GetVala{i n t& , int& ) :
7: void PrintVals (int , int) ;
8.
9: i. nt ma.in()
10 :
11 : i n t va l One~l , valTwo~2:
12 : i nt c ho ice , i :
13 : COn!l t i n t MaxArray " 5;
1 4: v o id (·pFuncArray [MaxArray]) ( i nt&, int& ) :
15 :
16 : f or (i=O ; i <MaxArray; iH-)
17 :
18 : s td: : cout « • (l ) Change Val ue s "
19 : « " ( 2 }Square {3 }Cu be (4)Swap: ";
20 : std: : cin » choice;
21 : s wit c h (choice )
22 :
0 mutat6k 387
2006ra' 1 1

23 : case 1 :
24 : pFuncArray[il GetVal s:
25 : break :
26 : case 2 :
27 : pFuncArray(i) ~ Square ;
28 : break ;
29 : case 3 ,
30 : pFuncArray [i I Cube ;
]1 : break ;
32 : case 4 :
33 : pFuncArray[i] = Swap ;
34 : break;
35 : default :
36 : pFuncArray ( i 1 O,
37 :
38 :
]9 :
40 : fo r ( i .. O;i <MuxArrIlY ; i+ 't )
41 : (
42 : pFu n cArray [i J (valOnc , valTwo) ;
43 : PrintVals(valOne , valTwo) ;
44 :
45 : return O;
46 :
47 :
48 : void PrintVals(int x, int y)
49 :
50 : std : : cout « 'x: • « x « • y: • « y « std : :endl;
51 :
52 :
SJ : void Square (int & rX, int tit r'il
54 : (
55: rX .... rXi
56 : r'i ' ", rV i
57 :
56 :
59 : void Cube (int tit rX , int tit rYl
60 :
61 : int tmp :
62 :
63 : tmp = rX i
64 : r X * ", rX i
65 : rX = rX .. t mp ;
66 :
6 7, tmp = r'i i
68 : r.Y * - rV i
69 : r'i = rY • tmp ;
70 :
71 :
72 : void Swsp(int tit rX , int tit r'il
73 : {
74 : int temp;
75 , temp '" rX;
388 1VI. rész • Különlegességek

76 : rX rY;
77 : rY = temp ;
78 :
79 :
80 : void GetVals (int & r ValOne, int & r Va l 'J'wo)
81 :
82 : std :: cout « "Ne w value for ValOne : " ;
83 : std : : cin » rVa l One ;
84 : std :: cout « "New va!ue for ValTWo : " ;
85 : std : : cin » rValTwo ;
86 :

Flgyeleml Legyünk 6vatosak ezzel a példaprogrammal!


Ahogy a könyv elején említettük, nem írtuk példaprogramjainkat golyóálló biztonsá-
gúra; nem követtük a Hdefenzív programozás" elveit. Ha az 1, 2, 3 vagy 4 helyett
más számot Irunk be a fenti program bemeneteként, hibát kapunk. A pontos hiba·
üzenet operációs rendszertől, fordftóprogramt61 és a nyomkövetötöl is függ; a hiba
oka az, hogy definiálatlan függvényt próbáltunk meghívni (lásd a 36. sort). Bár nem
lenne nehéz úgy átfrni a programot, hogy ez ne fordulhasson elő, ezzel sokkal
hosszabb és kevésbé szemléletes lenne.

menet

(l)Change Values (2)S quare (J}Cube (4) Swap : l


(l )Change Values (2)Sl:;Iuare (J)Cubc (4)Swa.p : 2
(1 )Change Values (2)Square (J)Cube (4 )Swap : 3
(l)Change Va l ucs (2)SQuare {J)Cube (4}Swap : 4
(l)Change Values (2)Square (J)Cube (4)Swup : 2
New Value for ValOnc : 2
New Value for ValTwo : 3
X: 2 y : J
x: 4 y : 9
x : 64 y : 729
x : 729 y : 64
x : 531441 y : 40 96

Elemzés

A 16-38. sorban lehetőséget ad a program a felhasználó számára, hogy válasszon


a meghívható függvények közül. Egy tömb elemeit rendre beállítjuk a megcélzott függ-
vény eimére. A 40-44, sorban egymás után meghívjuk ezeket a függvényeket (a 42. sor-
ban zajlik le a tömbelemnek m egfelelő konkrét függvényhívás). Minden hívás utá n ki-
íródnak az eredmények.
20. óra • 389

Függvénymutatók átadása más függvényeknek


A függvénymmatók (vagy akár az ezekb61 képzetttömOök) átadhat6ak más függvé·
nye knek, amelyek felhasznál hatják ezeket, és alkalmas pillanatban meghívhatják
a szükséges függvényt a megfele l5 mUlató használatával.

A 20.6 ü stát például lö kéletesítherjük oly módon, hogy a kiválasztott függv(:nyl egy
másik fí.iggvénynek adjuk át (a fap rogramon kívül), és ez fogja kiírni az értékeket,
meghívnia soron következ6 függvényt , majd újm megjeleníti az értékeket. A 20.7 Lista
eZl a változatOT mutatja be.

20.7 Lista - függvénymutatók átadása egy másik függvénynek paraméterként


lpassingptrfunction.cpp)
O, 1/ 20 . 7 Lis t a Függ v é nymutat6 k á t adása paraméterként
1 , h nc1ude <iol:itream>
2 : usin; namOApacc s t d ; / I e z a z á llomány s td:, objektultloku t használ
3,
4 , voi d Square (int& , int&) ;
5: void CUM (i n t& , int&) ;
6 : void Swap (int&, int &1 ;
7: void GetVals(int& , int&) ;
8: void I'rintVals(void ( * )(1nt&, int&) , int& , inl.&) ;
9,
10 : int !MinO
ll : {
12 : int val0ne=1, valTwo=2 ;
13 : int choice;
14 : boo1 fQuit = false ;
15 :
16 : void ("pFunc:) (int& , int&) ;
17 :
18 : whi1e (fQuit ~= false)
19 : {
20 : cout « . (O}Qui t (l)Chang e Val u es •
21 : « " (2)SQuare (3)Cube (4) Swap : ' ;
22 : cin » choice ;
23 : s wi tc h (c ho i ce )
2 4:
25 : case 1:
26 : pFunc '" Gc t Vals;
27 : breu k;
28 : case 2 :
29 , pFunc = Square;
30 : b reak;
31 , case 3 ,
32 : pFunc - Cube ;
33 , break ;
3 4: case 4 :
35 , pFunc = Swap ;
I
390 VI. rész • Különlegességek

36 : break ;
37 : de fault :
38 : EQuit = true ;
39 : break;
40:
41: i f (EQuit -== true)
42: break;
43 : PrintVals ( pF'unc, valOne, valTwo) ;
44 :
45 ;
46: re t urn O;
47 :
48 :
49: void PrintYals( void (*pFunc) (int&, int&),int& x, int& y)
50 :
51 : cout « 'X : « X « « y « endl;
52 : pFunc(x ,y ) ;
53 : cout « 'x : « x « « y « endI ;
54 :
55 :
56 : void Square (int & rX . int & rY)
57: (
58: rX *..,. rXI
59 : rY *= r'l;
60 :
61 :
62 : void eu be (int & rX , int & rY)
63 : (
64: int tmpl
65 :
66: tmp = rX;
67 : rX *= rX;
68 : rX = rX * tmp;
69 :
70 : tmp = rY ;
71 : rY * : rY;
72 : rY = rY * tmp;
73 :
74 :
75 : void Swap(int & rX, int & rY)
76 :
77 : i n t temp ;
78 : ternp = rX;
79 : rX rY;
80: rY = temp;
81 :
82 :
83 : void GetYals (int & rVal0ne, int & rValTwo)
84: (
85 : co ut « 'New va!ue Eo r ValOno : ' ;
86 : cin » rValOne ;
87 : cout « 'New vaIue fo r ValTwo : ' .
88: cin » rYalTwo;
89 :
20. óra • 391

menet
(O)Quit (l)Change Values (2)Square (3)Cube (4)Swap: l
x: 1 y:2
New value for ValOne : 2
New valuc f or Val '!'wo: 3
x: 2 y : 3
(O)Quit (l)Change Values (2)Square (3jCube (4)Swap : 3
x : 2 y:3
x: 8 y: 27
(O)Quit (l)Change Values (2)Square (3)Cubc (4jSwap: 2
x: 8 y: 27
x:64 y:729
(O)Quit (l)Change Values (2)Square (3)Cube (4)Swap: 4
x:64 y:729
x: 729 y : 64
(O)Quit (l)Changc Values (2)Square (3)Cube (4)SwQP: O

Bemzé.

A 16. sorban deklarnljuk a pFunc függvénymut3tót, ami két egész hivatkozást vár és
üres a visszatérési értéke. A 8. sorban deklaráljuk a PrintVals () függvényt, amely há-
rom p;mun{:lCI1 vár. Az első egy függvénymul<ltó (ami kél egész hiv~lIkoz:hl vár és üres
a visszatérési értéke), a m:lsodik és harmadik pedig egy-egy egészre ul:1 16 hivatkozás.
A felhaszn;'\16nak újra felajánljuk a függvényvál aszrás lehetőségét.

KerÍlsenek egy C++ programoz6t, és kérdezzék mcg t6le, mit jelent az :11ábbi dekhlmci6:

void PrintVals(void (*) (int& . int&l,int&, int');

I1yesft!le dekbráci6t nyilván nem túl súron használ az ember, és val6színtíleg a kedves
olvasó is egy kt!zikönyvból fogja kikeresni, ha nelán ilyesmire szüksége lesz. Lehel
azonban olyan ritka helyzet, amikor épp egy ilyen dcklarádóval lehet megoldani egy
programozási feladatol. A fenti kérdésre a következ6 a válasz: ez egy olyan függvény
deklarációja, amelynek üres (void) a visszatérési értéke, paraméterként pedig egy
függvénymutatót vár (melynek két p:H"Jmétere egy-egy egész hivatkozás), illetve még
két egész hivatkozást. Ezt olvashat6bban fogalmazhat juk meg a typedef segítségéve!.

A typedef használata függvénymutatókkal


A void (*) (int&, int') konstrukció használata enyhén szólva kényelmetlen. Egy-
szenísítésként használhat juk a typedef ku\csszót. Deklaráljunk egy VPF típust olyan
Függvl:nymutatóként, amely két egész hivatkozást vár és üres a visszatérési értéke.
A 20.8 Listában újrafogahnazzuk a 20.7 Lista elejét, és a typedef segítségéve! két lé-
pésben deklaráljuk a Prin t Vals () függvényt .
392 1VI. rész· Különlegességek

20.8 lista - A ftIggrinymutat61c OIVaShat6bbá _ (lIIiogtypedef.cppl


o: II 20 . 8 List a typede f használ ata
1 : 'lnclude <lostream>
2 : uslng namespace std; II ez az á llomány std :: objektumokat has zná l
3.
4 : voId Square (int& , lnt&) ;
5 : voidCubc (in t& , int ,\,) ;
6 : void Swap (Inl& , inl &) ;
7 : void GetValsCint& , i nt&) ;
8 : typedef void ( *VPF) (int'\' , int&)
9 : void PrintVals(VPF, int& , int &) ;
10 :
ll : i nt mal n ()
12 :
13 : int v n I One=l , v aI Two=2 ;
14 : int choice ;
15 : boo l fQu i t ~ false ;
16 :
17 : VPF pf'un c ;
18 ;
19 : while (fQuit ~= false)
20 ; {
21 : cout « . (O)Quit (l)Change Val ues'
22 : « "{2)Square (3)Cubc {4)Swap : ";
23 ; cin » choice ;
24 : s witch {choicel
25 : (
26 : case l :
27 : pFunc ~ GctVals ;
28 : break;
::19 , case 2 ,
30 : pFunc = Square ;
31 , break ;
32 : case 3 :
33 : pFunc " Cube ;
34: break ;
35 : case 4 :
36 : p Func - Swa p;
37 : b rea k;
3B : d efau l t :
39 : f Qu i t - t ru e ;
40 : break;
41 : )
42 : i f (fQuit == tru e)
43 : break;
44 : PdntVals ( pFunc , valOne, va l Two) ;
45 : )
46 ; return O;
47 :
48 :
49 : voId PrintVa ls{ VPF pFunc,lnt& x, l n t& y)
50 :
20. óra • mutatók

51 : cout « 'x: « x « y. « y « e nd l ;
52 : p F'unc( x, y) ;
53 : cout « 'x : « x « y. « y « cnd1 ;
51\ :
55 :
56 : void Square (int & rX. int & rY)
57 :
,
58 : rX - rX ;
59 : rY '- rY ;
60 :
61 :
62 : void Cube (int &: rX . int &: rY)
63 :
64 : int t mp ;
65 :
66 : tmp ::: r X;
67 : rX .. = rX ;
68 : rX • r X • tmp ;
69 ,
70 : t mp = rY ;
71 : rY * ,. rY ;
72 : rY = rY • tmp ;
73 : }
74 :
75 : void Swap(int &: rX, int &: rY)
76: (
77 : int temp ;
78 : temD -' rX;
79 : rX • rY ;
80 : rY = temD;
81 : )
82 :
83 : void GetVAls (int ~ rVolOne, int &: rVal'I\.Po)
84 : (
85 : cout « "New vAluc for ValOne : "
86 : cin » rVAIOne ;
87 : cout « 'New vAlue for ValTwo : ' ;
88 : cjn » rValTwo ;
89 , )

Kimenet

(O)Qu i t (l)C ha nge Values (2)Squa r c (3)Cube ( 4 )Swap : l


x : 1 y :2
New value for ValOne : 2
New value for ValTwo : 3
x: 2 y : 3
(O)Quit (l)Change Values (2)Square (3}Cube (4)Swap : 3
x, 2 y : 3
x : 8 y : :n
394 1VI. ,ész' KOIönl.gesség.k

(O)Quit (l)Change Values (2)Square {3)Cube (4 )Swap: 2


x; 8 y: 27
x:64 y :72 9
(O)Quit (l)Change Values (2)Square (3)Cube (4)Swap : 4
x:64 y : 729
x:729 y,64
(O)Quit (l)Change Values (2)Square (3)Cube (41Swap : O


A 8. sorhan a typedef segíL'iégével deklarál juk a VPI" függvénytípusl, amely kf:t pam-
métert vár (egy-egy egészre vonatkozó hivatkozást), és (ires a visszatérési értéke.

A 9. sorban dckbráljuk a PrintVals () függvé nyt, amely három pamrnétcrt vár: a:l el-
56 VPF tíPlISÚ, a többi egy-egy egészre vonatkozó hivatkozás. 1\ 17. sorba n már a VPI"
típussal deklarálharjuk a pFune függvényt.

A VPI" definíci6jával sokkal világosabbá válik e nnek a típusnak a használata, példáu l


pFune és a PrintVals () deklarációja kor. A typedef igazából csak egy szinonimfu
:L
boz létre; a 20.7 és 20.8 Lista csak az olvashatóságb,Ln tér el egymástól.

Tagfüggvényekre vonatkozó mutatók


Eddig a pillanatig csak o lyan függvé nymutatókat használtunk, amelyek osztályon kívü-
li , általánosan használható függvényekre vonatkoztak. Természetesen tagfüggvények-
hez is gyárthatunk mutatókat.

Tagfüggvénymulatót ugyanolyan szintaxissal lehet létrehozni, mint amit eddig is h:lsz-


nálrunk normál függvénymuratók esetében, csak a • jel el6tt meg kell adni az osztá ly
nevét és a hatókör operáton (két kett6sponlot) is. l-la például a pFunc a Shape oszlály-
beli tagfüggvényre mutat, amelynek üres (void) a viss~atérési é rtéke, paraméterként
pedig két egészet vár, akkor az alábbi módon lehet deklarálni:

void (Shape: :*pFunc) (int, int);

A tagfliggv6nyckrc vonatkozó mutatókat ugyanú!,'Y lehet haszn:'ilni, mint az egyéb


függvénymutatókat, kivéve azt, hogy csak a meghívandó osztályból való objektumok-
kal képesek dolgozni. A 20.9 Lista hemutatja a tagfüggvényekre vonmkozó mutatók
használatát.
Figyelemi A fiiggvény neve vagy címe?
Néhány fordítóprogram nem teszi lehetövé, hogya függvény nevét használjuk me-
móriacíme helyett. Ha a fordítóprogram hibát vagy figyelmeztetést ad az alábbi
programra, próbálja az alábbi módon megváltoztatni a 71 -76. sorokat.
71 : case
72 :
73 :
"
pFunc o & Marrunal : : Speak ;
break ;
74 : default :
75 : pFunc o & Manunal: :Move;
76 : break:

A következő programokban is ilyen módon korrigáljuk ezt a fajta hibát (vagyis egy
jól eltalált & jellel a függvény neve előtt).

Borland fordítóval nincs ilyen probléma.

20,9 Usta - Tagfüggvényekre vonatkozó mutatók használata (ptrtomember.cpp)


o : / / 20.9 Lista Tagfllqqvényekre vonatkozó mutatók
l: linclude <iostream>
2,
3: enum BOOL U'ALSE , TRUE) ;
4,
5: class Mammal
6: (
7: public :
8: Manunal() : itsAge(l) (
9: virtual -Ma.mmal() ( )
10: virtual void Speak() const = O:
ll: virtual void Move() const = O;
12: protected:
13: int itsAge:
14: );
15 :
16: class Dog : public Mammal
17 :
18: public :
19 : void Speak ()const { std :: cou t « "Woof!\n" ; }
20 : void Move() conl'lt ( std: : cout« 'Walking to heel ... \n" ; )
21: ):
22:
23: class Cat public Ma.mmal
24 :
25: public :
26 : void Speak {)const std :: cout« "Meow!\n ' ; )
27 : void Move() const std : : cout « ' slinking ... \n' ;
28 : );
29 :
396 1VI. rósz' Különlegessógek

30 : class Horse public Mammal


]1: (
32 : public :
33: void Speak()const std : : cout « "Winnie!\n"; l
34 : void Move() const std :: cout« "Galloping ... \n" ;
35: };
36 :
37: int main()
38:
]9: void (Hammal : :* pFunc) () const ,,0;
40: Mamma1* ptr "0:
41: int AnimaI;
42: int Method;
43 : boo1 fQuit " false ;
44:
45: while (fQuit "" false)
46 : {
47 : std: : cout« "( OlQuit (l)dog (2)cat (J)horse: ";
48 ; std: : cin » Animai;
49 : switch (AnimaI)
50 : (
51 : case l :
52 : ptr " new Dog ;
53 : break;
54: case 2:
55 : ptr " new est;
56 ; break;
57 : case ]:
58: ptr = new Horse;
59 : break;
60 : default:
61 : fQuit = true;
62 : break;
63 :
64 : if (fQuit)
65 : break;
66 :
67 : std : , cout « • (1)5peak (2)Move :";
68 : std , : cin » Method;
69 ; switch (Method)
70 : {
71 : case l ;
72 : pFunc Mammal : : Speak;
73 : break ;
74: default:
75 : pFunc = Mammal : :Move ;
76 : b r eak;
77 :
78 :
79 : (ptr-> *pFunc) (J ;
80 ; delete ptr;
81 :
82 : return O;
83:
20. 6ra • 397

menet
(O)Qu i t (1) Dog (2)CaL (3}Horse : 1
(1)Speak (2)Move : 1
Woof!
(O)Quit (1)oog (2)Cat (3)Hor sc : 2
(1)Speak (2)Move : 1
Meow!
(O)Quit ( 1) Oog (2)Cat (3)!-!orse: 3
( 1 )Speak (2)Movc : 2
Galloping
(O)Quit (1)Oog (2)Cat (3)Horse : o

Az 5-14. sorban a Mammal (emlös) absLtr..kt adattípust deklar:lljuk két tiszt~n virtll{llis
metódussal; ezek a Speak () és a Move () . A Mammal alosztúlyai a Dog, ;1 Cat és
a Horse (azaz a klltya, a macska és a 16), mindegyik feIOlírj:\ a Speak () -et és
a Move ()-ol.

A f6program megkérdezi a felhaszná lót, hogy melyik áJlatfajtát ho zzuk létre. Ekkor lét-
rejön 11 mum6riában az AnimaI-nak megfelel6 ~J[:l!oszt{lly, és címe bukerül ti ptr nm-
tmóba a 49-63. sorban.

Ezután eldöntheti a felhasz.náló, hogy melyik metódlIst szerelné meghívni. A döntésé-


nek megfele l ő metódus címe bekerül <I pFunc mutatóba. A 79. sorb,1n lefuuatjuk a lét-
rehozotl osz.tály kivá laszto!! tagfüggvényét; a ptr mUlat a létrehozott osztályra, és
a pFunc segítségével érjük el <I megfelel6 függvényt.

Végül a 80. sorban törlés következik; a delete paranccsal fclsz:lbadítjuk a ptr által
mutatott dinamikus memóriaterüleleL Fontos, hogy nem a pFunc-ra szabadít juk rá
a delete-et, mert a... egy kódrészletre irányul6 mutató, nem pedig egy memóriafoglaló
objektumra. Az efféle kísérletet a fo rdítóprogram kiszúri egy megfelelő hibajelzéssel.

Tagfüggvényekre vonatkozó mutatókból álló tömbök


Ahogy a normál függvénymlltatókat tömbbe lehet szervezni, ugyanígy a tagfiiggvé-
nyekre vonatkozó mutalókat is. A tömb a különböző tagfüggvények címeivel iniciali-
zá lható, melyek kés6bb meghívhatóak az adon indexű tömbelemre hivatkozva.
A 20. 10 Lista mutatja ezt a lehetőséget.
398 VI. rész •

20.10 usta - TagflIggvényeIcre _ _ 6IhI _ _ _


,"rnyptrfunctioo.cppl
o: II 20.10 Lista TagfOggvényekre vonatkozó mutat6kból tömbje
1: linclude <iostream>
2.
3 : class Dog
4: {
5: public :
6: void Speak()consL ( std : : couL « 'Woof!\n'; )
7: void Move () const ( std :: cout « 'Walking to heel ... \n";
8: void Eat() const ( std :: cout« "Gobbl ing feod . .. \n ", l
9: void Growl{) const { etd :, cout « 'Crrrrr \n" , }
10 : void Whimper () const { std : : cout « 'Whining no i se!! ... \n"; }
ll : void RollOver () const ( std : : oout « 'Rol l ing over ... \n '; )
12 : void PluyOead () const
13 : { std :: cout« "Is t his the end of Li t tIe Caesar?\ n ' ;
14 : };
15 :
16 : typedef void (Dog : ," PDF) ()const
17 : int main()
IB : {
19 : const int MaxFuncs '" 7;
20, PDF DogFunctionslMaxFuncsJ
21: { D o g : : Speak,
22 : Dog' ::Move,
23 : Dog : : Eat,
24 : Dog : : Growl,
25: Dog , :Wh imper,
26: Dog : : RollOver,
27 : Dog : : PlayDead
28 : l;
29 :
30: Oog* pOog ,.0 ;
31 : int Method ;
32 : bool f Qu it " false :
33:
3 4: while ( !fQuit)
35 : (
36 : std :: cou t « ' (O)Quit {l)Speak. (2) Mov e (3)Eat (4) Growl';
37 : s t d :: cout « ' (5) Whimper (6)Roll Over (7)P l a y Dea d : " ;
38 : s t d : : cin » Method ;
39 : i f (Method ,,= O)
40: {
41 : fQuit '" true ;
42 : break;
43 :
44 : else
45 : {
46: pDog = new Dog;
47 : (pDog - >*DogFunctions[Method- lJI {l ;
20. óra •

48 : delet e pDog;
49 :
50 :
51 : return O;
52 :

menet
(O)Quit (l)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Rol l Over (7)Play
Dead: l
Woorl
(O)Quit (l)Speak (2)Hove (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)P!ely
Dead: 4
Grrrrrr
(O)Quit (l)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play
Dead : .,
Ia this the end of Littie Caesar?
{O)Quit (l)Speak (2)Move (3)Eat (4)Crowl (5)Whimpcr (6)Roll Over (7)Play
Dead: O

A 3-14. sorb:ln létrebozzuk a Dog osztályt h6llagfüggvénnye l, melyek mindegyike azo-


nos visszatérési típusCl és pamméter-szignatúrájll. A 16. sorban typedef-fel definiáljuk
a PDF típust, amely egy olyan Dog-tagfüggvényre vonatkozó mUlató, amely nem vár
par<unétert és nem is ad vissza semmit, dc konstans. Ez ugyann, mint a Dog héllag-
függvé nyének a paramé ter-szignatúrája.

A 20-28. sorban dekbráljuk il DogFunctions tömbŐL, amely a hé Llagfüggvényt fogj:"!


tarta lmazni; ezek mem6riacímeivel inicializáljuk.

A 36-37. sorban a felha sználó t választás elé állítjuk. Hacsak nem lép ki (O -Quit), lét-
rejön egy új Dog objektum a dinamikus mem6riaterületen, majd a tömb megfelel6 in-
dexű metódusa kerul meghívásra a 47. sorban. Ez újfent egy olyan sor, amellyel vizs-
gázt:uni lehet ct C++ programozókat, hogy vajon tudják-e, mit csinál:

(pDog->·DogFunctionsIMethod-lj) () ;

Valljuk be, ez így egy kissé ezoterikus, de ha a programban valami miau éppen ragfü8b",é-
nyekb61 álló táblázatm van szükség, ez Cf,'Yszeruöbé és 0lvashat6bbá teszi a kódot.
I
400 VI. rész· Különlegességek

Kérdések és válaszok
Kérdi'!>: Miél1 haszllá/llánk slatikus vállozókal, ha lehel globálisakails lIasZ/lálni?

\fáIasz: A statikus változók ha16köre egy osztályra ko rlátozódik, igy csak ezen osztály
példányaiból érhetóek el: nyilvános változ6 csetén explicit és teljes osztálynév-meg-
adással, vagy esetleg egy statikus tagfüS&",ényen keresztüL A statikus adatok típusa
az osztályhoz kÖl6dik, és a szigorítou elérés, illetve :I Z erds típusoSs{lg miatt b iztonsá-
goslibban használh:Hók, mint a globálisak.

Kérdés: ANér/ "aszt/álnúnk slaukus lagfüggvényel-.:cI, lIa lehet glo/:Jálisakal is !taszllál"i?

Válasz: A statiku.s lagv:i lloz6k egy osztály hatókörébe tm1.07;nak, és Csak az adott osz-
t(lly objektum{m keresztül érl1et6ek el, vagy kifejezett és teljes osz!álynévvel, mi nt pél-
dúu l C1U8sName: : Funct;.ionName ( ).

Kérdés: Miér/ nCIII Icsszf}k mindcll osztályul/kal aZOIl osztá{}'Ok barálaivá, amelyeket
használllak?

Vúlasz: Egy o..o;ztály baráuá nyilv{mít:'isa napvilágra hozza a megval6.sítás részleteit és la-
7.ítja az egységlx:zárást. A cél :lZ, hogy az osztályok megvalósításának a részletei lehe-
t6leg maradjanak rcjrvc más osztályok eI6t1.

Gyakorlatok
Az elmúlt órában megisrnerkedtünk a különleges osztályok, fí.iggvények és mUlatók vi-
lágával. Yíllaszoljunk meg néhány kérdést és végezzünk cl nC:hány fel::tdatot, hogy el-
1l1élyíL<;ük ezzel k:lpcsol:ltos tudásunkat.

Kvíz
1. Mi II .szerepe a statikus adatugoknak egy osztályon beled?
2. Mi a szerepe a statikus tagfüggvényeknek egy osztályon belLil?
3. Milyen e16nye van annak, hogy lélrehozzuk II string. hpp állományt (20.3 Lis-
t:I), ahelyett, hogy beillesztenénk tartaImát II 20.4 Listába, ahol haszn5ljuk?
4. Milyen e16nyökkel jár a barátosztályok használata?
20. óra· "1 I mutatók 401

Feladatok
1. Vegyük ki a megjegyzésb61 a string . hpp állomány (20.3 Lista) cout lIIasításait
(36, 50, 62, 74, 82, és 99. sor), majd futlassuk le az employeelllain. epp progra-
mot (20.4 Lista). így láthatóvá válik, hányszor hívódnak meg a konstruktorok,
destruktorok és operátorok.
2. Módosítsuk úgy a ptrarrayfunction . cpp programot (20.6 Lista), hogy helye-
sen kezdje a fe lhasználó által bevitt értékeket! (Ötlet: Mi tárolódik eJ a tömbben
érvénytelen bevitel esetén? Ne felejtkezzünk el a 42. sorról!)
3. Hasonlítsuk össze a string . hpp állományban (a 20. 1 tistában) definiált String
osztály lehet6ségeit azzal a szabványos könyvtárral, amely a fordít6programrnal
(:rkezett. A Borland fordítóprogram esetén ez a Súgó, Súgó témák, Já l1alom me-
nüpont Referellcia részére katti ntva érhető el. A súg6ablakhan látszani fog
a <string> h ivalkozá.~.

Válaszok a kvfzkérdésekre
1. Az osztály összes objeklumához csak egyetlen példányban léteznek. Lehet6vé
teszik, hogy az Oszt<'ilypéldányokra vonatkozó fontos közös adatokat nyilvánrart-
suk (például a létez6 példányok darabszámát).
2. A statikus adattagokhoz hasonloon aZ osztály összes objektumához egyetlen vál-
tozatban példányosulnak. Ált.1lában a statikus adauagok eléré~re szolgálnak.
3. A string. hpp-hez hasonlÓ beemelend6 (illclllde) állományok létrehozásával
lehet6vé válik, hogy ugyanazt a k6drészletet több programmodul is (esetleg
több munkatársunk is) használhassa. Ez egy remek programozási gyakorlat; in-
kább Jehet6ség, minL<;em a fordil6progmm elvárása.
4. A ban'Ítok lehet6vé teszik m:~gu k közt ~ tilkaik" mcgoszliisát. Ahogya védett el-
érés lehet6vé teszi :LZ egymásból származtatott osztályok között :lZ adatok és
rüggvények elérését, míg a világ többi része el6l mindez elzárva marad (így
a priv,ít és a nyilvános "közötti" az elérési módjuk), hason lóképp működ i k a ba-
ráti elérés is a megfelel6 osztályok, tagfiiggvények illetve az erre kiv{dasztott
I'i.iggvények között.
21. ÓRA

Az előfeldolgozó
Ebben az órában a következőkról lesz szó:
• Mil nevezünk felt ételes fordításnak, és hogyan alkalmazhatjuk a gyakorlatban
• Hogyan írhatlInk makr6k.11
• Hogy:m használhatjuk lZ el6feldolgozó szolgáltat.1sait hibakeresésre

Az előfeldolgozó és a fordftó
Valahányszor elindítjuk egy program fordítását, először az előfeldolgozó kapja meg azt.
Az e16feldolg07.6 először is kikeresi II forrásk6db61 a neki szóló utasítások:lt, amelyek
mindegyike egy kelt6skereszttcl (ji j pound) kezd6dik. Ezek az utasítások kivétel nél-
kül valamilyen szövegm6dosítási eljárást írnak le, amit az előfeldolgozó a forrnsk6don
fog végrehajtani. Az eredmény egy új, átmeneti fOfrásfájl, amit progmmoz6ként általá-
ban nem is látunk. Ugyanakkor e16írhaljuk az eltífe1dolgoz6nak azt is, hogy készítsen
egy mentést cbb6l a köztes állapotból, hogy késo"bb tanulmányozhassuk.
VI. rész •

A fordítóprogram soha nem használja az eredeti forrásfáj lt, helyette mind ig az előfel­
dolgozó kimenetén kezd cl dolgozni. A már eddig is gyakra n látott # include direktíva
például arra utasítja az el6feldolgozót, hogy keresse meg a parancs után megadott fájlt,
majd annak teljes tartaImát szúrja be a {Ydrancs helyére úh'Y, mintha azt nú magunk gé-
pe1tük volna be. Amikor a fordító megkapja a fájlt, a beszúrt tartalom már on van.

A köztes állapot mentése


Gyakorlatilag valamennyi fordítónak van egy oly<ln kapcsolója, amivel másolatot ké-
szíthetünk az előfeldolgozó kimenetér61. Ezt a sz.olg5.ltatást bekapcsolhatjuk a grafikus
fej l esztői környezetUen, de megadhatjuk <I kérdéses kapcsolót a parancssorban is.
Ha tehát valamiért kíváncsiak vagyunk az előfeldolgozó kimenetére , olvassuk el a for-
dít6programunkhoz mellékelt do kumentációl. BiztOsan benne lesZ a megoldás.

A #define direktfva használata


A IIdefine direktívával egy karaklerláncokon végezhct6 hclycuesílést írhatunk e16.
Nézzünk egy pé ldát:
IIde(if\tl EIG 512

Ez az elCífeldolgozónak szóló utasít.ls azt jelenti, hogy bárhol, aho1 a BIG szöveget látja ,
cserélje azt le 512-re. Fontos hangsúlyozni , hogy itt a k.. raklerlánc szót nem annak
C++-os értelmében használjuk. A IIdefine direktíva után tulajdonképpen egy jelet
(tokent) adunk meg, ami a sl6 általános értelmében véve egy karakterlánc, és amit
a kMszövegben egy másik karakterláncr:1 fog le<::serélni az előfeldolgozó, függetlenü1
attól, hogy a7. egy karaktcr!{111ccal, egy állandóval , egy tömbbel vagy bárm i egyébbel
kapcsolatban bukkan-e fel. Né7.zünk talán egy konkrét esetet:
Idefine BIG 512
int myArray [BIGI;

Ilyenkor az cl6fcldolgozó által el6állíloll köztes fájl II következ6t fogja tartalmazni:


int InyJ\r ray [ 512 J ;
figyeljük meg, hogya ltdefine lJ(;lSítás már nincs sehol. Az elGfeldolgozó a köztes
k6dból eltávolítja az összes neki szóló utasítást, v'lgyis azokal a fordítóprogram soha
nem is látja.
21.óra- 405

A #define használata állandókkal kapcsolatban


A idef i ne egyik leggyakoribb felhasználási módja az állandók definiálása. Amim eZl
már kor.ibban is említelllik, ez aO! esetek túlnyomó többségében nem valami jó ötlet,
mert az el6feldolgoz6 egysl.cru szöveghelyettesítést végez, a kontexmst, vagy a típuso-
kat soha nem elemzi. Éppen ezért sokkal biztonságosabb a const kulcssz6t használni
ilyen helyzetekben.

A #define és az #ifdef használata vizsgálatok végzésére


A #dcfine direktíva hasznli(at{lnak másik módja az, amikor mindössze :umyit közlünk
általa :IZ clófcklolgozóv;!l, hogy egy karakter!{tnc - illetve az általa képviseltloken -
defini:í !l. lr!wjlJk p61dául a következ61:
~d e finc BIG

A k(x.! későbbi részeiben aztán megvjz'sgfl lh:ltjuk, hogy meghatározotl-e ennek a token-
nek az érték, éS:l vizsg!i1at eredményét61 függ6en végeztethctünk el más mGvelctcket.
Ehhez az lIifde [ és az IIlCnde f direktívákat basználhaljuk, melyek közül az elM) :lkkor
ign, lm a tokcn del1nitilt, a másik pedig akkor, ha nem. Mindkét utashástömhöt eb'Y
fendif direktívának kell zárnia, még a kövctkez6 bezáró k.lJX..-.;Q.c; zárójel előtt.

Az li fdef segitségével megfogalmazou reltétel tehát akkor értékelődik ki igazra, ha


az utáml sze replő tokent korábban már def'ini:íltuk. Ha például egy feltételesen végre-
hajtandó nyomkövetési utasítlist szeretnénk beépíteni a k6dba, azt a következ6képpen
tehetjük meg:
U fdc f DEBUG
cou t « 'OCbug d c fin cd ';
.cndif

Arnikor ez előfcldolgozó elérkezik az lIifdef direktívához, megnézi ti belső nyilvlintar-


(Ó táblázatában, hogy szcrepel-e benne a DEBUG szimbólum. Ha igen, :lkkor :IZ Itifdef
ut'l.~ítás igazra é rtéke l őd i k ki, melynek következtében minden, a következ6 !telse
vagy ltendiC direktív:íig szerepl6 k6dsor belekerül a fordítónak {uadotl köztcs kódba .
Ha ezzel szcmben a logikai vizsgálat hamis értéket credményez, akkor az It ifdef és
a következő #else vagy ~endiC direktíva által határolt sorok nem kerülnek bele II Ic-
fordítandó kódba. Kimaradnak, mintha soha nem is lenek volna.

Jegyezzük meg, hogy <lZ "i fnde f az " i fdef logikai ellentéte, vagyis ez akkor értékel6-
dik ki igazra, ha az urána szerep lő token a forráskód adott pontján még nem del1niált.
406 1VI. rész • Különlegességek

Az #else direktfva
Arnint a név is sugallja, az #else direktíva az " ifdef vagy #ifndef és az II endi f kü~
zött szerepelhet, és a feltételes utasításrendszer másik ágát képviseli. A eddig megis-
mert direktívák használatára mulat példát a 21.1. Lista.

21 .1. Lista - A #defin8 direktfva használata lusingdefine.c


o: 1/ 21.1. Lista A Ide fine direktiva használata
1 , 'define DemoVersion
2 : 'deHne OOS_VERSION 5
3 : 'include <iostream>
4 : int main ()
5: {
6 : s td : : cou t « 'Check Jng on t he dc f i n i t ione oC DemoVersion, •
7: « 'DOS_VERSION {lnd WI NOOWS_ VERSION .. . \ n ";
B.
9 : *ifdef DemoVeraion
10 : std : : cout « "DemoVersion defined . \n ';
11 : ~else II DemoVeraion
12 : std :: cout« ' OCmoVersion not defi n ed . \n ";
13 : Icndif /1 DemoVersion
H ,
15 : 'iEndef DOS_VERSTaN
16 : std :: cout« "DOS_VERSION not de(ined ! \n ";
17 : "else II DOS_ VERSION
l B: s td: : cout « " DOS_VERSI ON dcfined "'S : "
19 : « DOS_VERSION « s t d :: cmU;
20 : nc nd if II DOS_VERSION
21 :
22 : li f def WINDOWS_VERSION
23 : std : : cout« "WINOOWS_VERSION defined ! \n" ;
24 : 'else II WINDOWS_ VERSTON
25 : std : : cout « 'WINDOWS_VERSION w",s not defined . \n" ;
26 : 'endif II WINDOWS_VERSION
27 :
28 : std : : cout « "Donc . \n' ;
29 : rc t ur n O;
30 :

Check ing on Ule definitions o f DemoVersion,


OOS_ VERSION a n d WINDOWS_ VERSION ...
DcmoVers ion defined
OOS_ VERSION defincd as : 5
WINOOWS_VE RSION was not de finc d .
Done.
21. 6ra • Az

Az 1. és 2. sorokban definiáltuk a De moVersion és a OOS_VERSION Lokeneket. Közü lük


a OOS~V ERS! ON énéket is kapott CS). A 9. sorban az elófeldolgoz6val megvizsgált3ljuk,
definiálva van-e a DemoVersion. Bár ehhez a tokenhez nem tartozik érték, .tzért szere-
pel az előfeldolgozó bels6 tábláz.ttában, tehát a vizsgálat logikai igazra értékel6dik ki.
Ennek hatására a 10. sor bekerül .. k6db.. , vagyis a képernyóre ki fog ír6dni a megfele-
lő szöveg.

A 15. sorban a DOS_VERSI ON dcOniáltságát, pontosabban annak hiányát vil'_<;gáljuk. Mi-


vel az előfe l dolgozó számára. a OOS_VERSION token is definiált, ezért a teszt é rtéke ha-
mis, ami azt jelenti, hogy nem a 16., hanem a 18. sor kerül bele a Icfordítandó kódba.
Ht a DOS_VERSION helyére egyben behelycucsít6dik az; érték, vagyis a fo rdítóprog-
ram végü l a következ/'} kódsol1. fogja látni:
cout « 'OOS_VER SION defined as : • « 5 « Gnd!;

Figyeljük meg, hogya DOS_VERSION else; e l őfordulásá t az előfeldolgozó érintetlenül


hagyja , mivel az kettős idézőjelek között szerepel a k6dban. C.<;ak a második
OOS_VERSION . ulnik el ", illelve kenU be helyére a megfelelő élték. Maga a fordítóprog-
ra m tehát ebben ;1 sorban mar csak az S-ös számot fogja lálni ugyanúgy, mintha azt mi
gépeltük volna be a megfelel6 helyre.

Végezetül a 22. sorban az el6feJdo lgo z6 megvizsgáJja a WINDOWS_VERSION to ken defi-


niáltságát. Mivel :IZ ezt me gel6 z6 kódban sehol nem szerepelt a
' dcfine WINDOWS_VERSION

sor, ezért ez a token az előfeldolgozó számára ismeretlen, nem definiált. A vizsgálat


végeredménye így logikai hamis, vagyis a lefordítand6 kódba nem a 23. hanem a 25.
sor kenil majd be.

Beszúrás és annak vezérlése


Egy projekt {tltalában számos forrásfáj lb6l á ll. Alapvet6en sokféleképpen lehet szervez-
ni egy kód szerkezctét, de objektum-orientált tervezésnél az egyik legkézenfekv6bb
módszer az, ha minden egyes osztálynak van egy saját fejlécállom5nya C. hpp), benne
az oszL'íly deklaráci6jával , és egy a konkrét megva l6sításltanalmazó . c pp állománya,
ami a metód~l sokat tartalmazza.

A main () filggvényt célszerűen szintén egy önálló. cpp állomány tarta lmazza. A fordí-
tás során aztán valamennyi. cpp fájlb61 készül egy tárgyk6dú állomány C. obj), ame-
lyeket végül a linker összeszerkeszt egyetlen funatható p rogrammá.
408 1VI. rósz· Különlegességek
Mivel a programok többsége s7.ámos osztályt használ, számos különböző helyen kell
a kódba beszúrni az ezek deklarációját L,1rtalmaz6 fejlécállományokat. Emellett az sem
ritka, hogyafejlécállományok egymásra hivatkoznak, vagyis egymást töltik be. lia pél-
dául van egy szá rmaztatott osztályunk, és ehhez tartozik egy külön fejlécállomány, ak-
kor ennek nyilvánvalóan be kell emel nie az alaposztMy fejlécá llomá nyiít is.

Képzeljük e l, hogy az AnimaI nevű osztály deklarációjá( az ANIMAL . HPP fájl tartalmaz-
za. A Dog osztályt az AnimaI osztályból származt:ltjuk, ezért a JX)G. HPP-nek be kell
szúmia az ANIMAL. HPP-l is, Iliszen az tartalmazza az alaposzlállyal kapcso1:l1os informá-
ciókaL Tegyük fel , hogy létrchozunk egy eae osztályt is, amelynek alaposztálya szintén
az Anima! oszt..'íly, vagyis a CAT . HPP-ben is szerepelnic kcll cb')' az ANIMAL. HPP-l be-
s7.úró parancsnak. MármOSI ha ínmk egy olyan metódlIst, amely egyszerre hasl.nál C6t
és Dog típusú objekl'Umokat, akkor az ezt tartalmazó k6dnak egyaránt be kell emelnie
II CAT . HPP-t, és a rxx; . HPP-t, ami viszont azzal jár, hogya köztes k6dllnkban k6tSZt:r fog

szerepelni az ANIMAL. HPP t:1rt<1IIl1<l.

A helyzet tehát é rdekes, hiszen erre fordítási hibát kell kapnunk, mivel nem deklarál-
ha\'unk egy oszt{llyt - jelen esetben az l'Inimal-L- ké tszer, még akkor sem, ha a k(:t
deklaráció egyébként mindenben megegyezik. EZl a p robU~ mát úgy kerülhetjü k meg,
ha megfelelő jelzőkkel vezéreljük a fejlécl lJományok beszúrását Onclusion guards).
A módszer egészen egyszen1. Szúrjuk be az ANIMAL. Hpp elejére, illetve végére a kö-
vetkező sorokat:

~ifndcf ANIMAL_HPP
*define hNIMl'IL_HPP
... II ide jOn a fejlécállomány te lj es tartalma
~endif

Ez a kódrészlet az c1őfeklolgoz6 számára a következőt jelenti: ~ Ha az ANTMAL.-HPP token


nem meghatározoti, akkor tegyi.ik nzá, amúgy pedig mehetünk tovább a kódban ~.
A Idefine és az lendif között szerepel a fej léc-.íllomány teljes ~hasznos~ tartalma.

Amikor progmmunk els6 alkalommal szú~a be a fenti fejlécállomá nyt, az előfe l do lgozó
elolvassa az első SOlt, amely igazra é rtékelócJik ki, hiszen az ANlMAL_HPP token még
soha nem szerepelt. En nek megfelelően a feldolgozás halad tovább, a k6clba bekerül
a fej J6d llomány tel jes t:l l'talma, mellesleg ped ig meghatározottá válik az ANIMll.L_ HPP.

Amikor második alkalonum l pr6báljuk meg heszúrni ugyanezt a fáj lt, az előfeldolgoz6
megint elolvassa az els6 son. Ekkor azonban a feltétel már hamis lesz, hiszeo
ANtMAL_ HPP- t az előző alkalommal definiáltuk. Ennek megfelel6en az előfeldolgozó
elkezdi keresni a kövelkező lIelse direktívát (jele n esetben nincs ilye n) vagy a követ-
kező fiendif-ct (ez esetünkben egyben a fájl vége), és addig a pontig mindent kihagy.
Ez konkrétan azt jt!lenti, hogy gyakorlatilag nem tönénik senuni, hiszen a fej lécállo-
mány teljes tartaimát kihagyjuk, vagyis bár kéLo;zer szerepelt II kódban a z AnimaI osz-
tály deklarációját tartalmazó részlet beszúrása, mégsem keletkezik szintaktikai hiba.
21.óra - 409

A kódban szercpl6 szimbólum neve CANH1AL_HPP) amúgy teljesen lényegtelen, de


az általánosan bevett szokások szerint megegyezik a fejlécállomány nevével úh'Y, hogy
azt csupa nagybetűvel írjuk, és a pontot Iccseréljük aláhúzás karakterre. Hang5Úlyoz-
zuk, hogy ez csupán konvenció, nem a nyelv álta l támasztott követelmény.

Tudta hogy...? A beszúrási direktfvák védelme


A fent bemutatott védelmi módszernek semmiféle ~káros mellékhatása~ nincs. Hasz·
náljuk tehát bátran, mivel bizonyos esetekben sok órányi hibakeresést spórolhatunk
meg vele.

Szimbólumok definiálása a parancssorban


Csaknem v:alamennyi C++ fordító Ichct6vI! teszi, hogy mindazt, am it a /ldefi ne direktí-
V'I s(!gíts(;gével magában a k6dhan dcfini:t1n{l11k, megadhassuk a par.Jncssorb:lll, vagy
az intcgr.ílt rejleszt6i környezeten keresztül is. Ha tehát a célszerűség úgy kívánja ,
a 21.1. Uslából akár ki is hagyhatjuk az 1. és a 2. sort," DcmoVersion és a OOS_VE:R ·
S ION szimbólumokat pedig bizonyos fordításoknál meg:ldhatjuk a parancs.'.orban, míg
más esetekben simán kihagyhatjuk 6ket.

A programozók körében teljesen :'ihaláno.'ian alkalmazott munkunódszer:lZ, hogy


a nyomkövetéshez használt k6drészletckct fi fdef DEBUG és lendif direktívák közé
zá~ák, majd amikor nyomküvetést akarnak végezni, akkor fOfclít{\skor a parancssorban
megadják ezt a szimb6l u111QL. Ez rcndkívü l egyszerű és kényelmes módszer :lml, hogy
a nyomkövetési részeket egyetlen mozdulattal kihagyhassuk al efordított kódból, de
~zükség eselén azonnal ~vissz.1Varázsolhassuk " 6ket. Ha a végs6 kódot fordítjuk, egy-
szerűen ki kell hagyni a param:ssorból a DEBUG meghatározását és kész.

A meghatározottság megszüntetése
Ha korábban meghatároztunk egy szimb6lumot, de a kód hátralevő részében törölni
szerelnénk azt, akkor használhatjuk az lIundef direktÍv;ít. Ez egyszcrúen a "define el-
lentéte, vagyis törli az előfeldolgozó bels6 táblázatából a kérdéses tokent.

Feltételes fordftás
A Itdefi ne és a parancssori dcfiníciók, valamint az hfde f, "eJse és IIHndef direktí-
vák segítségéve l olyan kéxlOl írhanmk , amdynek egyes darabjai attó] füg,gően kerülnek
bele alefordított progmmban, hogy éppen nillyen IIdefine direktívák szerepelnek ben-
ne, vagyis melyek az aktuálisan meghatározott szimbólumok. Ezt a lehet6séget felhasz-
nálhatjuk például egy olyan program fejlesztése során, amelynek két különböző platfor-
mon, például DOS és Windows alatt is működnie keH, de nyilván máskénl. Így nem keU
kél külön kódoL ka rbantartanunk, elég egyetlen kódbázisra építeni a projektet.

A feltételes fordítás másik nagy felhasználási területe a nyomkövetés, amikor a már ko-
rábban említett OEBUG szimbólum meghatározottságát vizsgáljuk a megfelelő ponto-
kon. Errol a módszerr61 amúgy hamarosan bővebben is ejtünk még szót az assert ()
kapcsán.

Makróként megvalósított függvények


A Itdcfi ne direktív{lt úgynevezett mak.rófüggvények megadására is felhaszn1ilhatjuk.
A makr6függvény olyan az elóf"e1dolgozó által értelmezett nyelvi konstl1.lkdó, amely
form:lilag a függv(:nyekllez teljesen hasonlóan képes argumentumokat fogadni. Ebben
az esetben persze a behdyettesítf!st az előfeldol gozó végzi el, a p:lrarnéterek pedig ter-
mészetesen mindig karakterláncok. Nézzünk talán egy példát, ami rávilágít a dolog lé-
nyegére. Definiáljunk egy olyan TWICE nevlÍ m3krófüggvényt, amely egy 1llegszoroz
egy számot kelt6vel:
~ define TWICE(x) ( (x) * 2 )

EZl :IZ új nyelvi konstrukciót megadása után a kódban a következőképpen haslnálhatjuk:


TWICE(4)

Az e lőfeldolgozó ebben az esetben a TWICE (4 ) karakterláncot el fogja távolítani a for-


ráskódból és kicseréli 8-ra. Ez kissé részletesebben úgy történik, hogya behelyettesítés
eredményeként előá1l6 ( (4) * 2 ) kódot az el6feldolgoz6 ki is énékeli, és ennek
az eredménye keJiIl a makrófüggvény hívásának helyére.

Egy makrónak természetesen egynél több paramétere is lehet, és bármely paramétert


egynél többször is felhasználhatunk az elvégzendő míivelet leír(lsa során. A két talán
leggY'lkrabban használt többparaméteres makró, a MAX és a MI N definíciója például
... követkcz6képpen fest:
#def i ne MAX(x,y) (x) > (y) ? (x) (y)
~d ef i ne MIN(x,y) ( (x) "" (y) ? (x) (y)

Ügyeljünk r:í, hogya makródcfinídókban a paramétereket körülzáró zárójelpár nyitó


tagjának közvetlenül a makró neve után kell következnie, nem lehet köztük szóköz.
Általában is igaz, hogy az előfeldolgozó sokkal kevésbé megbocsátó a fölösleges üres
karakterekkel kapcsolatban, mint a fordítóprogram.

Lássuk, mi történik, ha megfeledkezünk erről:

Ildefine MAX (x ,y) ( (x ) > (y ) ? (x) : (y ) )


21. óra • I I 411

Pr6báljuk csak ki, mit tud a MAX makrónak ez a változata:


int x = 5, Y = 7, Z;
z = M!\X(x,y);

A fenti k6drészletct az előfeldolgozó ebben az esetben a következőképpen ala látja át:


int x = 5 . Y = 7 . z;
z .". ( x,y ) ( (x ) > (y ) ? (x ) , (y ) ) ( x,y )

Látható, hogy a fölösleges szóköz miatt közönséges szövegbchdyettesítés történt


a makr6füID,>vény értelmezése helyett. Az előfeldolgozó tehát úgy gondolta, hogy van
a MAX szimbólum, amit a (x,y ) ( (x ) > (y ) ? (x ) : (y) ) szöveggel kell he-
Iyettesítenic, hiszen ez szerepelt a definícióban. És a maga módján igaza volt...

J Ja ellenben kivesszük azt a bizonyos extra szÓközt, akkor a köztes kód a következő­
képpen fest:
int x • 5 , Y • 7, Z;
z ". 7 ;

Mi ez a rengeteg zárójel?
Az olvasÓ bizonyára <:!ksod§ lkozott non, mit keres az a rengeteg, mer6ben fölösleges-
nek nín6 zárójel az eddig bemutatott makr6kban. Mag'l ,IZ el őfeldolgozó ugyan nem
követeli meg ezeknck a kiírását, ugyanakkor számos potenciális hibát kerülhetünk el
az1hal, ha mindent, az utolsó részlctig akkur1nls.1n bez1r6jelezilnk. Efféle nem várt
mel1ékhatáSOk általában akkor bukkannak fe l, ha nem egyszeru számokat vagy válto-
zókat adunk át paraméterként a makróknak , hanem összetettebb kifejezéseket. A dol-
got megint egy példán lehel igazán jól szemléltelni. Vegyük a már látott MliX makr6mlk
egy "zár6jelmentes1tett" vá ltozatát, és nézzük m<:!&, hogyan mLTköc!ik:
Idefine MAX (x, y) x > y ? x : y

Ha parnmélerként az S és 7 értékeket adjuk ál neki, minden a legnagyobb rendben. De


ha valami összetettebb kifejezéssel próbálkozllnk, röglön érdekes dolgok lörténnek,
Ezt szemlélteti a 21.2. Lista.

21.2. Ulta - Miért használunk annyi zárójelet 8 makrókban .. , (usingparen,cpp)


o: 1/ 21. 2 Lista Makr6k kifejtése
1: .include <iost r cam>
2,
3: ~ d efi ne CUBE(a) ( (a) .. (a) .. (a) )
4, #def ine THREE( a ) a .. a .. a
S,
ú : int mai n ()
7, {
8 : lon g x = 5;
rész·

9, long y CUBE(x) ;
10: long , "
THREE(x) ;
ll :
12 : std : : cout « .y : « y « std: : end1 ;
13 : std : : cout « ·z: « , « std : : endl;
14 :
l S: lo ng , " 5, b " 7,
16: y CUBE(a .. bj ;
17 :
"
, THREE(a+b) ;
18 :
19 : std : : cout « .y: « y « std : : end1;
20 : std : : cout « "z : « , « std : :end1;
21 : return 0,
22 :

Kimenet
y , 125
z : 125
y , 1728
82
"

A 3. sorban cld"iniálunk egy CUBE (köb) nevt1 makr6l, amelynél az a paramélen annak
minden egyes előforcl ulásakor 711rójeibe tesszük. A 4. sorban lálható THREE makr6nál-
amely hítsz61ag ugyanazt él mQ'velelel hivatott elvégezni - nincsenek zárójelek.

A makrók haszná latár demonstráló kódban e lőbb mindkettőnek az 5 értéket adjuk :'it
paraméterként, és amint az a kimenetból látszik, mindkettő mOködik is szépen.
A CUBE (5) kifejt<:se az ( (5) * (5) .. (5) ) karakterlánc lesz, ami kissé pongyolá-
nak runik, de az eredménye kétségkívül 125, ami helyes. A THREE (5) kifejtése ezzel
szemben egyszeruen 5 * 5 .. 5, ami szép, egyszeru , és szintén 125 az eredménye.

És akkor jön a meglepetés. A 15-17 sorokban paraméterként az 5 + 7 kifejezéSI hasz-


náljuk. Ebben az esetben CUBE(5 + 7) kifejtése a következ61csz:
( (5 +7 ) * (5+7) * (5+7) )

EZl kicsit tovább a.1akílva a követk ezőt kapjuk:


( (12) * (12) ~ (12) )

Az eredmény pedig természetesen 1728, amint az várható is. Lássuk, hogy teljesít a má-
sik makró ugyanebben a sziruáciÓban. A THREE (5 + 7) az előfeldolgozó munkájának
eredményeként a következ6vé változik:
5 + 7 * 5 + 7 * 5 + 7
21. óra • 413

Mivel a szorzás magasabb precedenciájú mIlvelet, mint az összeadás, a fenti kifejezés


t'gyenértékű a következővel:

5 + (7 .. 5) -I- (7 .. 5) + 7

Ezt tovább alakítva a következőhöz jutunk:


5 t (35) + (35) + 7

Az eredmény 82, ami a sz6 egy bizonyos értelmében ht'lyes ugy;m, de egyáltalán nem
ezt vártuk ettől a makrótól. Nos, ezért kell oda az a rengeteg zárójel.

Makrók, függvények, sablonok


A CH rmkróival van néhány (összesen négy) probléma, El őször is a nagyobb d,u". . bo-
km meglehet6sen ne héz visszafejteni, mivel minden makró denníciójának egyetlen sor-
ban kell szerepelnie. A backslash (\) karakwrrel ugyan beikWthaturlk sortörést, de
az igaJ'_:U megvallva ez nem sokat haszmíl. Akármit teszünk, a nagyobb makrók egysze-
ruen nehéz karb:mtartani.

A másik problémát a l1lakr6k mllködési logikája jelenti. A makr6kat :lZ e l őfeldol gozó
értelmezi, vagyis az általuk el őírt helyettesítési nlliveletek mindannyiszor leflltnak, va-
lahá r'yszor a makr6 felbukkan il k6db;U1. [la 12-szer haSzn{t[lI11k valamit, akkor
az e[őrcldo lgozó l2-szer fogja "lefuttatni» a m,lkrót, szemben ;I Z igazi függvényekkel,
amelyeknél csak egy hiv:ilkozás kerül a kódba. Ugyanakkor az is igaz, hogy a makrók
álta lába n gyorsabbak, mint a fOggvények, hiszen segítségü kkel a lefordított kódban cl-
kerüljük a függvényhívással járó löbbletmunk:'it.

A feldolgozás módjából adódik a harmadik probléma is: maguk II makrók a köztes


kódba n már nem szerepelnek, azokkal a fordítóprogram egy:'iltal:'in nem találkozik. Eb-
b6l következ61eg a legtöbb nyomkövető sem. I (a tehát hibakeresés során egy makfÓra
gyanakszunk, mindenréle kö rmönfont trükköket kell alkalmaznunk.

A legnagyobb problémáI a végére hagytam: a makrók nem típusbizrosak. Egyrészr61


nagyon kényelmes, hogy egy makr6nak bármit átadhanmk par<Hnéterkém, másrészt vi-
szont nagyon veszélyes is. Ez a szolgá ltatás gyakorlatilag tökéletesen aláássa a C++
nyújtotta er6s tipizálást, így a programozók ki is átkozták. A megoldást a sablonok
használata jelenti, amelyekről a 23. órában lesz majd szó.
414 V1.ré,,' ..

Karakterláncok kezelése
Az elófe klolgoz6 két olyan speciális operátort bocsát a rendelkezésünkre, amelyekkel
a m .. kr6kba n felbukkanó karakterláncokat lehet feldolgozni. A szövcgg(: alakító
Cstringizing) operátor (I) akárnti is következik utána , azt idézőjelek közt: zárt karakter-
lánccá al akítja . Az össze fúző operátor Cconcatenation operator; III) nevének megfele16-
en két karakte rláncOl ch'Ycsít.

A szöveggé alakrtó (stringizing) operátor


A szövegg/! alakító (slringizing) operátor, vagyis a ... úgy működik , hogy :IZ utána kő­
velkez() szövegel - legyen az bármi - idézőjelek közé teszi, egészen a következő üres
kanlkLc.:rig. ]-Ia tehát ;l következőt írjuk:
Idefinc WRITES TRING(x) cout « #x

majd meghívjuk ezt rt lllakr61:


WRITESTRING(Thio io a string) ;

akkor az előfe l dolgozó a következő köztes kódot fogja előá ll ítani :

cout « "Thi s is a string" ;

Figyeljük meg, hogya fordítónak átadou k6dba a karaklerlánc (This is a string)


már ke tt6!'i idé.:őjcl ck között szerepel, ahogy azt a cout megköveteli.

Az összefúző operátor
Az összefírző operátor (concatenalion operalor; II H) lehet6vé teszi, hogy kél vagy több
karakte rláncot egy új sz6vá fűzzünk össze. Az új szó a fordító számára valójában egy
token lesz, amit kés6bb használhatunk egy osztály vagy v{jltoz6 nevekém, egy tömb
címzésekor eltolási értékként, vagy bármely olyan szituád6ba n, amikor betl.1k sorozatát
v,hja a fordítóprogram.

Tl:gyük fel például, hogy van öt függvényünk, amelyekí:!l a következ6képpen hívnak:


fQnePrint, íTwoPrint, fT hreePrint, fFo urPrint, és f FivePrint . Ezek használa-
tál egyszcn1sítcnd6 létrehozhatjuk a következő makr6l:
fdefine fPRINT(x) f 1111 x U Print

Ha kés6 bb szükségünk van mondjuk az fTwoPrint függvényre , :Ikkor aZl


a fPRINT(Two) fo rmában is meghívhatjuk. Teljesen hasonlóan a fpRINT(Three)
forma a fThreePrint függvénynévvellesz egyenértékű.
21. óra • 415

A 19. órában a láncolt listák kapcsán létrehoztunk egy Pa rtsList nevű osztályt. En-
nek él listának kizá rólag Li s t típusú elemei lehelnek. Tegyiik fel , hogy ez a mócbzer
valamilyen programmal kapcsolatban kiválóan beválik, és a sikeren felb~lzdll lva szeret-
nénk létreho7.0i állatok, autók, vagy számítógépek listáit is. Ez egyik megoldás ilyenkor
az, hogy él megrele l ő kódrészletek szolgai átmásolásáva ! létrehozzuk az Anima lLi s t ,
CarList vagy Compu t e r"Lis t nevl1 osztályokat is. Ez elsőre ugyan egyszen1nek tl1n-
het, de ha karban kell tartani a kódot, igencsak fájni fog a fejünk , hiszen bárhol is ve-
zelÜnk be valamilyen módosítást, azt át kell vczctnünk az összes többi helyre is. Sok-
kal elegánsabb és persze hatékonyabb is, ha megfelelő makrókat és az összeftízés ope-
rátorát használjuk . íme egy lehetséges megoldás:
Rdefine Listof(Typel class Type'.List \
( \
pub lic , \
Type ~#L ist(){) \
private : \
int i tsLeng th; \

"
A fel1ti példa természetesen kissé er6ltetett, hiszen leginkább pcdagógiai célokat szo l-
gá i, ám él lényeg világos.111Iátszik: ;I megfelel6 helyre beemeljOk :IZ összes adatot és
metódusl. Ha magával a makróval készen vagyunk, az AnimalList osztályt eb'Yszeru-
en a kövctkezőképpen hozhat juk létre :
Listot (Animal )

Bármennyire meglepő, ez az egyetlen sor az előfeldolgozó munkája nyomfl n átalakul


az An:Í.mal List oSltály teljes dek lart'lciójává. Persze azért van n~hány probléma is ezzel
a megközt!lítéssel , ezekről azonban majd a 23. órában lesz sz6 a sablonok k:lpcs.'Ín.

Előre meghatározott makrók


Számos fordítóprogram eleve tartalmaz néhány tucat általánosan használt makr6l. Jlyen
például a _DATE_, a _TI ME_, a _ LINE_, és a _ FILE_. Minden ilyen makr6
nev~ t elöl és hátul két-két aláhúzásjel határolja, amire csupán az~rL van szükség, hogy
ezzel minimálisra csökkentsük a felhasználó {Iltal definiált makrókkal való névütközés
es~lyér.

Amikor az előfeldolgozó egy ilyen makróvaltalálkozik, automatikusan elvégzi a szük-


sége.,; hclyeuesítéseket. A _ DATE_ hatására például behelyeuesíti az aktuális dátu-
mot, a _ TIME_ helyére pedig beszúrja az időt. A _ LINE_ és a _ FILE_ az akruá-
lis sor számát, illetve a forrásfájl nevét jelenti. Ügyeljünk rá, hogy ezeket a makr6kat-
mint nlinden más makrót - az előfeldolgozó kezeli, vagyis az értékek a fordításra és
nem a lefordított program futtatására vonatkoznak. Ha tehát valahova bcszúrjuk
rész •

a _DATE_ segítségével az dátumot, akkor az a fordítás dátuma lesz, nem pedig az az


időpont, amikor a progr.ullot futtat juk. Ezeknek abeépíten makróknak általában a hi-
hakeresés során vehetjük nagy hasznáL

Az assertO beépftett makró


S7..3mos fordítóprogram rendelkezik eb')' assert () nevtl előre deHniált makróvaL
Az assert () működése viszonylag egyszeru: igaz értéket ad vissza, ha a neki paramé-
Lerkém átadou kifejezés igazra értékel&lik ki, és vagy végrehajtja a megadott urasítássol1,
ha nem. A fordíl6programok működése ezen a ponton eltér6 lehet. Számos program
megáll, ha egyassert () makró hamisf'J. él1éke lődik ki. Mások csupán kivételt dobnak
ilyenkor. CA kivételekr61 és a hibák kezelésér61 majd a 24. órában lesz szó.) Az assert ()
makró egyik hasznos tulajdonsága, hogy al: el6fddolgozó Leljesen figyelmen kívül hagyja
il vele kapcsolatot kódol, ha ti DEBUG token nem def"iniftlt. Amíg fejlesztjük a kódot, a le-
fordított progmmb:m végig benne lehetnek azok a részk:Lck, amelyek a hib:!keresést.~e­
gítik. Ugyanakkor nyilv{m értelmetlen volna ezeket a végső programban is benne ha~,'yni .
hiszen az így nagyobb és lassúbb lenne. Ha az a!:l!:lert () -ct használtuk, a hi1:mkeresés-
hez használt k6dok egyetlen mozdulattal eltávolíthat6k a bin:írisbóL

Természetesen nem kötelez6 a fordít6progmm által felkínft lt assert () makr6ra tá·


maszkodni. Megírhatjuk akár a saját változatunkat is. A 21. 3. Lista egy ilyen egyedi - vi-
szonylag egyszerű - assert () megoldásra nllltat pl:ld{ll.

12.3. Usta - Egyegyszertl assenI) makró megvalósftása (simple8ssen.cpp)


o: /1 21 . 3 . ListA 11.2 Assert() makr6
1: #define DF.BUG
2: linclude <iostream>
3,
4: lifndef DEBUG
5: Idefine ASSERT{x)
6: 8else
7: *define ASSERT{x) \
8: H (! (x») \
9: ( \
10 : std : : cQu t « 'ERROR l ! Assert ' « Ix« " fai l ed\n '; \
11 : std :: cQu t « on li ne « _ L! NE_« "\n" ; \
12 : std :: CQut « • in fi l c ' «_FILE_« "\n' ; \
13 :
14 : lendit
15,
16: int main()
17 :
18 : int x ~ 5 ;
19, 5td,:cQut « 'First asser t : \ n';
20 : ASSERT{x==5) ;
21 : 5td :: CQut« ' \nSeco nd as sert : \n';
21. óra •

22 , ASSERT (x ! = 5) ;
23 : s td:: cout« O\nDone . \n" ;
24 , r eturn O;
25 :

Figyeleml Fáj~onnátumok
Egyes fordítóprogramok a 21.3. lista 1-13 sorait több sorra darabolhat ják. Ilyenkor
ki kell törölnünk a kódból a sortöréseket, másként nem tudjuk lefordítani. A problé-
mát amúgy az okozza, hogy a különböző operációs rendszerek, fejlesztői környaze-
tek és fordítóprogramok különböző karaktereket használnak a sor végének jelölésé-
re (..újsar karakte(), ami aztán a fentihez hasonló értelmezési gondokkal járhat.

Apropó Egysoros makr6k


A makrók kódjának a szó múszaki értelmében nem kell egy sorra korlátozódnia.
A 1-13 sorokban látható kód például inden kétséget kizáróan több soros, mégis
egyetlen makrót ír le. Ez úgy lehetséges, hogy minden sor végén ott van egy
backslash N karakter is, amely elrejti az egyébként fizikailag jelenlevő újsor karak·
tert, és a sorok összefüzésére utasftja az előfeldolgazól.
Ügyeljünk rá, hogy egy backslash segftségével csak egy újsor karaktert lehet .ha-
tástalanrtani~, vagyis ez azon kevés szituációk egyike, amikor az egymást követő
üres karakterek számának jelentősége van.

menet
First assert :
Second assert :
ERROR !! Assert xl .. 5 flIIiled
on line 24
in file E:\adisk\cppin24 new\Hour21\simpleassert . cpp

Az első sorban definiáljuk a DEBUG szimbólu mot. Ea amúgy megtehetjOk n pnranessor-


ból vagy az integrált fejleszt6i környezet segílségével is, ami sokak számám egysze-
rűbb módja a nyomkövetés ki- illetve bekapcsolásának. Az assert () makró mcghatá-
roz.ísát a 7-13 sorok tartalmazzák. Az ilyesmit általában egy fejlécállományba szokás
helyezni, amit esetünkben bizonyára ASSERT . HPP-nek neveznénk és a megfelel ő po n-
lon beszúrnánk a forrásfájIokba.

A 4. sorban megvizsgáljuk, hogy definiált-e a DEBUG szimbólum. Ha nem, akkor


az assert () nem hoz létre senuniféle kódot. Ha azonban a DEBUG definiált állapotll ,
akkor a 7-13 sorokban ta lálható kód bekerül a fordítónak átadou fájlba.
418 VI. rósz'

Maga az assert () makró az előfeldolgozó .számára tulajdonképpen egyellen nagyon


hosszú par.mcs, amit jelen esetben hél rövidebb sorra törtünk az olvlIshatóság kedvé~
ért. A 8. sorban megvizsgáljuk egy paraméterként átadon kifejezés értékér. Ha ennek
a kiértékelése hamis eredményt szolgáltat, akkor II 10-12 sorokban látható utasításokat
hajtjuk végre, amelyek egy hibaüzenetet jelenítcnek mcg. Ha ellenben a makr6nak át-
adolt kifejezés él1éke igaz, akkor semmi sem történik.

Nyomkövetés az assertO makró segftségével


Miközben írunk egy programot, ott belül, mélyen II lelkünkben s:dunos dologról tud-
juk, hogy igaznak kell lennie. Egy függvé nynek az adon szitlllki6ban egy bizonyos ér-
tékel kell vissz~ladnia, egy mUlal6nak érvényes dmCI kell tartalmaznia és így tovább.
Progr.m1()z{lsi hibár61 - többek között - akkor beszélünk, ha egy ilyen "alapigazság" bi-
zonyos IlclyzlJtlJkben mégsem tel jesül. Példáu l pontosa n tudjuk, hogy egy 111uLat6nak
a megfelelő értékkcllwll rendelkeznie, a programunk mégis egy ezzel kapcso1:.nos hi-
bávalleá ll. Az ilyen és ehhez hasonl 6 hibák 111egtalálásában n:lgy segít.ségünkre lehet
az assert () mllkr6, de csak akkor, ha kellően gyakran és :l megfelelő szabadsággal
használjuk az általunk fejleszten kódban. Például valahányszor értékel adunk egy nlU-
tat6nak, vagy paraméterként átadjuk azt egy függvé nynek, ellenőrizzük le
az assert () segíL'iégéve::l, hogy az értéke val6ban érvényes-e. Kicsit általánosabban
fogalmazva vab llányszor o lyan he::lyzetet látunk, amikor programunk működése egy
adott vá ltoz6 adon 61ékétőJ függ, ellen6ri;>;zük le az assert (l-lel , hogy valóhan az a~
énék v:m-e abban a változóban.

Semmiféle káros következménye nincs annak, ha az assert () makrot gyakran hasz-


náljuk. Ha kikapcsoljuk a nyomkövetést (vagyis nem definiáljuk a DEBUG szimb6Ju-
mot), vabmennyi, az assert () -tel kapcsolatos kód e l tűn ik a progr-.Jmból, mintha on
se lett voln:l. A hibakeresés mellett az assert () kifejezései a kód egyfajta dokumcn:...
h'isM is jelentik, hiszen a karbantartást végző pontosan [{nhatja, hogy mely pontokon
mirc kell í.lgyelnie.

A makr6k mellékhatásai
Meg l chc:t őscn gyakori ám eLsőre kissé paranormá lisnak tűnő jelenség, hogy egy hib
csak akkor jck:ntkczik, ha eltávolítolluk a lefordított kódből az assert () makrÓkh.
kllpcsolmos kifejezl!sekel. Ennek csaknem mindig az az oka, hogya programunk rT'~­
köclése nem s7,.ándékosan ugyan, de függ az assert () makrok által végrehajtott m . >
veletek, vagy bármilyen más, hibakereséshez használt kódrészletek mellékhatásait(
Nézzünk egy példát:
ASSER'J' (x - 5)
21. 6ra • Az

lu szinte bizonyosan azt akartuk ellen6rizni, hogy x értéke valóban 5, vagy valami más
(x == 5). Ehelyett elkövettünk egy bosszantó hibát, hiszen az ötöt értékOl adtuk a vál-
tozónak, vagyis még ha eddig nem is 5 volt az értéke, most már az. Tegyük fel, hogy
közvetlenül ez elűÜ az assert () e16tt volt egy függvényhfvás, ami valamilyen hibás
mCiködés folytán nullára állította x értékét. Az assert () kódj[lban sajnos :ítmenetileg
kiküszöböljük ezt a csorbát, hislen visszaállítjuk az 5-ös értéket. Mivel pedig az érték-
adásnak is van visszatérési értéke, mégpedig maga az érték, ezért az x = 5 kifejezés
nem csak beállítja x értékét, hanem az assert () -nek is visszaadja azt. Mivel az 5 mint
visszatérési érték nem nulla, ezért logikai igaznak felel meg, vagyis az assert () azt lát-
ja, hogy - az am6gy teljesen hilúsan megfogalmazott - feltétel teljesü lt. Összességében
tehát elkövettünk közvetlenül egymás után kél olyan hibát, amelyek hatásai kiejtették
egymást. Az x vált07.6 értéke 5 (naná, hiszen mi állítottuk be), tehát a program tökélete-
sen mCiködik, egészen addig, amíg be van kapcsolva a nyomkövetés. Aztán amikor elér-
kezünk a kibocsátáshoz, amikor kikapcsoJjuk a hibakeresésl, jön :l meglepetés.

Most, hogy az ass ert () k6dja már nem kenU bele a lefordított állományba, nincs töb-
bé semmi, ami x értékél 5-re változtamá. A hibás függvény viszont, ami x értékét nul-
lázza ottmarad, tehát progmmunk hibásan fog működni. EZI konstmáJva lennl'slete.';en
viss7.akapcsoljuk a 11yomkövetést, am ire a hiba eltűnik ... Ez az a dolog, amin j6t derül
az ember, ha tudja, mir61 van SZÓ, dc sirni tudna tőle, ha kénytelen ~é Jesben " megélni.
Legyünk lehát óvatosak a nyomkövetéshez használt k6c:lok mellékhatásaiv;J1. Ha pedig
olyan jelenséget találunk , amely kizárólag bekapcsolt nyomkövetés mellett bukk:m fel,
az e lső h'Yan(lsíIOIt maga a nyomkövetéshez haszn:'ilt kód legyen. Vizsgá ljuk meg, hol
lehet az a bizonyos f-urfangos mellékhat.ás ..

Apropó Mellékhatások nagyon sok makr6han felhukkanhalnakl


Amint azt a 21.2 Lista 16. és 17. sorában láthattuk, makr6kat nem csak egyes vál·
tozókkal vagy értékekkel, hanem egészen összetett kifejezésekkel kapcsolatban is
meghívhatunk. (Ilyen történik például az adott kód 9. és 10. sorában.) Ha azonban
ez az összetett kód módosftja egyes változók értékét, annak nem várt mellékhatásai
lehetnek. Tegyük fel például, hogya CUBE makrót az aH kifejezéssel hIvjuk meg:
y = CUBE (aH);
Al ember ezt a sort olvasva úgy gondolná, hogy a értéke csupán eggyel lesz na-
gyobb a makró kódjának végrehajtása után. Ez azonban nem Igy van, mégpedig
azért nem, mert ilyenkor a makró a következó kóddá alakul az elófeldolgozó munká-
ja nyomán:
( (d++) " (a++) * (a++) )
Ez pedig bizony nem egy, hanem három inkrementálást tartalmaz, vagyis egyáltalán
nem az történik, amire a kód olvastán számItunk.
I
410 VI. rész· Különlegességek

Osztályinvariánsok
A legtöbb oszLályhoz rende l hetők olyan logikai feltételek, amelyeknek igaznak kell
lenniük, valahányszor lefuttatl.lnk egy az osztályhoz tartozó tagfüggvényt. Ezeket a fel -
ttteleket nevezzük osztályinvariánsoknak (class invariant). Ha például van egy CI RCLE
nevű osztályunk , akkor enne k egy invariánsa lehet az a fe ltétel, hogy a sugár nem le-
het nulla. Hasonlóan val6szíml, hogy egy az ANIMAL osztályba tartozó obje krumná l
a kor (age) mindig nagyobb mint nulla, de kisebb, mint 100.

Az elmondottak miatt általában hasznos, ha minden ilyen osztályon be1ül létre hozunk
egy olyan Invarinats () nevtl metódust, amely akkor és csak akkor ad vissza igaz ér-
téket, ha valame nnyi osztá lyinvariáns vizsgálata igaz é rté ke t szolgáltat. Ha ugyanis van
ilyen met6dusunk, akkor megtehetjük, hogy minden egyes metódushívás előtt éli utá n
végrehajtunk egy asse'Ct (Invariants ()) hívást, és ezzel ell enőrizzük, hogy minden
re ndbt.!n van-e. Ezze l:) módszerrel természetéb61 ad6dóan azonnal jól behatárolhat6
az esetle ges hib:!. Van persze két o lyan sZitu5ci6, amikor megengedhetó, Ilogy
az I nvari ants () metóduli nem adjon vissza igaz énéket::1 konstruktor, illetve
a destru ktor teljes lefutása e l őtt ne m biztos, hogy :lZ adolt objckrum a megfelel6 álla-
potban van. Az Invariants () metódus használatát a 21.4. Lista szemlélteti egy megle-
hetősen triviális felépítéSll osztállyaI kapcsolatban.

o: II 21 . 4 . Li sta Osztá1yinvariánsok
l : #define DEBlJG
2 : *define SHOW_INVARIANTS
3 : finclude <iostream>
4: tinc1ude <string . h>
S,
6 : tifndef DEBUG
7 : tdefine ASSERT(x)
8 , 'else
9 : tdefine ASSERT(x) \
10 , i f (! (x)) \
ll : ( \
12 : std: : cout « "ERROR! ! Assert • « #x « " f ailed\n' ; \
13 , std :: cout« on line « __ LINE __ « " \n" ; \
14: std :: cout « • in file • « __ FILE__ « • \n' ; \
15 : )
16: tendif
17 :
18: class String
19 : (
20 : public :
21 : II Kons truktorok
22 : String () ;
23 : String(const char ~cons t) ;
24 : St ri ng (conöt String &) ;
25 : -String() ;
26 :
27 : char & operator[1 (int offset) ;
28 : char operator [) ( int offset) const ;
29 :
30 : Std ng & operator= (const String &) ;
31 : int Ge t Le n()const ( return itsLen; )
32 : const c har * GetString() const ( ret urn its String ; )
33 : bool Invariants () const ;
34 :
35 : privaLa :
36 : String (int) ; II private constructor
37 : char · itsString ;
38: unsigned short itsLen;
39 : ) l
40 :
41: II AZ alapértelmezet t konstruktor létrehoz egy csupa. O bájtból á1l6
__ karakter l ánco t
42: String : : String ( )
43: (
44 : itsString =: new char{l) ;
45 : itsString[OJ =: ' \0' ;
46 , itsLen",O;
47 : ASSERT(Invariants(j);
48: )
49 :
50: II Privát (kisegit6) konstruktor . Csak arra használjuk
51 : II hogy segitségével maqának az osztálynak a met6dusai
52 : II létrehozhassák a megfele16 méret~ karakterláncokat .
... Ez u t 6bbiek at nullákkal t ölti f el .
53 : String : : String{i n t l en )
54 : (
55 : itsString = new char[len+1) ;
56 : for (int i '" O; i<=len ; i++)
57: itsString[iJ _ '\0';
58 : itsLen=len ;
59 : ASSERT(Invariants() ;
60 : J
61 :
62 : II K"lra ktertOrnb áta l akitása String os ztá lyba tartozó o bj ektumrná
63 : String : : Str ing (const char * const cStr ing )
64 : (
65 : itsLen -' strlen{ cS t ring) ;
66 , it!lS t ring = new char[itsLen+1) ;
67 : for (int i = O; i<it sLen, i ++ )
68 : itsString[i) '" cString(i) ;
69 : itsString[itsLen ]='\O' ;
70 : ASSERTtlnvariant.s() l ;
71 : l
72 :
73 : II Máso16 konstrukt o r
74 : String : :String (const String & rhs l
75 : {
I
422 VI. rész • Külön~gességek

76 , it sLen~ r hs.GetLen();
77 : itsS tring :: new char(it.sLe n+ll;
78 : for ( int i = O; i<itsLen;i-H- )
79 : itsSt ring[i] = rhs[i l;
80 : i tsStri ng[its Len ] = ' \0 ';
81 : ASSER"{Invariants()) ;
82 : )
83 :
84 : II Destruktor : felszabadí tja a dinamikusan lefoglalt memóriát
85 , String :: ·String ()
86 , (
87 , ASSERT(Invariants();
88 : delete [l itsString;
89 : itsLen - O;
90 :
91 :
92 : II Az cgycn16ség operátorának túlterhelése. Felszabadítja a meg!ev6
93 : II 11lel1L6riát , majd lemásolja a karakterláncot és a méretet
94 : String& String :: operator=(cons t St ring & rhs)
95 : (
96 : lISSER"(Invariants();
97 : i f (this :. ... &.rhs)
98 : retwrn *this;
99 : de!ete lj itsString ;
100 : itsLen."rhs .eetLen () ;
101 : itsString :: new char(itaLen+l] ;
102 : for (int i " O; i<itsLen ; i++)
103 : i tsString [i]" rhs]il :
10 <1: itsString[itsLen l - ' \0 ';
105 : ASSERT(I nvariants(» ;
106 : return *th Ls ;
107 :
108 :
109 : 1/ Nem állandó eltolási (offset) operátor.
110 : II Egy karakter hivatkozásával tér vissza, amely igy rajta
lll : 1/ keresztül módositható .
112 : char & String: : operator]] (int offset)
113 : (
114 : ASSERT{Invariants(» ;
115 : i f (offset> itaLon)
1 16 :r eturn itsString [ i t sLen - ll;
1 17 :else
1 18 :re turn itsStr ing [offset] :
119 : ASSERT(Invaria nts(»;
120 : }
121 :
122 : II Állandó eltolási (offset) operátor. Const objektumokkal
123 : II kapcsolatban használható (lásd a konstruktort!) .
124 : char String :: operator [] ( int offset) const
125 : {
126 : ASSERT(Invari ants () ) ;
127 : if (offsc:t > itsLen)
12 8 :return itsString [ i t sLen - 1 ] ;
129 : else
130 : return itsStr ing!offset] ;
131 : ASSERT (Invariants(» ;
132 : }
133 :
134 , II Meggy6z6dünk róla, hogy vagy van a karakterláncnak érvényes
135 : II hossza , i l l etve a mutat6 ért éke nem null , vagy hogy a hos s z is
.. és a mutató é rt éke is nulla egyidejIlleg .
136 : bool String :: lnvariants(} const
131: {
138 : *i fdcf SHOW_INVARIANTS
139 : std :: cout « • String OK
140 : fendif
Hl : return ( (il::sLen 6o6o itsString) II (!itsLen 6o6o !itsString) );
142 :
143 :
144 : class AnimaI
US :
146 : public :
147 : Animal{) : ltsAge(l),itsName('John Q. AnimaI')
148: (ASSERT(Invariants () } ;}
149: AnimaI (int, const String&);
150 : _Animall){}
151 : int GetAgel) ( ASSERT(Invariants() ; rcturn itsAgc;}
152 : void SetAge(int Age)
153, (
154 : A~SERT(lnvaria n ts()) ;
155 : itsAgc :: Age;
156, ASSERT(Invarianta();
157, J
158 , String&: Ge t Name I) ( ASSER'l' ( I nvarian t s () ) ; return i t DName; )
159 : void SetNamelconst String& name)
160 , I
161 : ASSERT(Invariants(»);
162 : itsName _ name ;
163 : ASSERT(Invariantsl» ;
164 : I
165 : bool Invariants () ;
166 : private :
167 : int itsAge;
168 , String itsName;
169 , );
170 :
171 , AnimaI : : Animallint age . const string& name) :
172 : itsAge(age) ,
173 : itsName (name)
174 : (
175 : ASSERT(Invariants()};
176 : )
177 :
178 : bool AnimaI :: I nvaria nt s ()
179 : {
180 : lifdef SHOW_tNVARIANTS
181 : std : : cout « • AnimaI OK
182 : !l e nd i!
424 1VI. rész • Különlegességek

163 : return (itsAge > o && itsName.GetLen (» ;


184 :
185:
186 : int main ()
181 :
188: AnimaI sparlcy(S, 'Sparky');
189 : std : : cout « "\n" « sparky . GetName{) . GetString() « • is ";
190: std: :cout « sparky.GetAge{) « • years old,";
191 : sparky.Set.Age(B) ;
192 : std : : cout « "\n' « sparky . GűtName() .GetString() « • is ';
193: std: :cout « sparky .GetAge{) « • years old,' ;
194 : roturn O:
195:

String OK String OK String OK String OK String OK String OK


String OK Stri ng OK AnimaI OK String OK An ima I OK
Sparky is AnimaI OK 5 years old . Anima! OK AnimaI OR AnimaI OK
Sparky is Animal OK 6 years old . String OK

Aprop6 A fordft6programt61 figyelmeztető üzeneteket kaphatunk


A fenti program fordítása során az itt látottakhoz hasonló figyetmeztetö üzeneteket
kaphatunk. Ezek azt átlftják, hogy az assert () -tel kapcsolatos kódrészletek néme-
lyike soha nem kerül végrehajtásra. Ez a kód tehát ebben a formájában kissé fur-
csának túnhet, de megfelel egy amúgy követendő szabálynak: minden függvény
elején és végén van benne egy assert () .
·usinginvariants.cpp": W8066 Unreachablc code in function
String: :operator [] (int) at line 119

"usinginvariants.cpp· : W8070 Function should rcturn a value


in function String :: operator [l (int.) at line 120

·u~inginvariants . cpp" : W8066 Unreachable code in function

String: : operator II (int) const at line 131

·usinginvariants .cpp· : W8070 Function should return a value


ln function St ring : : operator [ J (int.) const at line 132

Az assert () makró kódját a 6-16 sorok tartalmazzák. Ha a DEBUG szimbólum meg..


rozott, a makr6 hibaüzenetet jelenít meg, ha a neki áeadott kifejezés h<lmisra értékel
ki. A 33. sorban találjuk <I String osztályhoz cartozó Invariants () metódus de~
óját. A hozzá lanmn definíció a 136-142 sorokban láthat6. A konstruktor deldarnci.
a 22-24 sorokban lalálható, a 47. sorban pedig, miután az objektum már teljesen
meghívjuk az Invariants () metéxlust, amely ellen6rzi a folyamat helyességéL
21. óra •

Ugyanez a minta isméll6dik meg a másik konstruktorral kapcsolatban is. A destruktor


ezzel szemben csak az el6tt hívja mcg az Iovarinals () metódust, micl6tt megkezdené
az objektum lebontását. Az összes többi, az osztályhoz tartozó tHgfüggvény futásának
megkezdése előtt, és a m(iveletek befejezése után is ~önellenőrLés" végez, vagyis meg-
hívja az Invariant s () -t. Mindez jól demonstrálja a c++ egyik alapetvét:
a konSLruktoroktól és destruktoroktóJ eltekintve a tagfüggvények csak érvényes állapot-
ban levő objektumokon m01<.ödhetnek, és azokat érvényes állapotban is kell hagyniuk.

A 165. sorban látható al Anima I osztály saját Invariants () metódusának deklaráció-


ja , amelynek megvalósítása a "178-184 sorokban olvasható. Figyeljük meg, hogya 148,
ISI , 154, 156, 161, és 163 sorokban inline függvények formájában hívjuk meg
az Invarian t s() metódust.

Köztes értékek kifratása


Amellett, hogy az assert () makró segítségével ellenőrizzük bizonyos feltételek telje-
sülését, néha szükség lehet arra is, hogy menet közben megjeleníl.sÜk egyes változ6k,
mUlatók vagy karnklerláncok aktuá lis tarl:llmát. Ezzel például nyomon követhet{5 prog-
r,ununk végrehajtása, és kisztirhet6k olyan apró ám bosszantó hibák, mint az eggyel
túlfutó ciklusok A módszer működését a 21.5. Lista szemlélteti.

21.5. Usta - rtékek kilratása DEBUG módban (printingvalu8s,cpp)


Q : / I 21 . 5 . Értékek kHrat.ása DEBUG módban
1 : 'inc1ude <iost.ream>
2 : 'defi ne DEBUC
3,
4: ~ i fnde f DEBUC
5 : #dcfi ne PR J N~ ( x )
6 : " else
7 : "d ef ine PRINT( x ) \
8 : std :: cout « "x « ": \t" « x « st.d :: endl :
9 : tendU
IQ :
lt : int mai n ()
12 :
13 : int. x " 5 ;
14: loog y ~ 738981 ;
15 : PRI NT {x ) ;
16 : for (int i " O; i < x ; i ·H )
17 : {
18 : PRI NT{ i ) :
19 : )
20:
21 : PRINT (y) ;
22: PRINT{'Hi . " ) ;
23 : i nt. ~px " &x;
24: PRINT (px ) ;
426 1VI. rész • Különlegességek

25: PRINT (*px) ;


26: return O;
27 :

"i: o5
i: 1
i: 2
i : 3
i: 4
y. 73898
"HL", HL
px : 1245064
" px : 5

A 4-9 sorokban megadOll makr6 kiírja a neki átadott p:lmmt:tcr énékét. Figyeljük meg.
hogya cout-nak átadou els6 dolog a paraméter karakteresítct[ (stringized) változata.
Ez tehát aZt jelemi, hogy ha paraméterként mondjuk az x v,íhoz6t admk ál, akkor
a cout elóször egy 'x " kar,.lk((:!rJúncot kap.

A couL következő param6tcrc minden esetben egy": \t" braklerlánc, :uni kiír egy
kett6spontol, majd utána egy tabulálort A harmadik maga a kiírandó vá lloZÓ (jelen
esetben x), majd végül egyendl, ami kiüríti a puffert és kiír egy újsor karaktert.

A 24. sorban látható kód vagy egy akimenetben bemutatotthoz hasonló értéket ír ki
(ilyen a Borland fordítóv:ll el6:'illrtot.l kód), vagy olyasmit mint a Ox2100 . Ennek a sor-
nak" működése tehát fordítófügg6.

Nyomkövetési szintek
A kifejezeuen nagy és összetett fejlesztési projekleknél az embernek általában töbh
a nyomkövetés.~e1 kapcsol:ltos ellen6rzési lehet6ségre van szüksége annál, mint ho;"\
a DEBUG szimb6lumot ki és bckapcsolhatja. Ilyenkor megadhanIOk nyomkövetési .;,
teket, és az el6feklolgoz6 ezek vizsgálat.1 alapján döntheti el, hogy a makrók közü
melyek kerüljenek bele alefordítandó kódba.

Ha nyomkövetési szinteket akanmk meghatározni, nincs más dolgunk, mint a C~


után megadni egy számot is. Ha már szinteket adunk meg, általában hasznos, ha ..
vetkez6 négyet különböztetjük meg: HIGH, MEDIUM, LOW és NONE. A 21.6. Lista a ~~
21. óra •

Listában bemutaloU String és AnimaI osztályok kapcsán mutatja be a nyomkövelési


szimek használatál. Helytekerákosság nliatt kihagyruk minden , az Invariants () -l61
különbözo metódus kódját, mivel azokat a változás nem érinti.

21.6. 1JsbI- A nyomkiiYet6slZintjei ldebugginglev....cppl


o : II 21.6 . Lista NyomkOvetési szintek használata
1 : linclude <iostream>
2 : linclude <8tring.h>
3.
4, enum LEVEl. { NONE, LQW, MEDIUM , HIGH } ;
5.
6 , ~define OEBUGLEVEL !iIGli
7.
8 , hf OEBUGLEVEL < LQW 1/ Csak LOW , MED IUM vagy HIGH lehet az értéke
9 , ~define ASSBRT(x)
10 : #elsQ
ll : #define ASSERT(x) \
12 : i f (! (xl) \
13 : ( \
14 : std : : cout « "ERROR!! ~8sert " « #x « " failed\n"; \
15 , std : , eout « on line «_LINE_ « " \n "; \
16, std : ,cout « " in file" « _FILE_ « "\n"; \
17, l
18, 80ndif
19 :
20: _if DEBUGLEVEL < MEDIUM
21: 'do fine EVAL{x)
22: 'else
23: 'doHne EVAL{x) \
24: std::cout« Ix« ',\t"« x« std " endl;
25: 'endH
26:
27, 'if DEBUGLEVEL < IiIGli
28: #define PRINT(x)
29 : ~e1se
30 , IIdefine PRIN'I'{x) \
31 : std : , cout « x « std : : endl ;
32 : llendif
33 :
34 : class String
35 : {
36 : public:
37 : II Konstruktorok
38 : String{);
39 : String{const char · const) ;
40: String(const String &) ;
41: -String () ;
42 ,
43 : char & operator[ l (int offset) ;
44 : char operator[] (int offset) const ;
45 ,
46: String & operator= (const String &);
428 1VI. rész • Különlegességek

47 : int GetLen{)const ( r e turn itsLen:


48: const char * GetSt ring () const
49 : { return itsString ; }
50: bool Invariants() cons t;
51 :
52 : private :
53 : String (int); / / Privát konstruktor
54 : char * itsString :
55 : unsigned short itsLen ;
56 : );
57 :
5B : bool String : : Invariants() const
59 : (
60 : PRINT( " (String Invariants Checkad)") ;
61 : return ( (bool) (itsLen && itsStrin~J) II
62 : ( ! itsLen Ct& ! ltsString) );
63 : }
64. :
65 : c1t1ss AnimtIl
66 : (
67 : public :
68 : Animal() : itslIge(I),itsName('John Q . AnimaI")
69 : (ASSERT(Invariants(» ; }
70 :
71 : Animal(int, const String&) ;
72 : _Anima1() II
73:
74 : int GetAge{)
75: (
76 : ASSERT(Invariants(»);
77 : return itsAge;
78 : )
79 :
BO : void Setllge(int Age)
B1 :
82 : ASSERT(Invariants(» :
B3 : it9Agc '" Age ;
84: lISSERT(Invariants(»;
BS : }
86 : String& GetName()
87 : (
BB : ASSERT(Invariants(» ;
89 : return i.t s Name ;
90 :
91 :
92 : void SetName(const Str ing& name )
93 : (
94 : ASSERT(Invariant s(» ;
95 : itsName '" name ;
96 : ASSERT(Invariant9(»;
97 : }
98 :
99 : bool Invariants() :
21. óra· Az

100 : private:
101 : int itsAgc ;
102 : String itsName;
103: };
104 :
105 : II Az alapér t elmezett konstruktor O bájtokból áll6
.. karakterláncot hoz létre .
106 : String :: String ()
107 : {
108: itsString : new char]l]:.
109 : itsString[O) =. '\0' ;
110: itsLen .. O;
lll, ASSERT(Invariants () ) ;
112: )
113 ,
114: II Privát (kiscgit61 konstruktor. Csak arra használjuk
115, II hogy segitségével magá ntlk <IZ osz t álynak a mctódusai
116 : II létrehozhassá k ti megfelel6 méret~ karak terláncoktit .
.. Ez utóbbiakat nullákkal tOlti fel .
117: String : : String(int len)
118: {
119 : itsString = new char[len+1];
120 : tor (int i = O; i<_len; i++)
121 : itsString[i) = '\0';
ll!2 : itsLcn.1en;
123 : ASSERT(Invariants(» ;
124 : }
125 ,
126 : II KaraktertOmbOt String tipusú ob jektummá alakit .
127 : String : : String(const char * const cS t ring)
128 : (
129 : itsLen .. strlen(cString);
130 : itsString = new char[itsLen+ll;
131 , for (int i " O; i<itsLen; iHol
132 : itsString[i) = cString-[i};
133 : itsString[itsLen}= ' \O' ;
134 : ASSE:RT(Invariants{);
135 : )
136 :
137 : II Másoló konstruktor
1]8 : String :: String (const Stri ng & rhs)
139 : (
140: i tsLen=rhs.Getl.en();
141: itsString = new char]itsLen+l[;
142 : for (int i = O; idtsLcn;i++)
143 : itsString]i] = rhsli};
144 : itsString[i tsLen) = '\0';
145 : ASSE:RT(Invariants());
146 : }
147 ,
148 : II Destruktor . Felszabaditja a dinami kusan lefoglalt memó riát .
1 49 : String : : _S tring ()
150 :
I
430 VI. rész • Külön~gességok

151 : ASSER'l'(Invariants(»;
152 : de!ete I I it sString ;
153: itsLen "- O;
154 : J
155:
156: /1 Az egycn16ség operátorának túlterhelése . Felszabadítja
157: 1/ a meglev6 memóriát, majd lemásolja a karakterláncot
... éG a méretet .
158: String& St ring: : operator=(const String & rhs)
159: (
160: ASSERT(Invariants(» ;
161: iC (this == &rhs)
162: return ~th is ;
163 : delete [l itsString ;
164: itsLcn=rhs ,GetLen () ;
165: itsString :: new char[itsLen+11;
166: for (int i = O; i<itsLen ; i+-+-)
167, itsString(i] " r-hs[i] ;
16 8 : itsString[itsLenl = '\0';
169: ASSERT(Invariants(» ;
170 : rcturn *this;
171 :
172 :
173 : II Nem állandó eltolási (offset) operátor. Egy karakter
174 : il hivatkozásával tór vissza, ame ly igy rajta keresztal
175 : il m6dosithat6 .
176: char & String: : operaLor[ ] (int offse t)
177 : (
178: ASSERT(Invariants(»):
179: i f (offset> itsLen)
180: return itsString[itsLen-1):
181: else
182 : return i tsString[offset l;
183 : ASSER~(Invariants()) ;
18 4: )
185:
186: II Allandó eltolási (o ffset ) operátor . Const objektumokkal
187 : II kapcsolatban használhat6 (lásd a konstruktort!).
188: char String: : operator[) (int offset) const
189 : (
190 : ASSERT(Invariants());
191 : it: (offset> itsLen)
192: return itsString[itsLen - 1 ];
193: else
194: return itsString[offset) ;
195 : ASSERT(Invariants();
196: )
197 :
198 : AnimaI : : Anima1 (int age , const String& name) :
199 : itsAge( age),
200: itsNallle(name)
201 : {
21 , 6ra • kl 431

202 : ASSERT(lnvariants(» ;
203 : }
204 :
205 : bool Animal :: Invariants ()
206 : {
207 : PRINT ( • (AnimaI Invariants Checked) ') ;
208 : return (itsAge > O && itsName.GetLen{» ;
209 :
210 :
211 : int main()
212 :
2 13: const int AGE = 5;
214 : EVAL(AGE) ;
215 : AnimaI sparky(AGE . 'Sparky') :
216: std : : cout « " \n' « sparky . GetNarne() . GetString() ;
217 : std :: cout«' is ' I
218 : std :: cou t «sparky . GetAge() « • years old . ' ;
219 : spnrky . SetAge (8) ;
220 , std: : cout « ' \n " « sparky .GetName( ) . GetString() i
221 : std :: cout « ' is' ,
222 : std :: cout « sparky .GctAgc() « • years old ."
223 , return Oi
224 :

AGE: 5
(String Invariants CheckedI
(String Invariants Checked)
(String Invariants CheckedI
(String Invariants Checked)
(String Invariants Checked)
(Stri ng Invarianto Checked)
(String Invariants Checkcd)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Chec~ed)
Sparky is (AnimaI Invariants Chec kedl
5 Ycars old . (AnimaI Inv",ri",nts Checkcd)
(AnimaI Invarisnts Checkcd)
(Animul Invariants Checked)
Sparky is (lmirnal Invaria nts Checked)
8 years old . (String Invaria nts Checked)
(String Invariants Checkcd)
II run again with DEBUG : MEDIUM
AGE : 5
Sparky is 5 years old .
Sparky is 8 years old .
432 1VI. rész • Különlegességek

Apropó A fordftáskor figyelmeztető üzeneteket kaphatunk


Ennek a kódnak a fordítása közben is felb ukkanhatnak ugyanazok a figyel meztető
üzenetek, a melyekről a 21.4. Lista kapcsán már volt szó. Az. ok természetesen
ugyanaz.

Elemzés

Az assert () makró k6dját a 8-18 sorok tartalmazzák. Ez ebben az esetben úgy van ki-
ahtkítva, hogy Im a DEBUGLEVEL énéke alacsonyabb mint LOW(vagyis NONE), akkor
a nyomkövetési kódok egyáltalán nem kerülnek bele a fordítandó állományba.
Ha a nyomkövetés bármilyen szinten megengedett, akkor az assert () ml1ködésbe
lép. A 20-25 sorokban híthat6 EVAL nem kerül bele a k6dba , ha DEBUGLEVEL énéke
alacsonyabb mint MEDIUM. Ez tehát azt jelenti, hogy ha DEBUGLEVEL énéke NONE vagy
I.OW, akkor EVAL nem kerül bele a k6dba.

Végezetül a 27-32 sorokban látható PRINT makró aktjválásá hoz DEBUGLEVEL HIGH ~r­
léke szükséges. Ez tehát azt jelenti, hogy cz a makró még a MEDIUM nyomkövetési szin-
ten is hatást.dan , miközben ezeken aszinteken EVAL és assert () már ml1ködnek.

A PRINT makrót az InvarÍ<!l.n ts () metódus kódjában haswáljuk egy üzenet kiíratásá-


ra . Az. EVAL a 214. sorban bukkan fel , ahol egy AGE nevO egész állandó ;tktuális érték(:t
vizsgá lja meg.

Helyes Helytelen
A makrók neveiben használjunk csupa Igyekezzünk olyan makrókat írni,
nagybetúl. Ez egy álcalánosan elfoga- amelyeknek egyáltalán nincsenek
dotl é... alkalmazott szokás, vagyis ha mellékhatási. Ne inkrementáljunk
ncm taJtjuk hozz:'i magunkat, azzal például makrón belül változót, vagy
összezavarhatjuk a többi programozót. ne adjunk értéket neki.
A makfÓfü&,>vények valamennyi para-
méterét tegyilk kerek záfÓjelek közé.

Kérdések és válaszok
Kérdés: Ha a C++-nak vmmak az előfe/dolgozóI/ál jobb szolgáltatási, akkor miél1 Vim
Illeg még mil/dig benne ez ff lehelőség is?

\lálasz: El6ször is a C++ visszafelé kompatibilis a C nyelvvel , vagyis annak lninden je-
lent6s szolgáltatását tartalmaznia kell. Ilyen az előfeldolgozó is annak minden direkti-
21. óra • Az 433

vájával és makrójával. Másodszor az elófeldolgozónak vannak olyan szolgáltatásai,


amelyeket a CH programozók is előszeretettel és gyakran használnak. Ilyen például
a beszúrási műveletek ellenőrzése (inclusion guards).

Kérdés: Miérl használ/mk makfÓJrigguényeket, ha ugyanaz a Jeladatot igazi fiiggtJé-


nyekkel is megoldJ/aljuk?

Válasz: A makr6függvények nem igazi függvé nyek, mivel aZ el6feldolgozó fejti ki és


szúrja be őkel a megfelelő helyre. Ezek tehál tu lajdonképpen a kódolást könnyítő esz-
közök, hiszen nem kell újra és újra begépclnünk ugyanolyan, vab'Y nagyon hasonló
kódokat. Ugyanakkor az is igaz, hogy a sablonok jobb alternatívát jelentenek.

Kérdés: Mi~)ICH más módszer vagy cszk6z áll a a rendelkezésüllkre az előfeldolgozó


helyett, ha hlbakercsés I.:ózbcll változók k6ztes énekeit szere/lléllk kiírallli.?

Válasz: A legjobb alternativa a megfigyelő


lltasítások (watch statements) használata
egy nyomkövet6prograrnban. Ezzel kapcsolatban olvassuk el a fordítóprog~mur1khoz
mellékell nyomkövető dokumentációját.

Feladatok
Mo.st, hogy megismerkedtünk az előfeldolgozó szolgáltatásaival és fel használási módo-
zataival , megszerzett tudásunkat meger6sítendő válaszoljunk néhány kérdésre és vé-
gezzünk el néhány gyakorlatot.

Kvrz
1. Hogyan törölhelünk makr6változókal?
2. Mikor kerülnek feloldásra a makrók, illetve lnikor kapnak értéket a szimbólumok?
3. Hogyan alakíthatjuk karakterlánccá egy makró valamely paraméterét?
4. Milyen operntorra l fúz!1etünk össze karakterláncokat egy makr6ban?

Gyakorlatok
1. Mooosít.suk a 20. óriiban bemutatott employeernain . cpp programot (20.4. Lista)
úgy, hogy az kétszer szú~a be a string . hpp fejlécállományt. (Ismételjük meg
az elsó son.) Mi törll!nik fordításkor?
2. Alkalmazzuk az ebben az órában megismert módszert a string. hpp fej ll!cá llo-
mány beszúrásának vezérlésével kapcsolat Onclusion guards). PfÓbáljuk meg új-
ra lefordítani az e l őző gyakorlatban módosítorr kódot. Mi történik?
434 VI. rész •

3. A dokumentáció segítségével delÍt50k ki, hogyan adhatunk értékel egy


makróváltoz6nak a parancssorban. Írjunk egy olyan progrdmot , ami kiírja a kép-
erny6re a makr6nak így áradott értéket.

Válaszok a kvfzkérdésekre
1. Makr6kat az fundef direktíva segítségével lehet törölni. Ez a módszer egyaránt
mJködik a magában a kódban, illetve a parancssorban megadott makr6kkal
kapcsolatban.
2. A makr6k kifejtése (vagyis a forráskód megfelel6 módosítása) fordítási id6ben
történik. Ez azt jelenti, hOb'Y az így beállított értékek később, furásid6ben már
nem m6dosíthat6k, hiszen azok abban a köztes k6dba kerültek be, amit a fordí-
tóprogram az el6feldolgoz6t6! megkapott.
3. A paraméterek a karakteresít6 (stringizing) operátor (It) segítségével alakíthat6k
br:akterlánccá. Ez tulajdonképpen csak annyit tesz, hogy kelt6s idéz6jelek közé
z{lrja a kérdéses argumentumot, akármi legyen is az. Ezzt::l a m6dszerrellerrné-
szetesen mi magunk is élhetünk. Bármi, amit a makr6 k6djában kett6s idézőjele
közé les7.ünk attól kezdve konstans lesz, vagyis ·x" már nem magának az x vál-
tozónak a tartaimát jelenti, csak egy betűt.
1. Az összeli1zés operátora a U.
22. ÓRA

Objektum-orientált elemzés
és tervezés
Ebben az órában a következőkról lesz szó

• Hogyan elemezzünk problémákat objektuffi.orientálr szemszögb61


• Ilogyan tervezzük meg a progmmunk:!.l objektum-orienl:'ilt szemsz("gb61
• Áttekintjük az elemzési folyamatot, illetve ezek átültetését C++ nyelvre a terve-
zési célokat szem előtt tartva
• Hogyan telvezhetí.\ nk bővíthető és újra hasznosítható kódot

A fejlesztési ciklus
Szá mos könyvet és összefoglalót írtak már a fejlesztési ciklusr61. Néhány a vízesés mo-
dellt támogatja, ahol a tervező határozza meg, mire legyen képes a program, a mérnök
határozza meg, hogyan épüljön fe l az, milyen osztályokat tartalmazzan , és így tová bb,
436 1VI. rész • KOIönlegas.égek

végűl a programozó felada ta az í!,'Y kialakult tervet és architektúmt egybefonni. A prog-


ramozó ennél a megközelítésnél már a kész tervet kapja meg, így csu pán annyi a fel-
adata, hogy megvalósítsa az abban leírt funkciókat.

Noha a vizesés modell kétségkívül működik, azért egymagában val6színúleg elég szegé-
nyes lenne igazán jó programok írásához. Fejlesztés közben van egy tennészetes és szük-
séges vissL1C5.1lolás a m.ir megírt, illetve a még megírásra váró programrészek között.
Az tény, hogy a jó C++ programokat alaposan megtervezik, mielőtt elkezdenék az érdemi
programoList, azonban az már nem igaz, hogy a telV nem változik a ciklus alan.

Hogy mekkora munkát igényel az előzetes tervek elkészítése, az természetesen a prog-


ramunk mé re t étől függ. Egy igen bonyolulL projektnek, melyen tucatnyi programozó
hónapokig dolgozik, nyilván világosabb, kidolgozottabb telVVel kell rendelkeznie,
111il1t egy ujjgyakorlalként megírt segédprogramnak, melyet egy nap ~I l att egy progra-
mozó kof:s7.Ítell el.

Ebben az órúban olyan nagy és összetett programok Ic jlcszl(:sére konccnlriUunk , me-


lyeket esetleg évekig fogunk bővíteni és fejleszteni. Szftmos progrmnoz6 egyszeruen
élvezi, ha a:wkkal .t legú jabb technológiákkal dolgozhat, melyek tudása és segédesz-
közci határd it rcszegetik. A CH nyelvet sok tekintetbe n éppe n arm tervezték, hogy se-
gíl<;égével egyre bonyoluhabb és bonyo lultabb rendszereket fej leszthessenek a progr.l-
mozők VlIgy progmmozói csapatok.

Egy riaszt6rendszer szimuláci6ja


A szimuláció egy a valódi vikig egy részének leírására alkotott számítógépes model.
Számos o kból szoktunk szimuláciőhoz folyamodni. Egy jó tervhez e l őször is meg ke:
értenünk azokat a kérdéseket, amelyekre választ vántl1k a szimu lációt61.

Kiindu lásképpen vizsgáljuk meg a következő problémát: relkértek minket, hogy


láljuk egy ház riasztőrendszerét. Egy kétszintes családi házról van sz6, mely egy k ....
ponti tér köré rend(:!zelt négy hál6szobával, berendezett alagso Tml és egy rnélyga-
rázzsal rendelkezik.

A rölclszinlcn a küvetkező helyeken találh.m6k ablakok: Itárom a konyhában, né~


az ebédlőben , egy a WC-ben, kettő-kettő a nappaliban és a t{lrsalg6ban, valamint '.c"
ablak a bejámti ajtó mellett. Négy hálószoba található az emeleten. Minden hál~
két ablaka van , kivéve fő hál6szobál, ahol né!,'Y található. A két fü rdÓSZOIYd egy-e:;:\
lakkal rendelkezik. Végűl négy kis ablak található az alagsorban, egy pedig a gan"".
A lakók els6sorban a bejárati ajtót használják közlekedésre. Emellett a konrha
egy elhúzható üvegajtó, valamint két ajtó a garázson az autó knak, illetve egr
al. alagsorhoz. Van továbbá egy pinceajtó, amely a hálsbkertre nyílik.
22, óra • elemzés és tervezés 437

Az ablakok és az ajtók riasztóval védenek, emellen minden telefonon van vészjelző va-
lamint egy nyomógomb közveuenül az ágy mellett a f6 hálószobában. Kültéri riasztók
is va nnak, de ezeket körültekint6cn állitouák be ahhoz, hogy kis testű állatok vagy ma-
damk ne indítsák be őket.

A riasztórendszer központja aZ alagsor!>an található, mely azonnal figyelmeztető jelzést


ad, amint vabmclyik szenzor jelez. Amennyiben nem kapcsoljuk ki a riasztót a meg-
adolt időn belül, úgy telefonon értesíti a rendőrséget. Ha megnyomjuk a vészjelzőt ,
a rendszer azonnal értesíti a rendórséget.

A riasztór.1 vannak kötve még a túZ és füst jelzők valamint a tűzoltórendsze r is. Maga
a riasz16rcndszer központj:! hibalűró, mely a heépített tartalék áramforrásnak és a tűz­
álló burkolatnak köszönhető.

Koncepcióterv
Ebben a fázisban megpróbáljuk megéneni, mit vár a megrendelő a programt61. Mi
:lcélja a progr:tmnak? Milyen kérd~sekre kellene a szimu lád6nak v;'i]aszt adnia? Megfe-
lelőek-e az alábbi kérdések: .,A szenzor jelzése után mennyi időve l riasszan a rend-
sze r?~ vagy . Kijátszhat6ak-e az ;ibl;lkriasztók úgy, hogy nem fusson lx: l:rtcsítés a rcnd-
őrség hez?~

A koncepci6terv készítése megfelel6 id6szak arra, hogy átgondoljuk, mi van a progra-


mon belül és mi van kívül. Szerepel a rcnd6rség a SZi111Uláci6blm? Hésze magának
a szimulált rendszernek:l:l húz rias%l6renuszerének vezérlése?

Elemzés és az igények felmérése


A probléma megfogalmazásának fázisa után az elemzés fázisa kövelkezik. Elemzés
közben feladalunk segíteni a megre lldelőt, hogy mcgérLo.;e, mit is v~rhat egy ilyen prog-
n!mtól. Pontosan milyen viselkedést mutasson a progmm? Milyen beavatkozási lehet6-
ségei lesznek a felhasználónak?

Ezeket az igények rendszerint dokumentumokba gyűjtjük össze, amelyek felhasz nálási


e.settanulmányokat is tanalmazhatnak. Használati eseldiagr.:lm írja le a program viselke-
dését: a kölcsönhatásokat és a használati mintákat, segítve ezzel a programozót a rend-
szer céljainak megértésében.

Tudta hogy...? Egységes modellez6 nyelv (Unified Modeling language - UML)


Az igények felmérésnek. és az elemzés dok.umentálásának egyik. lehetséges módja
az egységes mode!tező nyelv (Unified Modeling Language; UM L) használata.
438 1VI. rész • Különlegességek

Az. UMl hasmálatának két elónye is van: grafikus, vagyis könnyű áttekinteni, illetve
egységesített formátumot használ, így nagyon sokan - akár nem szakértők is -
megértik. Noha az UML bemutatása meghaladná a könyv terjedelmét (a témában
már számos kiváló könyv jelent meg), tudnunk kell, hogya használati esetek bemu-
tatását is lehetövé teszi. Objektum-orientált fejlesztéshez különösen hasznos, hiszen
számos absztrakt és egyéb osztálytípust támogat közvetlenül.
Jó forrás a témában: Teach Vourself UMl in 24 Hours, maly a Sams kiadásában je-
lent meg.

Magas és alacsony szintű tervezés


Miután leljesen megértettük a program célját és az igényeket a megfelel6 dokumen-
tumban rögzítettük, hozzákezdhetünk a magas szinn1 telVezéshez. Ebben a fázisban
a programozó nem foglalkozik a pl:nformmal, az oper.'iciós rendszerrel, vagy a progra-
mozási nyelv saj{ttságaival. Ehelyett a rendszer működésére koncentrál. Azt vizsgá lja,
mik annak a főbb elemei, illetve hogy milyen kölcsönhatás les7. közötlük.

A probléma egy lehetséges megközelítése az, hogy félrelesszük a felhaszná l6i felület
kiabkít:'Ísának kérd6scit és csupán a problématér komponenseire koncentrálunk.

A problémalér azon problémák halmaza, melyeket a programunkkal megpr6bMunk


megoldani. Hasonló:tn a megoldástér a lehetséges megoldások halmaza.

Ahogy haladunk a magas szi ntO tervezéssel, végiggondoljuk az objektumok felad:!lai!.


Mit csinálnak és milyr.:n információkat tartalmaznak? Természetesen végig gondoljuk
együtt működésüket is. Milyen objektumokkal vannak kölcsönhatásba n?

Például világos, hogy vannak különböző érzéke16ink, egy központi riaSZl6rem.lsze-


runk, gombjaink, vezetékeink és tdefonjaink. Tovább gondolva a dolgot szimu1:ílnunk
kell szobákat, a padlót és emberek csoportjait, num például a tulajdonosokat va&'Y
a rend6rséget.

Az érzékelők tovább bonthat6ak mozgás, betörés, hang és füstérzékelőkre, stb. Ezek


érJ.:ékelótipusok, szó szerinti é rzékelő nincs. Világosan látszik tebát, hogy az l!rzékel6
egy absztrakt adattípus (Abstract Data Type; ADT).

Absztrakt adattípusként a Sensor osztály biztosít interfészt az összes érzéke16típushoz


és minden származt,HOlt típus saját implememációval rendelkezik. A kliensek anélkül
dolgozhatnak az érLékel6kkel, hogy rudnák milyen típusú, mégis mindegyik a típusá-
nak megfel elően fog viselkedni.

Egy jó absztrakt adattípus létrchozásához teljes mértékben meg kell értenünk, mit csi-
nálnak az érzékelők - hogy ezt hogyan teszik, az számunkra most lényegtelen. Például
az érzékelők aktív vagy passzív eszközök? Várna k valamely elemük fel melegedésére,
egy vezeték szakadásám, a tömítés olvadására , vagy esetleg szondázzák a környezetü-
ket? Néhány érzékel6 lehet bináris (riaszt vagy sem), de néhány köztes állapotokat is
felvehet (hány fok meleg van?). Az absztrakt adattípus interfészének megfelel6en tel-
jesnek kell lennie, hogy kezelni tudja ezeket a számtalan származtatott osztályban.

Egyéb objektumok
Ahogy haladunk a tervezéssel , számos egyéb osztálym is szükségünk lesz, hogy meg-
feleljünk a spcdfikádónak. Például ha naplózni szeretnénk az eseményeket, akkor va-
lószínűleg egy id6zít6re is szükségünk lesz. De vajon az id6zít6 szólítja meg :IZ (:rzéke-
l6kel vagy minden é rzékelő maga küld időnként helyzetjelentést?

A felhaszn:'i16 a következ6képp avatkozhat VI:! a n::ndszer működéséve: élesítheti, ki-


kapcsolhatja, il letve a programozilatja, tehát szükségünk lesz valamilyen felhaszná lói
fe lületre is, Esetleg létn;hozhatunk cgy külön objektumot a szimuláci6nkban a ri:lS2t6-
programhoz is.

Milyen osztályaink lesznek?


Amint megoldottuk ezeket II problém:'íkat, llZ osztályok tervezésével folytat ju k a terve-
zést Például már kiderült, hogya HeatSensor osztályt a Sensor osztá lyból fogjuk
származtami. Ha :lZ érzékelő időnként jelentést küld, akkor többszörös örökl6désként
a Timer osztály is a szüli) osztálya lesz, vagy az időzítő csupán tagváltoz6 lesz.

A HeatSensor val6szfml1eg rendelkezik CurrentTernp () és SetTempLimit () tagfLigg-


vényekkel , és valószínűleg örökli a Sensor ősosztály SoundAlarm () függvé nyél is.

Rendszeres téma :\z objektum-orientált tervezés közben az egységbezárus. Képzeljü nk


el egy olyan megold!ist, ahol a ria$zt6rendszer rendelkezik egy HaxTernp értékkel. A ri-
asztó Ickérdezi a hómérséklctszenzortól az akmális h6mérsékletet és riaszt, h:\ túl mc-
leg van . Egyesek szerim ez megsérti al egységbezárás elvét. Valószínűleg jobb lenne,
ha nem a riaszt6rendszcr foglalkozna a hőmérséklet elemzés részleteivel, h:mem - bár
vitatható - ezt II HeatSensor osztá ly tenné.

Tudta hogy...? Egyéb források


Az objektum-orientált felfogásról érdemes elolvasnunk Matt Weisfeld The Object-
Oriented Thought Process (ISBN: 0-672-32611 -6) könyvét, mely már második ki-
adásban jelent meg a Sams kiadóná!.
I
440 VI. rész • Kmönleges,égek

Akár egyetértünk ezzel, akár nem, a probléma elemzése közben koncenLní lnunk kell
az ilyen döntésekre. Az elemzés folytatásaként e1gondolkodharunk azon, hogy csupán
az érzékelő és a Log objektum lássa-e az ér.lékelÓ aktivitibát, és maga az Alarm objek-
rum egyáltalán ne tudjon rola, illetve ne foglalkozzon vele, vagy alkalmazzunk más
megközelítést.

Megfele l ő az egységbezánís, ha nunden osztály koherens egész és két osztály nem


hajtja végre ugyanazt. Ha a Sensor osztály felelős az akruális hőmérséklet figye\ést:n,
úgy semelyik más osztálynak nem s7.ahad vele fogla lkoznia.

Másfelől viszont a többi osztály bóvítheti a szü kséges funkc ionalitással. Például ha
Sensor oszt.ály fe l e l ős a h őmérsékl et figyelésén és naplóz:ís:íért, akkor ezt úgyis meg·
val6síthatja, hogy egy Log objektumm bízza az aktuális adatok rögzítését.

A t(;vékcnys6gck éles clhatároJásával II programunk t:gyszenlbben bővithető és kar·


bantarth:n6 lesz. Ha később a riaszt6rendszert egy fejlettebb modulra szeretnénk cse-
rélni, úgy <rt interfésze az érzékelők és a napl6z6rcndszer fejé pontos és j61 definiált.
A riasztórt;:ndszerbeli módosít:1sok nem lehetnek hatáss,ll a Sengor oszl..'ilyokm. Persze
ez fordítva is igaz.

Rendelkezzen a Heatflensor ReportAlarm() fÜSb'Vénnycl? Természetese az összes


szenzornak tudni;l kell riaSltást küldeni. Ebból máris világosan következik, hogy
II ReportAlarm() a Sensor virtuális eljárás.1 kell legyen, val:Huim hogya sensor egy
absztrakt alaposztály lesz. Elképzelhető, hogyasensor osztá ly ReportAllI.rm () függ-
vénye túl álllllános a HeatSensor osztály száma föl , nonban :1 felülírt függvény megad·
ja azokat a par',unétereket, melyeket csak az adhat meg.

Hogyan jelezzük a riasztásokat?


Amcnnyiben az érzéke l ők riasztanak, úgy rengeteg információt kell átadniuk a rendt -
s(:gct tárcsázÓ objektumnak és a naplózó objeknunnak. Érdemes létrehoznunk eb'Y
Conditions osztályt, melynck a konstruktora számos mé rést végez. A mérések bon~ •
luJtság,ínak függvényében lehet ak.'ir objekmm, akár egyszenl egész LípL1SÚ skalár (-rtL

Elképzelhet6, hogya Condi tion objekmmot közvetlenül a központi riasltóobjekt


nak adjuk :ít, vagy a Condilion objekmm az Alarm objektum alosztálya lesz, mely
tudja, mit kell tennie vészhclyzer csetén. Elképzelhető az is, hogy nincs központi
jektum, ehelyeu az érzé kelők hozzák létre a Condition objektumokat. Néhány
Condition objektum saját magát naplózza, míg a többi II rend6ség elérésért felel" ~

Egy megfclel6en tervezett, eseményvezérelt rendszernek nincs szüksége közporn


ordinátorra. Képzeljük el, hogy a szenzorok függetlenül fogadnak és küldenek
______________________________~2~2.~6~~~·~Ob~j~.~~m~~~n~·.~má~tt~'~I'~mri~S~é=s~te~~~.~m=s~
I ,«~I~____~I··i

teket más objektumoknak, állítanak be paramétereket, végeznck mérésekcl, figyelik


a házat. Ha hibát észlel nek , az Alarm objeknllll naplózza a problémát (például üzene-
tet küld a Log objektumnak) és végrehajtja a szükséges lépéseket.

Eseményhurkok
Egy ilyen esemény vezérelt rendszer szimuláci6jához a programunknak rendelkeznie
kell cscm6nyhurokkal. Az eseményhurok gyakorlatilag e~,.y végtelen ciklus - ilyen pél-
dául a whil e (1) -, mely fogadja az operációs rendszer üzeneteit (egél'kattintás, billen-
tyű leütés, stb.), egyenként lekezeli őket, majd vissza ugrik az elejére, amíg nem teljesül
a kilépési felt étt!l. A 22.1 Lista cgy egyszeru esemény hurkOl mUlat be.

122.1 Usta - Egy egyszerü eseménvhurok (simpleevent.cpp) I


O, II 22.1 Lista
l: jinclude <iostream>
2,
3: class Condition
4. : (
5, public:
6: Condition () { }
7: virtual -Condition() ()
8: virtual void Log () = Ol
9: l;
10:
ll : c l ass Normal : public Cond i tion
l:t. : (
13: public :
14: Normal() ( Log() l l
15 : virtual -Norma1() {}
16: vj rtua1 void Log ()
17 : ( std : : ..:ou t « ' 1.ogging normal condition s .. . \n ' ; )
18 : l;
19 :
20 : class Error : public Condition
21: {
22: public :
2] : Error() ( Log();)
24 : virtual -Error() (l
25 : virtual void Log() ( st.d : : cout « "Logging error!\n" ; )
26 : );
27 :
28 : class Alarm : p ublic Condi tion
29 : (
]0 : public :
31 : Alarm () ;
]J : virtual -Alarm () {}
J] : virtual void Warn() { std :: cout « 'Warning ! \n" ; }
]4 : virtual void Log () { std:: cout « "General Alarm 10g\o' ; l
442 1 ~. rész • KlIlönl.g.sség.k

35 : virtual void Call() O,


36 : };
37 :
38 : Alarm::Alarm()
39 : (
40: Log();
41: Warn();
42:
43:
44: class FireAlarm : public Alarm
45:
46: public:
47: Firelllürm() {Log() ; } ;
48, virtual -FircAlarm() (j
49: virtual void Call{) ( std: : cout« ' calling Fire Dept . I \n" ; )
50 : virtual void Log () { std :: cout « " Logging rire call . \0' ; }
51 : );
52 :
53 : int maln!)
54: {
55: int input ;
56: int okay :o 1;
57: Condition· pCondition;
58: while (okay)
59: {
60: std : :cout « • (O)Quit (11Normal (2)Fire : '.
61: std: :cin » input ;
62: okuy = input;
63: switch (input)
64: {
65 : ci!lse O:
66 : break;
67: c.!Ise 1:
6B : pCondition = new Normal;
69 : delete pCondition;
70 : break ;
71 : case 2 :
72 : pCondition = new F.i reAlarm;
73 : delete pCondition ;
74: break ;
75 : default :
76 : pCondi t ion'" n ew Error ;
77 : de l e t e pCondition;
7B : okay", O;
79 : break;
BO :
B1:
S2: return O;
S3:

(O)Quit {l)Normal (2)Fire : 1


II
____________________________~U~.~
~_·~O~~~e~==m~~~n~·.~n~~
~ el=emri
~= s =és~t=.~
=e=re=s~

~ 3 _______ii;

Logg ing normal conditions ...


(O)Quit (l)Normal (2) Fire : 2
General Alarm log
Warning!
Logging fire call .
(O)Quit (l)Norma l (2 )Fire : O

r
Az 58. és 81. sorok között található esemény hurok egy éí.lékel6 szimuláci6j{1t valósítja
meg: értesít a normális mLÍködésr(51 vagy a túzrÓI. Megfigyelhetjük, hogy az értesítés
hatására létrejön egy Conditi on típusú objektum, melynek konstruktor.! kü lö nbözó
tagfüggvényckct hív meg.

Virtw'ilis ragfüggv6nyeket konstruktorb61 megbívva zav<lJ'Ó működést tapasztalll:ltunk,


ha nem figyelünk oda, hogy milyen sorrendben hozzuk létre az objektumokat. 1'61d{lUl,
ha létrehozzuk a 72. sorban található FireAlarrn objektumOt, akkor e16ször II Condi-
tion, :lZ Alarm, és végül a Fi reAlarm osztály jön IéIre. Az lüarm konstruklom meg-
hívja a Log-ot , dc ez az Alarm osztály Log () függvénye, nem pedig a FireAlarrn-é,
annak ellenére, hogy II I.og () függvényt virtutíliskénL deklanílluk. Ez történik így, merl
amiko r :lZ Alarm konstruktora leful, akkor mC:g nem létezik FireAlarm objektum. Ké-
56bb, ha már !C:trcjöu II F'ireAlnrrn, II konstrukto r ismét meghívja a Log () függvényt ,
ami már a FireAlarm : : Log () .

PostMaster: egy esettanulmány


Ussunk most egy másik problémát, amelyen gyakorolhat juk az objekrum-orientált ter-
vezést. Az Acme Sofrware cég felbérelte az OlvasóI egy új szoftver megalkol:lsára és
egy CH programozói csapat toborzás,íra. Jim Grandiose, az új termékek fejlesztési
részlegének alelnökc a megbízónk. Azt szeretné, ha megtelVcznénk és elkészítcnénk
a PostMastert, egy olyan e-maii olvasó programot , mellyel a különböz6 szolgáltatók
rendszerét elérhetjük. A potenciális célközön~g o lyan üzlclc mberekból áll, "kik egy-
nél több e-maii szolgáltatót használnak, mint például: CompuSclVe, America Online,
Internet Maii, lotus Notes, és így tovább .

A felhasználó mcgt:tníthatja a PostMastert, hogyan csatlakozhat a különböz6 c-maii


szolgáltatókhoz. A PostMaster letöiti a leveleket és egységes formában tá~a II fe1hllsz-
náló elé, Ichetóvé téve az e-mailek rendezését, a választ, a továbbítást a szolgáltatók
között, és í!,'Y lovább.

A PosrMas[er Profcssional - nlinc a PostMaster következ6 veni6ja - szintén tervbe van


véve. Ez rendelkezni fog adminisztratív segéd - titkár - móddal, ennek keretében a fel-
használó feJj ogosítllat egy másik személyt az összes vagy csak néhány e-maii elolvasá-
444 1VI. rész • KlIllnl,g,..óg,k
sával, rutin levelezések kezelésével, és í&'Y tovább. Tennészelesen már a marketing
osztály is jelezte igényét eb'Y mesterséges intelligenciával bíró komponensre, melynek
segítségével a PostMasler rendezhetné és megfelelő prioritással látná el a leveleket
a tárgyuk, illetve a tartalmukba n lévő kulcsszavak alapján.

Egyéb bővítésekr61 is volt szó, például nemcsak levelek, de levelezőlisták vagy


Internetes hírcsoportok kezelése. Nyilvánvaló, hogy az Acme nagy reményeket fúz
a POS1Master-hez, és piacra dobáshoz szűk idókerettel, de gyakorlatilag korlátlan köll-
ségvetéssel rendelkezünk.

Mérj kétszer, vágj egyszer


Megrendeltük az eszközöket és berendeztük az irodá t. Ezután els6 lépésként pontosan
speci fik51juk ~l lerméket. A piac fe lmérése után egyplatformos fejlesztést javasolunk
a f6nökün kn<::k , de döntenünk kell , hogy Unix, Macintosh vagy Windows legyen-e ez
a bizonyos egy?

Számos fámszló megbeszélésünk voll Jim Grandiose-:z.1l. Kiderül, hogy tökéleles dön-
tés nincs, tehát két részre boOljllk a projektet: el6térre, vagyis a fe lh:'lsznál6i felületre,
és háttérre, vagyis az adatbázisért és kommunikációért felelős részre. Hogy gyorsabban
haladjon a fejlesztés , el6ször Windowsra fejlesztünk, kés6bb pedig Unixra és va lószí-
nIlleg Mac-re.

Ez az egyszen1 döntés számos új kérdést vet fel projckuel kapcsolatban. Azonnal nyil-
vánva lóvá ,válik, hogy szükségünk lesz számos függvénykönyvtárrn , melyek feladata
a mcmóri:lkezclés, a kül önböző fe lhasználói felületek kezelése, valamilll a kommuni-
káció és :I Z adatbázis kezelése.

Gmndiosc úr komolyan gondolja, hob'Y a projekt azon áll v:lgy bukik, hogy megfelelő­
en átlátjuk-e problémáI. Éppen ezért arra kér minket, hogy csak a kezdeti elemzés és
telvezés után toborozzunk kollégákat. Nekilánmk a probléma elemzésének.

Oszd meg és uralkodj


Hamar kiderül , hogy igazán több problémát kell megolda nunk. A projektet az alábbi
nagyobb alprojektekre osztjuk:

• Kommunikáció: Az e-maii szolgáltató elérése modemes kapcsolaton vagy háló-


zaton keresztül.
• Adatbázis: Adatok tárolása a lemezre és visszaolvasása.
• E-maii: A küJönböz6 (szolgálta tófuggő) e-maii formámmok olvasása és létrehozá.......
22. óra • Objektum-orientátt elemzés és telVezés 1 445

• Szerkesztés: Kíváló szerkes ztő felület bil.tosftása üze netek létrehozásához Í!s m6-
dosításához.
• Platform kérdések: A felhasznál 6i felület elemei nek megjclenítése minden plat-
formon.
• Bővíthetőség: Tervek a fejlesztésre , bóvítésre.
• Szervezés és ütemterv: az egyes fejl eszt6k igazgatása és k6djaik közti függősé­
gek kezelése . Minden csoportnak kitűzi és közzéteszi a hat.á ridőit, hogy utána
ennek megfelelőe n It::hessen tervezni. A cégvezetésnck Í!s :I marketingeseknek
természetesen tudniuk kt!ll , nukorra készül el a termé k.

Felveszünk egy menedzscn, aki a szervezésén és a határidőkért fele l. Ezután felve-


szünk vezet5 programoz6kat, akik segítenek az elemzésben és a tervezésben, valamint
utána részt vesznek II megval6sításban. Ezek a vezető programoz6k a z alábbi csapato-
kat hozzák lé tre:

• Kommu nikációs csoport: A betárcsáz6s és egyéb hálózati ka pcsolatok kezelése.


Ok csomagokkal, :Idatfolyamokkal és bitekkel foglalkoznak, nem pedig maguk-
kal az c-maikkkel.
• Üzenetek formátumáért fele l ős csoport: Ez C5.'lpat felel a különböző e-maiI for-
mátumok és a kanonikus formátum - PostMasler sz..1bv~ny - közti oda-vissza
~dakítflsé rt. Szintén ez a csOport foglalkozik az üzenetek ICme7.re me ntésével és
ig6ny szerinti visszatöltésévcl.
• Üzenctszerkeszt6ért felelős csoport: Ez a csoport felel6s a teljes relhasználói felü-
Ict~rt mindegyik platformon. A sze rkesztők feladata, hogy a háttér és a kliensel-
d:.1 közti kommunikáció minél egyszen1bb és igényeit tekintve minél szűkebb le-
gyen , így a termék kiterjesztése más platfonnokra nem igényli a forrás másolásá:t.

Üzenetformátum
Úgy döntünk, bOh'Y fé lretéve a kommunikációs és fe lhasználói felületet, el6ször az üze-
nel formátumával foglalkozunk. A többi csak azután következik, bogy teljesen megér-
tettük, mir61 is van szó. Nem sek é rtelme va n azon törni a fejünket, hogyan jelenítjük
meg az információkat, amíg nem tudjuk , milyen információkal kell megjeleníte nünk.

A különböző e-maii formátumok vizsgálata során kiderült, hogy sok hasonl6ság van
köztük a számos különbség ellenére is. Minden e-maiI üzene tnek van felad6ja , címzett-
je és létrehozási dátuma. Csakne m minde n üzenet rendelkezik címmel vagy tárgysor-
ral, a levéltörzs pedig egyszeru vagy összetett szöveg (formázásokkal), tanalmazhat
grafikát, esetleg hangot, vagy bármilyen egyéb extrákat. A legtöbb ilyen e-maii szolgá l-
tatás lehet6vé teszi csatolt áll ományok kezelésél is, melyet a felhasználó más progmm
segítségével megnyithat.
44s l VI. rész· Különlegességek

Megerősítjük korábbi döntésünket, mely szerint az eredeti formátumot áta lakít juk
Postl\1aster [ormátumúvá. Ezzel a megoldással csak egy formátumot kell tárolnunk, va-
lamint a lemezre írás/ olvasás is egyszeruDbé válik. A fejlécben található információkal
(küldő , címzett, dátum, tárgy, stb.) a levéltörLSét61 elkülönítve tároljuk. Így ha a fel-
használó keresni szeretne a fejlécekben, ezt anélkül leheti meg, hogy a lcvéltörzsét is
be kellt!ne olv:I:.nia rt progrdmunknak. Előrelátóa n felkészü lünk arm az esetre is, ami-
kor a felh<lszná16 csupán csak a levél fejlécét szeretné letölteni a szolgáltatótól a levél
törzse nélkül. A Pos~bsler első verl.iójában azonban [elöltjük ;. teljes e-maii-t.amit
azonban nem fel tétlen jelenítiink mcg a fclhaszná l6nak.

Az osztályok kezdeti terve


Az elemzés ut:ín megtervezzük a Message osztá lyt. A bővíthet6séget szem elŐlt tartvH -
később tudjuk kezelni a nem e-mai i jellegű üzeneteket is - létrehozzuk az Email -
Message osztá lyt a Message absztrakt alaposztályb61. Az EmailMeosage oszt:'ilyb61
sz:'irmaztatjtlk a Pos tMasterMessage, InterchangeMessagc, CISMessage, PrOdigy
Message , stb. osztályokat.

Az üzenetek természetesen objektumok lesznek a programban, azor,ban megalkotni


a megfelelő objektl.lI11okat az, ami a legnagyobb kihív ~s az objektum-orientált progra-
mozásba n. Néhány esetben, mint például üzeneteknél, az els6dleges objektumok kics-
nek, mint lehetséges megoldások. Gyakran hOSSL111 és komoly:.., gondolkoznunk kelJ
azon, hogy megtaláljuk a megoldandó feladathoz a megfe l elő objektumokat.

Ne essünk kéL~égbe , H legtöbb terv nem lesz tökéletes cls6 nekifutásrn. Kiinduláském
fogalmazzuk meg a probléma leírását szóban. Késl-Ítsünk listát a leírásában szereplő
f6 nevekr6 1és igékr61. A f6nevekb61 jó eséllyel <IZ objektumai nk lesznek. Az igék pedig
ezen objektumok metódusai (vagy akár objektumok is lehetnek). Ez nem bolondbiztos
megoldás, de addig megfele l ő, amíg nem szerzünk gY:Lkorlatot a tervezésben.

Ez volt a könn}'\.1 rész. Viszont felmenii a következő kérdés: Az üzenet fejU:ce és


az üzenet törzse külön osZtály legyen? Ha igen, szükségünk lesz párhuzamos hierardu-
ára: NewsGroupBody és NewsGroupHeader, illetve E."ma il Body és EmailHeader?

A párhu:t.:unos hierarchia sokszor hibás tervezésre figyelmeztet . Gyakori hiba objek-


tum~orientált fejlesztéskor, hogy létrehozunk objekmmokat az egyik ágon, míg ugyan-
ezek egy másik ágon is jelen vannak. A hiernrchiák frissítése és szinkronizálása egy ic..
után nyomasztóvá válik, ez tipikusan egy karbantartói rémálom.

Természetesen nincs szilárd szabály, hiszen néha párhuzamos hierarchiák bevezetése "'oC--
zet a probléma leghatékonyabb megoldásához. Azonban ha ilyen irányt vesz a te["\"ez6
érdemes újragondolni a problémát, valószínűleg létezik sokkal elegdnsabb módszer is_
r
______________________________~2~2,~6~m~·~O~b~ie~~~m~~~n~en~~~tt~e~le~mz~é~s~é~s~m~N~e~re~s~I~44~7~______k~ t

Ha e-maiI érkezik ,I szolgáltatótól, nem feltétlen kapjuk meg külön a fcjJécet és külön
a törzset. Sok szolgáltató egy nagy adatfolyamkt':nt küld i, amelyet a progl"Jffiunknak
kell szétbomania. Talán érdemes ennek megfele l ően kialakítani a hierarchiát

Tovább vizsgálva a feladatot arra jutunk, hogy meg kell próbálnunk összegyűjteni
az üzenet tulajdonságait, szem e l őtt tartVa új képességek bevezetésének lehet6ségét és
az adauárolás megfe l elő absztrakciós szintjét. Az objekrum rnlajdonságainak összegyúj-
tésével könnyen meghatározhatjuk az adattagokat, illetve az esetleges egyéb objekru-
fiakat, melyekre szükségünk lehet.

Az üzeneteket tárolnunk kell, akárcsak a felhasználói beállításokat, telefonszfimokat, és


így tovább. A tfirolásnak a hierarchia alj-án kell elhelyezkednie. Vajon közös legyen
az üzenetek és a beállítások aJaposzt!llya?

Egy vagy több kiindul6pontú hierarchia


Az örökl6déssel kapcsolatban alapvet6en kétféle megközelítés létezik: az egyik szerint
minden, vagy majdnem minden osztály cgy közös 6st61 - il gyökéroszliilyt61 - ered,
a másik szerint megengedjük a többszörös örökl6dé.'it. A közös 6sosztály előnye, hogy
gyakran elkeri.\lhet6 a többszöröS örökl6dés, hátránya viszont, hogy az irnplernenládó
gyakran visszaszivárog az alafX)$ztályba.

Az osztályok egy halmaza gyökeres struktúrájú, ha minden osztálynak kÖZÖS:lZ 6se.

Gyökérrel nem rendelkez6 hierarchiáknál nem minden osztálynak ugyanaz az &osztálya.

Elhatároztuk, hogy több platformra fejlesztünk, azonb:tn a többszörös örökl6dés össze-


ten és nem feltétlen támogatja minden fordító megfelel6cn az összes pbtformon. igy
közös gyökérrel rendelkező hierarchiában és egyszeres öröklődésben fogunk gondol-
kodni. Megkeressük azokat a helyeket is, ahol a többszörös öröklődés szóba jöhet
majd. A hiemrchiát később fájtblommcntcsen felbonthatjuk és beil1eszthetjí.lk a több-
szörös öröklődést. Ii

A belső osztályok neveit p előtaggal l áljuk el, így könnyen mcg ludjuk mond;:mi, mely
osztályok szá rmaznHk tőlünk és melyek más függvény könyvtárakb61.

A pObject lesz a gyökérosztály. Gya korlalilag ez az objektum az 6se minden más osz-
tálynak. A pObj ect egyszen.1, csupá n adalcsere lesz a szerepe.

Amennyiben gyökérrel rendelkező hierarchiát szeretnénk, a gyökérosztálynak általá-


nos nevet adjunk (minl a pObjec t) és csak kevés képességgel ruházzuk fe l. A gyökér
objektumnak létre kell tudni hozni az utódait és azokra, mint pObj e ct példányokra hi-
\·atkozni. Cserébe az interfész &'Yakran áL,>zúródik a &'Yökérosztályba.
448 1VI. rész • Különlegességek
A hiera rchiában a pStored és a pwired osztályok következnek. A pStored objektu-
mokat a lemezen tároljuk (például ha nem használjuk a programot), a pWired objektu-
mok pedig modemen vagy hálózaton küldjük üzenetekként. Csaknem minden objek-
tumollernentünk lemezre, így érdemes azt a gyökérhez közel elhelyezni a hiemrchiá-
ban. Az összes elküldöu objektumot tárolnunk kell, de nem minden tárolt objektumOl
küldünk el, igy jogosan származtatjuk a pWired osztályt a pStored oszlályból.

Minden származtatott osztály hozzáfér a sz lilőosztá l y tudásához (adat) és fun kcionalitá-


sához (metódusok), és mindegyik egyedi képességekkel bővÍli azl. Ebből kiroly61ag
a pWircd larl:.llmazhat plusz metódusokat, dc ezek a mOO(!ffit:s adatátvitelért lesznek
felelfisek.

Lchc(!';éges, hogy minden elküldött objektumot t{irolunk, vagy minden tárolt objektu-
mOt elkü ldünk, de akár az is el6fordulhat, hogya fenti kijelentések egyike sem teljesül.
Ha csak néhány objeklumot tárolunk és csak néhány tárolt objektumot kolc!Onk el, ak-
kor a többszörös örökl6déssel kell dolgoznunk, V:lgy más módon kell megoldanunk
a problémát. Az egyik lcheL~égcs megoldás, hogya Wired osztMyt a Stored osztály-
ból sz:"irma:a;njuk. és JélrehoZllnk met6dusokat a tároláshoz, melyek nem csinálnak
semmit, vagy hibát jeleznek, ha az objektumot rnodemen elküldtük, de nem tároltuk.

Vegyük észre, hogy néhány objektumot nem kell elküldenil nk, llycnek például a fel-
használói beállítások. Persze minden küldött objektum tárolandó, így az ennek megfe-
lel6 örö kl ődési hicruchiál a 22. l-es ábra mutatja .

PObjOO

pSto'ad

pWirad pPrefe rences pProvlder Info

I
pMeuage

22.1 ábra
A l...>c:zdcli 6r6k1ődési hierarchia
22. óra • Objektum-orientált elemzés ás tervezés 449

Apropó Függvénykönyvtárak: Vásároljuk vagy frjuk?


Vásároljuk vagy írjuk? A tervezési fázisba n mindig felmerül a'kérdés, hogy milyen
rutinokat kell megirnunk és melyek azok, amelyeket megvásárolhatunk. Elképzelhe-
tő, h ogyelőnyösebb már meglévő kereskedelmi függvénykö nyvtárt használni, mely
megoldást nyújt néhány vagy akár az összes kommunikációval kapcsolatos kérdés·
re. A döntésnél persze a licencdíjakat és egyéb, nem technikai jellegú kérdéseket is
figyelembe kell venni.
Gyakran előnyös ilyen függvénykönyvtárakat vásárolni, és a tényleges problémára
koncentrálni, fgy nem kell újra feltalálnunk a kereket. Akkor is érdemes elgondol-
kodni a vásárláson, ha nem kifejezetten C++-hoz szánták a fej lesztők a függvény-
könyvtárat, de az alapfunkciói használhatóak. Ez jelentős szerepet játszhat abban,
hogy tudjuk tartani ahatári dőket.

Interfészek tervezése
Fontos, hogya tervezés ezen szakaszában ne nyugtalankodjunk az impk:ment,íd6 mi-
ali. Minden energi:'Ínk:lt az osztá lyok közti letisztult imerf!E!sLek rnegtervezésérc kell
koncentr.'ilnunk, majd pedig fel kell vázoJnunk, hogy az osztályon belül milyen adatok-
1".1 és metódusokrn lesz szükség.

Gyakr::\n hasznos, h:l teljes mértékben megértjük aL alaposztályokat, miel6tt a s7...árrnaz-


tatott osztályokkal foglalkoznánk, tehát e lsőként a pObject-re, a pStored-re és
a pWired-re fogunk konccmrálni.

A pObject - II gyökérosztály - csupán olyan adatokkal és metódusokkal rendelkezhet,


melyek közösek. Mondjuk minden objekmm rendelkezhetne egyedi azonosító szám-
mal. Létrehozhatnánk egy pID cpostMaster ID) váltOZÓI a pObject adatwgjaként, de
e lőször tegyiik fe l a kérdést magunknak: Szükséges-e ilyen azonosít6s7..ám olyan objek-
wmhoz, amelyet nem tárolunk, ts nem is küldünk el? Ez persze felveti a következő kér-
dést is: Van-e olyan objektum, melyet nem t:irolunk, de része ennek a hieF.lrchiának?

Ha nincs ilycn objektum, érdemes mérlegelni a pObject és a pStored osztá lyok


összcvonását, azonban ha minden objektum tárolt objektum, akkor mi alapján teszünk
különbséget? Ezt végiggondolva afra a következtetésre jutunk, hogy néhány objektum,
mim példtlu l a cím objektu mok, amelyet önmagában nem tárolunk, jogosan származ-
tathatnánk a pObject-b6I, hiszen ha tároljuk, akkor azt csakis egy másik objektum ré-
szekénttönénnc.

Jelenleg tehál egy külön pObject osztály mégiscsak hasznos lenne. Képzeljünk el egy
á mlistát, amely pAddress objekmmok gyűjte ménye lenne és noha önmagában
pAddress objektumot sose tárolnánk, mégis rendelkezhemének ezek az objektumok
egyedi azonosítósz:ímokkal , ami hasznos.
I
450 VI. rész • Különlegességek

Próbaképp rendeljük hozzá a pID--t a pObject oSllályhoz, amely í!,'Y néz ki minimálisan:
class pOjbect
(
public :
pObject () ;
-pobject ();
pID GetID{) const;
void SatID() ;
pr-lvate:
pID ltsID;

Számos dolgot megfigyelhetOnk ebben az osztálydeklar.:'ici6ban. Elóször is, n osztály


nem származtatott osztály, ez ti gyökéroszlály. Másodszor: senuni jele az irnplementáci-
únak, még a GetID ( ) -t sem implementáltuk, amely minden bizonnyal egy helyben ki-
rejlett (inline) met6dus lenne.

Harm:ldszor: felfigyelhetünk a const metódusnl, mely az interfész része, nem az imp-


lement<ÍCióé. V6gü1 pedig bevezettünk egy új ad'lltípUSl pID néven. A pID itt inkább tí-
PlIS, mint tényleges vállozó - jelenesetben előjel nélküli long típus -, mellyel rugalma-
sabbá válik a progr.llnunk fe lé pítése.

lia kidelÜl , hogy nincs szükségünk el6jel nélküli long-ra , vagy az el6jel nélIriili long
nem elég nagy, egyszeníen módosítjuk a pID-t. Ez a módosítás mindenhol jelentkezni
fog, ahol pID-Ve! dolgozik a program, nem kell megkeresni ti forrásk6dban az összes
olyan helyet, ahol el6fordul ::t pID.

Most::t typedef segíL'iégével a pID-t ULONG típusúnak deklaráltuk, az ULONG-Ol pedig


cl6jcl nélküli long-ként deklaráltuk. Ez felveti ::t ké rdést: v:ljo n hova kerüljenek ezek
a deklarációk?

Nagy projektek esetén a különooz6 fájlokat is meg kell terveznünk. Projektünkbe n


az egyik hagyományos megközelítést alkalmazzuk: minden osztály külön fejléc állo-
mánnyal és implementáci6val (.CPP állomány) rendelkezik. Ezén az állományaink ~­
vc OBJECT . HPP és OBJECT . cpp lesz. Hasonlóképp hozzuk létre a pMessage osztály
deklaráci6jához II MSG . HP?, az implementáci6jiihoz pedig a MSG . ePF iillományt.

Prototfpus létrehozása
Egy olyan nagy projekt esctén, mint a PoslMaster is, nem valószínű, hogy az els6 ter.
tökéletes és teljes lesz. Könnyen belefutharnnk a függ6leges skálázhatóság problém...
ba. Egyene,> út vezet a katasztr6fához, ha úgy próbáljuk meg létrehozni az összes g -
tályt és kiegészíteni az interfészeket, hogy még egy sor működó kódot sem ínunk.
______________________________~2~2~.6~~~·~O~bi~'~~m~~~n~·e~má=k~'=I'=mm~·S~é~S~m~N~'~Zé~S~I.4~51~_____1I ~

Számos igen jó ok van arra, hogy miért érdemes a tervet egy prototípuson kipróbálni -
amely egy kevéssé elegáns, de működ6 példa az alap ödeteinkre. Számos típusa van
a prolOtípusnak, melyek ntind más és más igényeket elégítenek ki.

A felhaszn(116i interfész prototípus lehet6vé teszi a végfelhasznál6kmlk szánt felület


tesztelését

A funkcionális prototípus nem foglalkozik a felhasználói felillettcl, viszont kipróbálható-


ak olyan képességek, mim pfldáullevelek továbbítása vagy csatolt állományok kezelése.

Végül pedig a fe lépítéssel kapcso];uos prototípus megadja a lebet6o,;éget egy kisebb


progr..1111 fejlesztésére , valamint annak megbecsülésf:re, vajon a tervezés tükrében ké-
s6bb hogyan skálázható a program.

Rendkívül fontos, hogy végig világos,lk maradjan:lk a prototípus céljai. Döntsük el,
hogya felhaszná lói felületet vizsgáljlIk, a funkcionalitáss<t l kísérletezünk, vagy mond-
juk a végső termék skálázhatóságára vagyunk kíváncsiak. Egy a felépítéssel kapcsola-
tos jó tervezői prototípus rosszul írja le a felhasználói fellilelel és persze ugyanez for-
dítva is igaz.

Az is fontos, ho!.'Y ne fejlesszük túl a prototípust, vagyis ne fektessünk bele annyi mun-
kál, hogy s:li:ikség esetén ne tudjunk könnyti szívvel megv{llni tőle , és elölről kezdeni
az egészet. .~

A BO/BO-as szabály
Egy amolyan tervezési őkőls7",,1.bály szerint <IZ a legcélszen1bb, ha a munka e lső szaka-
szában úgy gondolkodunk, hogy az idő 8O%-ban a céloknak az,,--al a 8O%-ával foglalko-
zunk amely a legtöbb ember s7.ámára fontos. Az idő maradék 20%-áb:ln aztán foglal-
kozharunk a partikuláris kérdésekkel. Természetesen előbb vah'Y ulóbb találkozni fo-
gunk a "peremfellt:telek" kérdésével, de amíg csak lehet, addig tartsuk magunkat a fc n-
li 8O/ 80-as szabályhoz.

Ennek megfelel6cn általában célszen1 a tervezést az alaposztá lyokka l kezdeni, a má-


s()(llagos osztályokat pedig egy id6re félretenni. Aztán ha már azonosítottuk a többszö-
rös osztályokat, melyek apró különbségekl6l eltekintve meglehetősen hasonlóan mű­
ködnek, akkor megleheljük, hogy kiválasztunk egyet, és csak a rra koncentrálunk,
al unokatestvéreit pedig majd a végén megvalósítjuk.
452 1VI. rész· Különlegességek

Apropó Parelo szabálya


Van egy másik szabály - a 80/20-as szabály -, amely szerint a program elsó 20%-
ának megírásához az idő 80%-ra van srukség, a program hátralévó 80%-a pedig
az idő másik BO%·át igényli.
Na jó, ez csak vicc. Az igazi szabály szerint az idő 80%-a a munka 20%-ához szük·
séges, de egy egészen másik összefüggésben azt is szokták mondani hogy egy cég
hasznának 80%-a az ügyfelek 20%-tól várható. Ezt a szabályt tehát széles körben le-
het alkalmazni.

A PostMasterMessage osztály tervezése


Mindezeket szem el6n tartVa úgy ctönrünk, hogy il továbbiakban a ElostMa sterMessage
osztályra koncentmlunk. Ez az az osztály ugyanis, amit a legközvetlenebb módon lU -
dunk el len6rizni.

Természetesen az interfész részeként a PostMasterMessage-nek a különböz6 tfPllsok


közt.i párbeszédet is bonyolítania kell. Azt reméljük, hogy az egyéb üzenettípusok szol-
gáltal6ival sikerült majd szoros együtt.működésl kialakítani , és meg t.udjuk szerezni 16-
llik az Ozenetformáwmok specifikáci6ir. Egyel6re .. zonban C$.1 k ügyes t.. lálgmásokra
tudunk hagy.. tkozni azok .. lapján, amit az adol:{ rendszerekb61 kapllJnk.

A PostM!lsterMessage minden esetben rendelkezik felad6val , címzettei, feladási dá-


tllmmal és tárggyal, valamin( tartalmazza levél vagy üzenet törzsét és esellegesen csa-
tolt állományoka!. Ezek alapján nyilvánvaló, hogy elérési met6dllsokat kell majd ír-
nunk ezekhez az :mribútumokhoz, a csatolt állomá rlyok és a levél méretének jelzésé-
hez, és így tovább.

Néhány s7.0lgált..tó, melyekhez csatlakozni fogunk, b6vített szövcgrormátllmot haszna.


vagyis olyan szövegel, mely utasításokat a betűtípussal és egyéb, :t formázáss:!l kapcso-
latos utasításokat (félkövér, dőlL) is tartalmaz. Más szolgáltatók ugyanakkor nem támo-
g:lIják ezeket az attribúlumokat, viszont esetleg saját rormáttunot használnak bővített
formátumú szöv(;!gek kezcl6sére. Az osztályunk tch,ít rendelkezni fog konvcrzi6s eljá-
rásokkal, amelyek a bővitett szövegrormátumot egyszerű szöveggé alakitják, illetve
PostMaster formátumúra hozzák az egyéb formátumokat.

Az alkalmazásfejlesztói interfész
Az alk:llmazásfejleszt6i interrész (Applicalion Programming Interface - AJl!) leír.'isok es
rutinok gyűjteménye egy szolgáltatás használatához. Számos e-maii szolgáltató elérhe-
t6vé teszi saját API-ját, igy a PostMaster is használhatja az olyan kifinomult lehetősé-
22. óra •

geket, mint például a bővített szövegformátum kezelése vagy a beágyazott állományok


hozzáfűzése. Minden bizonnyal a PostMaster API-jának közzététele is hasznos lesz, hi-
szen így más szolgáltatók számár.. lehetóvé válik a Posl1\1asterrel való egyiitunúködés.

A PostMasterMessage-hez egy jól megtervezett nyilvános interfészt keJllétrehoz-


nunk, a PostMasler API-jának egyik legfontosabb részét pedig az említet! konverziós
fü8!,lYények jelentjk majd.

A 22.2. listában látható a PostMasterMessage felületének pillanatnyi leírása.

Figyelem! Még csak meg se pr6báljuk...


A lista nem definiálja az alaposztályt (Mai1MeSSage ), és emiatt nem is fogjuk tud·
ni lefordftani.

22.2 Um - A Po.tM••terM8IIag. interfész (postmasterinterfaca.cpp)


o: 1/ 22 . 2 Lista. a PostMa.sterMessagc osztá l y
1.
2: class PostMasterHessaqe : publ i c MailMessage
3: {
4: public :
5: PostMaatcrMcsaaqc();
6: Poflt.Ma~terMel'lsaqe(
7: pAddre~s Sender.
8: pAddrc99 Rccipient.
9: pString Subject.
10 : pDatc crcationDate);
11 :
12 : II egyéb konstruktorok jOnnek ide
13 : II ne fe1edkezzank el a máso16 konst r uktorr61
14 : II mint ahogy a storagc
15 : II és a wire (ormátumú konstruktorokr61 sem
16 : II A kalönbözó egyéb formátumok konstruktorair61 se feledkczzQnk meg
17 : -PostMasterMessage() ;
18 : pAddress& GetSender() const;
19 : votd SetSender (pAddress&) ;
20 : II egyéb adattag elérési fOggvények
21:
22 : II operátor metódIJsokat ide . beleé rtve az egyen l őség operátort is
23 : II valamint a különböző konverziós rut i noka.t me lyekkel
24 : II a PostMaster azene t ck más formátumra alakithat6ak.
25 :
26 : private :
27 : pAddress itsSender ;
28 : pAddress itsRecipient ;
29 , pString itsSubject;
30 , pDate itsCreationDate ;
31 : pDate itsLastModDate ;
32 : pDate itsRecciptDatc ;
454 1VI. rész • Különlegességek

33: pD~te itsFlrstReadDate :


34 : pDate itsLas tReadDate ;
35:. );

A PostMasterMessage osztályt a MailMessage osztályból származtauuk. Számos


konstnlktort adtunk meg, melyek lehetóvé teszik PostMasterMessage objckrumok
létrehozását más Üzenelformátumokból.

Számos adaltagelérő me16clllsL adtunk meg a különböz6 adaLLagok eléréséhez és 016-


dosításához, az operálorok pedig lehetővt! teszik ;~Z üzenet egy részének va!,')' egészé-
nek átalakítását más formánlffiokrn. A korábban tárgyalt üzenetolvas6 és tároló függvé-
nyeket is fe1tünteltük.

Programozás nagy csoportokban


Már ez a kezdetleges terv ;s elég arra, hogy lássuk, a kí.i1önbözl'í fejlesztői csopon.ok-
nak mil is kell megval6sít:u,;uk, A Kommunikációs csapat máris kezdheti a kommum-
kációs háuérrel kapcsobtos munkál, de meg kell egyeznie az Üzenetformátum cso-
portral annak a szíik interfésznek a felépítésé r61, amelyen keresztül a kommunikáciö..
háttér szolgáltatása elérhet6ek lesznek.

Az Üzer,etfonm1rulll csapat v::rlószínO"leg egy abpvető interfészt készít a Message OSZJ •


Iyokhoz, ahogy azt már korábban tettük, majd azzal a kérdéssel fog foglalkozni , h~....
l<'íroljuk és olvassuk ViSSz..1 :rz adatokat. Miután tisztáz6dtak az adauárolással kapcsol.
kérdések, máris egyeztetni lehet a kommunikációs réteghez vezet6 interfészr6l,

Az lizenctszerkeszt6 fejlesztői erős késztetést fognak érezni arra, hogya bels6 ÜZeT'
osztályok részletes felépHését szem e1Őlt tartva valósítsák meg II szerkeszt6ket, ez
azonban kifejezett tervezési hiba lelme. Nekik is egy szűk interfészen kell kommup
kálniuk :1 Message osztállYlII, az Ozenetszerkeszt6 objekrumok pedig csupán mim'......
ismeretekkel rendelkezhetnek az üzenetek belső strukru rájál illet6cn.

A tervezési folyamattal kapcsolatos szempontok


Ahogy halad a projekt , újra és újra felmerül majd néhány alapvető tervezési kém,
Melyik osztályba kenJljön egy adott funkcionalitás vagy információ? Ez a fúggH~'!"
az üzenet osztályba vagy a cím osztályba kerüljön? A szerkesztő tárolja ezt az m:
ciót, vagy az üzenet saj,ít magát menl<;e el?

Az osztályokat a titkosügynökökhöz hasonlóan csak minimális tudással érd~


házni, hogy a szükségesnél több adatot ne oszthassanak meg.
r
______________________________~2~2.~6~~~·~Ob~je=tru
~~ ~n=·'~n~=~~'=I'=mm
==s~é=s~te=~~e=~=s~I~4=55~____~I.

Tervezési döntések
A munka d<3rehaladlával százával jele ntkcwek majd tervezési ké rdése k. Lesznek amo-
lyan globális k6rdések, például »Ez mit csináljon r de akadnak majd specifikusak is
minL például "Hogyan bírjuk mO'ködésrer,

Noha az implementáció részletei:1 progmm végs6 formáj áig nem biztosak, illetve né-
hány inlt!rfész változik vagy cserél6dik munka közben is, már a fo lyamat elején is vilá-
gos tervvel kell rendelkeznünk. Létfontosságú, hogy még azelóLL megértsük, mit szeret-
nén k létrehozni, miel6tt akár egy sor progmmk6dot is írnánk. A szoftverfejlesztéssel
kapcsobtos cs6dök leggyakoribb oka az, hogya folyamat elej!.!n nem szü letett meg
a megfele l ő megállapodás arról, hogy mi is az, ami készüL

Mikor hozzunk döntéseket?


.
Hogy legyen valami viszonyítási :llaplll1k azz.'li kapcsolatban, milyen is egy tervezt!si
folya mat, vizsg:'iljllOk meg egy konkrét kérdést: mi lesz a menüben? A PostMaster-ben
az els6 va lószínl:Tleg az nÚj üzenet ír.'Ís.'l" lesz, amely viszont felvet egy újabb kérdést.
Ha ól felhasználó kiválasltja al ~Új üzenet létrchozása ~ pontot, akkor pontOS:1fl mi fog
történni? lkrrejön egy szerkeszl6, amely :lZtán lélrehozla az üzenetet vagy cl6bb It!lre-
jön az üzenet, amely ,lzután létrehozza a szerkeszt őt?

A pal"'dncs, amin dolgozunk, az Új üzene t létre hozása, tehát az (Jj ÜZ(;!I1Ct l (:trehozá~;a tű­
nik logikusnak. De mi törté nik, ha az iJzcnetírása után a felhasználó rögtön a Mégse
gombm kattint? Valószínűleg tisztáhb le nne el6bb a szerkeszt6t lé tre hozni, mely létre-
hozza (és birtokolja) az új Üzcnel(;!t.

Ennek a megközelítésnek az a hibája, h ogyasze rkesztőnek másképp kell viselkednie


új üzenet létrehozásakor és másképp egy már meglévó szerkesztésckor. Ha az üzenet
már korábban létrejött, átadjuk a szerkeszt6nek, így csak egy kód szüksl:ges, hiszen
mindig egy már mcglévó üzenetet kell szerkeszteni.

I la az üze netet hozzuk létre először, ki hozza létre? A menü p;lrancs kódj,\ houa létre?
lia igen, a m enütől kapja az Ü7.enet a szerkesztési utasítást, vagy ez a Mes sage osztály
konstm klorának része?

Első pillantásra ugyan van beJUle logika, hogya konstmktor csinálja, e lvégre ha létre-
hozunk egy üzenetel, valószím11eg szerkesZleni is szeretnénk. Mindazonáltal tervezési
szempontból ez nem jó gondolat. Először is nagyon valóslÍnű , hogy már az alapfe\te-
vésiJnk is hibás. Az ember gyakl".m küld afféle . konzerv üzeneteketn, például egy
a7. egyben továbbít a renJszergazdának egy hibaüzenetet. Ilyenkor nincs szükség szer-
kesztbre. M,ísodszor - és ez talán még az el6bbinél is fontosabb - a konstruktor fel-

I
456 1VI. rész· Különl.g.sség.'

adata :lZ objektum létrehozása. Egy rendes konstruktor ne tegyen ennél se többet, se
kevesebl>cl. Ha egy Ozenerobjektum elkészült, a konstruktor munkája véget ér. A szer-
kesztő met6clusnak a ko nstruktorból való meghívása c5<1k összekavarja a konstruktor
szerepét másrésl:l sebezhet6vé teszik az üzenet objektumot a szerkesztő hibáival szem-
ben is.

Ami pedig még ennél is rosszabb, az az a tény, ho!,')' az edit metódu5 meghív egy má-
sik osztályt - a szerkeszt6t- vagyis meghívja annak a konstnlktorát is. Ugyanakkor
a sze rkesztő nem alaposztálya a üzenet osztá lynak, nem is tanalm3zza ót :I Z üzenet
osztály, tehát tökéletesen szerencsétlen, hogy az üzenet létrehozása a szerkeszt ő
konstnlktornnak sikeres !efutásán múlik.

Végül teljesen nyilvánvaló, hogy sose hívnánk meg a szerkeszt6t, ha ll1ag:ít :lZ Ozenetet
sem sikerült létrdlo:t.ni, m:hpedig ennél a megold,ísnál éppen arról van szó, hogya lét-
rehoús sikr.::rl;! a szerkeszt6 sikeres hívását61 függ. Ez tehát (,!gy tökéletesen kjfordított
h e lyz~L Világos, hogy csak nzután hívhatjuk meg a Message : : Edit ( ) -et, miután
a ü7.enet oszttily konslmklora teljesen lefutott.

Munka a vezér1őprogramokkal
A tervezéssel kllpcsolalos hibák felderítésével kapcsolatban az egyik lehetséges meg-
közelítés lIZ, hogy már a lenrezés illetve megvalósítás kora; fázis.'iban létrehozunk egy
vezérl6progr.lmot (driver program). A vezér16program o ly:m függv61Y melynek felada-
t.1 csupán függvé nyek mllködésének bemutatása vagy ell e n őrzése. Péld;íul a PostMas-
ter vezér16progra mja rendelkezhet egy nagyon egyszen1 menüvel, mely létrehoz
PostMasterMessage objeknullokat, manipulálja őket, v,lgy más módon teszteli ;1 ter...
egyes részeit. A vezérl6programot éppen ezén szokás ~l eszthánUl ak" is hívni .

A 22.3. Lista bemutatja a PostMélsterMessage kissé robllsztllsabb vflllOZalál, valamint


egy egysze rű vezér16programot.

22.3 LII1a - A PostM....rM....ge vezé~6progr.mja Idrivllflll'Olfam.cpp)


o: II 22.3 Li~ta
l: #include <iostrcaffi>
2: ttinclude <s t ring . h>
3,
<I : typedef unsighed long pDat e ;
5: enum SERVICE { PostMaster, Interehange,
6: CompuServe , Prodigy, AOL. Internet } ;
7.

,.
8: class String

10 : public :
11 : 1/ konstruktorok
12: String();
22, 6ra • Objektum-orientált elemzés és tervezés 1457

13 : String(const char · const ) ;


14 : String(const String &);
15 : _S tring () ;
16 :
17 : II tú lterhel t operátorok
18 : char & opcratorl) (int o f fset) ;
19 : char operator(] (int offset) const ;
20 : String opcra l:or+ (const String&) ;
21 : vo i d oper~tor + =(consl String&) ;
22 : String & operator= (const String &);
23 : friend atd : : ostrearn & operator«
24 : (std : : ostrearn& theStream, String& theString) ;
25 : II alap adattagelér6k
26 : lnt GetLcn()const { return itsLcn ; }
27 : const char · GetSt.ring{) const { return it.sSt.ring ; }
28 : II stat.ic int Construct.orCount:
29 :
30 : privHte :
31 : St ring (int) ; II privát konstruktor
32: char ' itaString;
33: int. itsLen;
34 : };
35 :
36 : II az alap konstruktor létrehoz egy O bájt hOSSZÚSÁgú sztrin{Jet
37 : String: : String()
38 , (
39 : it.sSt.rlng = new char!l) ;
40 : itsSt.ring[O ] '" '\0';
41 : itsLen=O ;
42: II st.d: : cout « "\tDefault string construct.or\n";
43: II ConstructorCount++ ;
44 : )
45 :
46: 1/ privát (segéd) konstruktor, melyet csupán
47 : II aZ osztály metódusai haslmálnak üres sztringek
48: II létrehozására a kivánt méretben, Nullával töltjük fel.
49 : String : :String(int len)
50 : (
51 : j tsString • new char [len+ l) ;
52 , int i;
53 : for ( i '" O; i<,.len; i++)
54 : itsString{l) = ' \0 ' ;
55 : itsLen~len;
56 : 1/ std : : cout « "\tString(int) constructor\n" ;
57 : II ConstructorCount.++:
58 : }
59 :
60 : /1 KaraktertOmböt alakítunk át sztringgé
61 : String : : String(const char· const cString)
62 , {
63 : itsLen -- strl en (cString) ;
54 : i tsString = new char [ i ts Len+l );
65 : int i-
66 : for ( i '" O; i<i t.sLen ; i ++)
450 1VI. rész • KUlönlegos,ógek

67 : itsString[i] '" cString[i];


68: itsString[itsLen]='\O';
69 : II std: : cout « "\tString(char*) constructor\n";
70: II ConstructorCount ++;
71:
72,
73: II másoló konstruktor
74, String: : String (const String & rhs)
75 : I
76 : itsLen=rhs.GetLen() ;
77 , itsString := new char(itsLcn+ll;
78 : int i,
79 , for li = O; i<itsLen; iH)
80 , itsString[i] := rhs[i];
81 : ltsString[itsLen] = '\ 0 ';
82 : II std : : cout « '\tString(S t ring&) constructor\n',
83 : II ConstructorCount++ ;
84 :
85:
86: II destruktor, felszabadítja a lefoglal t memóriát
87: String : : -String (j
88: (
89, delet e lj itsString;
90: itsLen = O;
91, II std : :cout « "\tString destructor\n ";
92:
93:
94: String& String: : operator= (const String & rhs)
95: I
96: i f (this ... &rhs)
97 : return "this;
98: delete II itsString;
99: itsLen=rhs.GetLen();
100 : itsString ~ new char[itsLen+l];
101: int i;
102: for (i .. O; i<itsLen ; iH)
103 : itsString[iJ = rhs[i];
104: itsString[itsLen] = ' \0' ;
105 : return " th i s ;
106: II std :: cout « • \ tString operator:=\n' ;
107 :
10 8 :
109 : II nem konstans eltolási operátor, mely
110: II a karakter pozícióját adja meg, me ly így változtatható
111 : char &. Str ing : : oper ator[ ] (in t offset)
112: {
113 : i f (offset> itsLen)
114: return itsString[itsLen- l];
115 : elRO
116: return itsString[offset];
117 :
118 :
119, II konstans eltolási operátor, melyeket
120: II konstans objektumokon használunk (lásd másoló konstruktor)
f

22. óra • elemzés és tervezés

121 : char Str i ng : : operator [ ] (int offset) const


122 : {
123 : if (offset> itsLen)
124 : return itsString[itsLen-lJ:
125 ; else
126 : retu rn itsString[of fse t];
127 :
128 :
129: II létrehoz egy új sztringet, mely az aktuális sztringet
130 ; 1/ hozzáadja az rhs-hcz
131 : String String : : operator; (const String& rhsl
132 : (
133 : int total Len = itsLen + rhs ,GetLen () ;
13 4: String temp(totalLen) ;
135 : int i,j;
136 : for (i = O; i<itsLen; i+-l·)
lJ7 : tcmp!i] " itsString[i);
138 : for (j = O; j<rha.GetLen() . j++ . iH)
139 : temp[i] = rhs[j ):
140 : temp[totalLen] .. '\O·;
141 : return temp;
142 :
143 :
144 : /1 mcgváltoztatja az uktuális sztringet . nincs visszatérési értéke
145 : void String : : operator+=(const String& rhsl
146: {
147 : int rhsLen = rhs.GotLen() ;
148: int totalLen .. itst,en .to rhsLen;
149: String temp{ totalLen) :
ISO : int i , j :
151: for ( l = O; i<itsLen: iH·)
152 : templi] .. itsString[i);
153 : for ( j ,., O; j<rhs.GetLen(); j+t , iH)
154 : templi] It rhsli-itsLen];
155 : tcmp[totalLon] ='\0' :
156 : ·th is .. temp ;
157 :
158 :
159 , II int String : : ConstructorCount O:
160 :
161 : std , : ostream& operator« (
162 : std : : ostream& th oStream ,
163 : String& theString)
164 :
165: theStream « theString.GetString{) ;
166 : return theStream :
167 :
168:
169 : class pAddre ss
170 :
171 : public ,
172 : pAddress(SERVICE theService,
173 : cons t String& theAddress ,
174 : const St ring& t heDisplay) ,
I
460 VI. rész • Különlegességek

175: itsService (theService ) ,


176: itsAddres sString(theAddress ) ,
177 : itsDisplayString( t heDisplay)
178 : {}
179: II pAddress (String , String);
180: /1 pAddress() ;
161: II pAddress (const pAddress&);
182: -pAddress(){}
183: friend std : : ostream& operator« (
184 : st.d : :ostream& theStream, pAddress& theAddress) :
185: String& GetDisplayString{)
186 : ( return itsDisp!ayString ;
161 : private:
188: SERVICE itsService ;
189 : String itsAddressString ;
190 : String itsDisplayStri ng ;
191 : );
192 :
193 : std : : ostrearn& opera t or«
19 4: std : : ostrcam& theStream , pAddress& theAddress)
195 ,
196 : theStream « theAddress .GetDisplayString() ;
197 : return theStream ;
198 :
199 :
200 : class PostMasterMessage
201 : (
202 : public:
203 : 1/ PostHasterMessage () ;
204:
205: PostHasterMessage(const pAddress& Sender,
206: const pAddress& Recipient.
207 : const String& Subject,
208: const pDate& creationDate) ;
209 :
210: -PostMasterMessage(){)
211 :
212 : void Edit(); 1/ meghívja a szerkesztől erre az üzenetre
213 :
214 : pAddress& GetSender () re t urn itsSender ; }
215 : pAddrcss& GetRec i pient ( ) {return itsRccipient;
21 6 : Stri ng& GetSubject( ) {rct urn itsSubject ;
217 : /I vo i d Se tSender( pAddress& ) ;
218 : 1/ egyéb adat t age l ér6k
219 :
220 : /I operátor metódusokat i de , beleértve az egyenlőség operát ort ~~
221 : 1/ valamint a kl1l0nbóz6 konverziós rutinokat
222 : /1 melyekkel a Pos tMaster I1 zenetek más formátumra alakíthatóak.
223 :
224 : private :
225: pAcldress itsSende r ;
226: pAddress itsRecipient;
227 : String itsSubj ect ;
228 : pDate itsCrea tionDat e;
22. óra· Objektum-orientált elemzés és tervezés 1461

229 : pDate itsLastHodDate;


230 : pDate itsRecoiptDato ;
231 : pDat e itsFirstReadDate :
232 : pDate itsLastReadDate ;
233 : }:
234 :
235 : PostMasterMessage : , PostMasterHessage(
236 : const pAddress& Sonder,
237 : const pAddress& Recipient ,
238 : const String& Subject .
239 : const pDato& creationDato) :
240 : itsSender(Sender),
241 : itsRccipicnt(Recipient) .
242 : itsSubject(Subjcct) ,
243 : i t sCreationDate(creationDate) ,
244: itsLastModDate(creationDate) ,
245 : i t sFirs tReadDate (O) ,
246: i t sLastReadDate(O)
247:
2 48 : s td: : caut « " Post Master Message created . \0" ;
249 :
250 ,
251 : void PostMasterMessage : : Edit()
252 : {
253 : std :: cout« "PostMasterMessage edit function callod\n";
254 :
255,
256:
257 : int main (l
258 ,
259 : pAddress Sender(
260: PostMaster, " j1ibcrty@PostMaster", "Jesse Liberty"):
261: pAddress Recipient (
262: PostMaster, "s1ibcrty@PostMastor",'Stacey Libcrty") :
263 : PostMasterMess8ge PostMasterMessage(
264 : Sender , Recipient, 'Saying Hello', O) ;
265 : std : : COllt « "Messaqe review . . . \n " ;
266 : std : : cout « "Prom : \ t \t'
267 : « PostMasterMQS1HI.ge .CotSender () « std :: end1 ;
268 : std : : cout « ' Tc : \ t \t·
269 : « PostMasterMessage .CetRecipient () « std :: endt :
270 : std : , ccut « "Subj ect : \ t."
271 : « PostMasterMessage.GetSubject() « std : : endl;
272 : retur n O;
273 :

Post Master Message created .


Me ssage review ...
From : Jesse Libcrty
To : Stacey Liberty
Subject : Say ing Hello
462 1VI. rész • Különleges,égek

A 4. sorhan a pDate típust előjel nélküli long-nak definiáltuk. Nem ritka , hogya dátu-
mokat long típusú egészként tárolják. Gyakran ez olyan önkényes d5tumokt61 , mint
például 1900 január 1 . , eltelt másodpercek sz:ímát tartalmazz<l. Progr::.munkIYd.n
csupán a helye van meg, de később a pDate osztályból valódi osztályt is c.sinálhatunk.

Az 5. sorban a SERVICE egy konstans felsoroMs, mellyel követhetjük, milyen típusú


a dm objektum: Post\'vIaster, CompuServe és így tovább.

A 8. és a 167 ..~or között találjuk a String interfész{:t, valamint implcmentáci6j;n, szá-


mos oly:tn résszel egyetemben melyekkel az előző leckékben már találkozhattunk,
A String oSj(,tályl az üzenet osztály és az üzenet osztá lyt használó egyéb osztályok
hasznMják, így programunk lélfonLosságú eleme. Az üzenet osztályok teljességéhez el-
engedhetetlen egy teljes (:s robusztus String oszt5ly.

A pAddress-t:l 169. sortól a 191. sorig terjede; szakaszban dekJa ráltuk. Ez cstlpán
:lZosztály alap funkd onalitás{ll adja és bővíthet6 , ahogy egyre jo bban megénjük
a programot. Ezek az objektumok lét.fontosságú elemei minden üzenemek: a feladó és
a dmzell <:Íme is ilyen típusÚ. Teljes funkcionalitá ssa.I bíró pAddress objekulmmal old-
juk meg lovábbítoll, megválaszoll, slb. levelek kezelését.

A pAddress objcktum Celadala a megjelenítell sZl"ring és a bels6 l!tv<'l.lasztÓ sztring kö-


vetése :I Z adou szolgáltatáshoz. Egy megválaszolatlan ké rdés, hogy csupán egy
pAddress objektum legyen , vagy minden szolgáltatástípushoz legyen megfel el6 310sz-
t~ílya? Jelenleg a szolgáltatást egy ko nstans felsorolásból vál:ISZljuk ki és "Address ob-
jekUlmban adauagkéOl tároljuk.

A 200. és 233. sorok között találjuk a PostMasterMessage osztály interfészét. A listá-


ban az osztály még magában szerepel, de hamarosan az örökl6dési hierarchia részé\'é
tchetjük. l-la üjratcrvezés során a Message osztályból származtat nánk, néhány tagváltG-
zó bekcriilhctne az alaposztftlyba, illetve néhány aJaposzt5!ybeli tagfüggvényt felülbí-
ráIhatna agyerekosztá ly.

Az oszt{tly tc\jt:s L-nékú múködéséhez szükség lesz számos konstruktorra, adattageléro


függvényre és egyéb lagfüggvényre. Ez a programlista arra hívja fel a figyelmet, hogy
nem kelll00%-0s állapotban lennie a programunknak ahhoz, hogy vezérl6prograIllOl
írhassunk és néhány szempomból tesztelhessük azl.

A 251-254 sorokban egy csonka Edit () függvényt találunk, csupán jelezlük, hOY3 ke-
rüljön a függvé ny, ha az osztályunk már teljesen működőképes lesz.
____________________________~2~2~,6~~~·~O~bj~e~~m~-o~n~·e~má~l~te~le~~
~s~é~s~re~N~.~ú~s~
1 4~63~______~1 1

A vezérlőprogmffiol a 257. és 273. sorok között runtettük fel. Jelenleg II program nem
csinál mást, mint meghív pár adattagc1ér6 függvényt és a túlterhelt « operálott
(oper-ator«). Mindazonáltal megfelele; kiindulópontot nyújt a PostMasterMessage és
II kerelrendszerrel való kisérletezéshez - osztályok módosítása és a módosítások hatása.

Kérdések és válaszok
Kerdc.>S: Miben kIII6nbózik a!apvetöcn az objcktum-orielllált elemzés és leroczés ti főbbi
II/egMzelítést6l?

\fa/asz: Az objektum-orientált prog'ramozás előtti technikák esetén az elemZők és


a progmmoz6k úgy tekintenek II programra, mint függvényekrc, melyek műveleteket.
hajtamlk végre adat.okon. Az objektum- orientálL programozás úgy tekint <IZ adat.okra
és a velük kapcsolato." műveletekre, minl valam iféle egési'.re. Minden objektum tartal -
maz információkat (;ldat) és k6pess6geket (függvények) is. A proceduru lis progr::lmo-
zás ezzel szemben a rüggvényekre koncentrál és <Irra, hogyan hatnnk ezek ;lZ adatokra.
Úgy tartják, hogy míg Pascal és C prog ••unok eljárások, addig a C++ programok osztá-
lyok gytíjteményei.

Kérdés; Igaz-c az (IZ állítás, 1I0&)1 az objekwm-orientált programozás cm1ajfCI vC'gso


megoldás az 6sszes programozási problémára?

FáIasz: Nem, 56t soha nem is szánták annak. Az viszont igaz, hogy nagy. összetett
problémákná l az objeknun-orientált elemzés, tervezés és programozás olyan segédesz-
k~Zl ad a progmmoz6k kezébe, mellyelleküzdhet6ek a korábban lckilzdhetetlennek
hiu akadályok is.

Gyakorlatok
Ebben a leckében az objektum-orientált elemzést és tervezést sajátíthattuk el. V!ilaszol-
junk most néhány kérdésre és oldjuk meg a feladatokat, hogy elmélyítsük II megszer-
zeLl tudást.

Kvfz
1. Programoz:1s megkezdése előtt mit kell termünk?
2. Mit nevezünk alkalmazásfejleszt6i felülernek (Application Progmmming
Interracei AP!)?
3. Mit csinál a vezér1őprogl'"dm?
4. Miért előnyös a nagy alkalmazásokat apróbb részekre bontani?
464 1VI. rész • Különlegességek

Feladatok
Ne feledjük, ti problémák megoldásai megralálhat6k a CD-n.

1. Válasszunk egy kis problémát a ffiunkánkból vagy a mindennapi élellX51 és hajt-


suk végre rdjta a leckében bemutatott lépéseket.
2. M6doslL'iuk a 22.1 LiSl ál (simpleevent . cpp) úgy, hogy minden destruktort bő­
vlL"ünk egy az std: : cout küldött (izenettel. Futtassuk le a programot és fl&'Ye1-
jük, mikor futnak le a destmktorok . Azt már tudjuk, mikor futnak le
a konstruktorok, hiszen a napló üzenet megjelenik.
3. Keressünk llZ [nterneten C++ osztályokat és függvény könyvtárakat. Néhányat
mcgvizsgálva gondolkozzunk el azon, hogy megvás6.rolnánk v~lgy inkább meg-
írnánk?

Válaszok a kvfzkérdésekre
1. Kengeteg tennivalónk van a programozás megkezdése cl6tt: igényfelmérés (mit
akar a végfelhasznál6?), elemzés (mi szükséges az igények kielégítéséhez?), és
tervezl!s (hogyan valósíthatjuk meg a kívánt funkci6k:it?). Sajnos lú l sok cég
gondolja ügy, hogy . Ha már itt ol ez a sok programozó, hát :Ikkor programozza-
nak. Kl érdekel, hogy nem teljes az igénylisca,"
2. Az API egy dokumenrum és eljárás gyűjtemény, mely lehct6vé teszi egy szolgál-
tatás használatát. Nem kellrudnunk, hogyan mt.1ködik a mögötte álló kód, csu-
pán használjuk. A megvalósítás részletei rejtve maradnak el6ttünk.
3. A vezérl6program segítségével kipróbálhatjuk és demonstrálhat juk a program
már mesval6sítou funkcióit, illetve tesztelhet jOk az osztályokat fejlesztés közben
Noha ez a program rendszerint teljesen nyers és egyszen1, segítségével megfi-
gyelhetjOk a program viselkedését és az elemei között fe llép6 kölcsönhatásokai..
4. A részekre bontás egyik legnagyobb el6nye az, hogy több ember (vagy csopon
között oszlik meg a munka. Amíg mi az egyik osztályon dolgozunk, addig más-
valaki egy másik osztállya I foglalkozhat.
23. ÓRA

Sablonok
Ebben az órában a következ6k,61lesz szó:
• Mik a sablonok és hogyan használjuk őket?
• Mil6\ jobbak a sablonok a makr6knál?
• Hogyan hozzunk létre osztálys.'lblonokat?

Mik azok a sablonok?


A 19. ó rában megtanulluk, hogyan készítsünk láncolt listákat, amelyeket szépen bcc.:sa-
magoltunk, szakzsargonnal befogla ltuk (encapsulation): A lista csak a kezd6muLatór61
tudott, ami delegálta a munk:'ít a belső mUlatóknak, stb.

Az egyetlen zavaró tényez6 az voll, hogy a láncolt listánk csak egy adott típusú objek-
tumot tudott kezelni. Más típust sajnos nem tudtunk beletenni. Nem tudtunk például
Car vagy Cats objektumokat tárolni benne, és semmi mást sem, ami nem egyezett
az eredeti listában lévő tíPUSS,1 1.
466 1VI. rész • KOIönl.ges.ég.k

Korábban ezt úgy oldolták meg, hogy az új adattípusokhoz az objektum új verzióját


készítették eL Az objektum-orientált világban egy List ósosztályból származtathatju k
a CatsList osztályt, és a LinkedList osztály kódjának jelentős részét felhasználhat-
juk a Cats List deklarációjában. I-Ia azonban később Car objekru mok tárolásám akar-
juk befogni újdonsült láncoltlistánkat, az öröklést meg kell ismételnünk: új CarList
osztályt kell létrehozni , amelybe a meglévő kód egy része átmásolható.

Egyik megoldás scm tűnik üdvözítőnek. Az idő múlásával a List osztályt majd b6víte-
ni kelt , és rémálom lesz ezeknek a változásoknak az érvényesítése a már létezt> szár-
maztatou osztályokban.

A C++ egy viszonylag új adal(:ka, a sablon megoldást jelent a gondunkra. A divatját


múllmakr6kkal szemben ráadásu l a nyelv szerves részét képezi, vonatkozik rú ti lípus-
ellen6rzés, és rendkívül sokoldal(lan használható .

Lchetúvé teszi, hogy létrehozzu nk egy általfinos célú osztályt, majd egy parametrizált
típUSt íitadv;l, a sablon aZt példányosítja.

A sablon példányosftása
Sablon segitSégével megtaníthaljuk II fordítól, hogy ne cS3k néhány el6re megadott tí-
pusból hozzon Iéire list.'it, hanem bármiból. A PartsList Part objeknunok listája,
a CatsList Cat objeknllnok listája. Az egyetlen dolog, amiben különböznek az, hogy
milyen típusú dolgoka! szervezünk listába. A sablonok esetében a tárolandó típus
az osztálydefiníci6 paramétere lesz.

Példányosításnak hívjuk azt, amikor (egy osztályból) objektu mot, illetve egy sablonból
konkr(:t típust hozunk létre. Az így létrejött egyedi osztályok :I s:lblon példányai.

A sablon definfciója
A parametrizált List osztályt (listasablont) a következő' módon dekJaráljuk:
template <class T> II a sablon és a paraméter deklarációja
class List II a parametrizált osztály

public :
List () ;
II az osztály teljes deklaráci ója
} ,
A sablonosztályok deklarációját és definícióját minden esetben a template kulcsszó
cl6zi meg, amelyet :I példányonként változ[atható paraméter kövel. Az előbbi kódrész-
leIben bemutatottlistasablonban például a listában tárolt objektumok típusa változhat.
Az egyik listapéldányba tehetünk Integer, míg egy másikba AnimaI objeknllllokat.
Példánkban a c lass kulcsszó jelzi, hogy a pamméler egy típus. Ezt követi a T azonosí-
tó, amellyel a továbbiakban a parametrizált típusra hivatkozunk a sablondefinícióban.
A lista egy példánya tehát a T azonosítót mindenütt az int, míg egy másik a eat típus-
sal helyettesíti.

A parametrizált lista int és eat típusú objektumokat tárol6 példányait a következő


módon deklaráljuk:
Li st<int> anl ntList;
List<Cat> aC~tList ;

Az anlntList objektum típusa int , a CatList objektumé Cat objektumokat tároló


lista. Mostantól már használható a List<int> típus bárhol , ahol a szokványos típusok
is el6fordulnak: függvé ny visszatérési értékénél, függvény paramétereinél stb.

A 23.1 List{tban a Lis t objektumot pllnun éterezzük. Ez kiv{116 techinka sablonok gyár-
tás{tlloz: El6ször bírjuk működés re az objektumOl egy típussal, ahogy ezt a 19. órán tet-
tük, majd általánosílsuk, hogy bármely típust kezel ni tudja.

23.1 ulla - A parametrizált listjk bemutatna lIá••oh listi.all Ipannli"'.pp)


o: II •••••••••••••• • • _ •••••••••••••••••••••••••••• • *
l : /I 23 . 1 példa
2 : II
3 : II Paraméterezett lista bemutatása
4: /I
5 : /I
6 , II
'1 : II
8 : /I
9 : 1/ Paraméterezett lista objektum-orientált megval6sitása .
10 , /1 A List a z absztrakt Node táro16elcmnek delegálja a munká t .
ll : II Háromféle tárol6elemet használunk , ke~dot, zár6t és
12 , II kOzbnlsot . Csak ez ut6bbiban tárolu nk hasznos objektumot .
13 , II
14 , II
15 , II A Data os ztályt azér:-t hozwk létre , hogy példánya i t
16 : 1/ a listában t á roljuk.
1 7, 1/
IB , 1/ *•• **.**. *.** .**.*.**.* •• **.**.*.**. ** ***.*.***
19 : #include <io stream>
20 :
21 , enum { kl s Sma lle r , kls Large r, kIsSame } ;
22 :
23 , II A li stában t.á rolhat6 Data osztály
2 4: 1/ Minden olyan o sz tálynak , melye t ebben a listában s ze r e tnénk
... t.árolni , rendelkeznie kell a következo két met6dus sal":
25 : /1 Show: Kiir ja az objektum értékét .
26 : /1 Compa re : Összehasonlítja két ob j ektum é rté ké t,
... é s meghatá t"o zza a r e lat i v poz i ci6t.
468 1VI. rész • Különlegességek

27 , class Data
28 : {
29 : public ,
30 : Data(int val) : myValue(val) {}
31 : -Data()
32 : {
33 : std :: cout « "Data objektum tOrlése. melynek értéke :
34 : std::cout« myValue« " \n" ;
35 :
36 : int Compare(const Data &) ;
37 : void Show() { std : : cout « myValue « std: :endl; }
38 : privatc:
39 : int myValue ;
40: J;
41 :
42 : II A Compare metódust arra használjuk , hogy egy adott objektum
4 3 : II listábun elfoglalt helyét megállapitsuk.
44: int Data : :Compare(const Data & theOthcrObject)
45 : (
46 : if (myValue < theOtherobject . myValue)
47 : return kIsSmaller ;
48: li (myValue > theOtherObject.myValue)
49: return kleLarger;
SO : else
51 : return kIsSame ;
52 :
53 :
54 : II Egy másik osztály, amit szintén a listában kívánunk tárolni
55 : II Ebben a láncolt listában is a korábban említett két met6dusra
56 : II lesz szakség :
57 : II Show : Kiírja az objektum értékét .
58 : II Compare: Osszehasonlítja két objektum értékét. és relatív
.. podci6t ad vissza .
59 :
60: class Cat
61 : {
62 : public :
63 : Cat(int age} : myAge(age} (j
6 4: _Cat()
65 : {
66 : std : : cout « myAge « " éves macska tOrlése\n" ;
67 :
68 : i n t Compare (const Cat &) ;
69 : void Show()
70 :
71: std , : cou t « "Ez a macska':
72 : std : : cout « myAge « • éves\n":
7] :
74 : private:
75: int myAgű ;
76 : } ;
77:
78 ,
1
23, óra •

79 : II A Compare met6dust arra használjuk , hogy egy adott objektum


80 : II listában elfoglalt helyét megállapits uk .
81 : int Cat : :Compare (const Cat & theotherCat )
B2 :
B3 : if (myAge < theO therCat . my Age)
B4: return k IsSmaller ;
BS : i f ImyAge > theO therCat .myAge)
86 : return kIsLarger;
87 : else
88 : return kIsSame;
89 :
90:
91:
92 : II A liűta táro16objektumának absztrakt adattipusa
93 : II Minden származtatott tipusnak falűl kell defint~lnia Sz Insert
.. és Show met6dusokat
94 : tcmplate <class T>
95 : class Node
96 : {
97 : public :
98 : Node () ()
99 : virtual -Node I) I)
100: virtual Node ~ InsertIT· theobject)zO;
101: virtual void Showl) = O;
102: private :
103: };
104:
105: ternp1ate <class T>
106 : class Interna lNode : public Node<T>
107 :
lOS : public :
109 : InternalNode(T * theObjcct , Node<T> • next);
110 : -Interna1Nodel){ deleta n~Next; delete myObject ;
lll : virtual Node<T> • InsertiT · theobject) ;
112: virtual void Show ( )
ll3: (
114 : myObject - >Show():
115 : myNex t->Showl);
116 : II delegálás
117 : private :
US : T • rnyObject ; II Maga az objektum
119 : Node<T> * myNex t ; II a lista kOvet kez6 t áro16e l emére muta t
120 : ) ;
121 :
122 : II A konstru ktor csak inicializál
123 : tcrnplate <class T>
124 : InternaINode<T>:: InternalNode IT * theObject, Node<T> * nex t) :
125 : rnyObjectltheübject),myNext(next)
126: (
127 : )
128 :
129 : II A lsita velej e
130 : II Amikor a l i s t ába új objektumot tes zü n k,
131 : II átadju k a táro16elemnek , amely ki ta l álja
470 11n, rész • Kiilönlegesség.k

132 : /1 a helyét. és beszúrj a a listába.


133 : templat c <class T>
134 : Node<T> ~ InternalNode<T> : : Insert(T * theObject)
135 :
136 : /1 Az új szerzet kisebb vagy nagyobb nálam?
137: i nt rcsult = myOhject->Colnpare(*thcObjec t) ;
138 :
139 : switch(result)
140 : {
141 : 1/ 11. tradició szerint, ha egyen16ek vagyunk, övé az els6bbség.
14 2 : case kisSame : / I {gy ez az ág átvezet arra az esetre,
.. amikor nagyobb nálam
143: case kisLarger: 1/ Az új objektum elém kerül
144 : (
145: IntcrnalNode<'I'> • ObjectNode =
146 : new InternalNode<T>(thcObject , t his):
147 : return Objcc t Nodc l
1.48 :
149 : II Nagyobb nálam , tehát adju k át a kOvetkez6 táro16elcmnck
150 :
151 : case kisSmaller :
152 : myNext = rnyNext->Insert(thOObjcct) ;
153 : return this;
154:
155, return this;
156,
157:
158 , II A Tail tárolóelern csak őrszem szC!repet játszik
159 , template <class T>
160: class Tai1Node : public Node<T>
161 : (
162 : public :
163: TailNoda(){}
164 : virtuel -TailNode(){}
165 , virtual Node<T> • Insert (T .. theObject) ;
166 : virtual void Show() { }
167 : private:
168 : }:
169 :
170 : II Ha az Object típusú objektum hozzám kerül, mindenképpen elém
171: /1 lesz beszúrva, hiszen én vagyok a zár6elem ,
.. aki IllOgOt t már semmi sincs.
172 : templat e <class T>
173 : Node<T> • TailNode<T> :,Insert( T .. LheObject}
174 :
175 : Interna1Node<T> • ObjectNode =
176 : new InternalNode<'J'>(theObject, this);
177: return ObjectNode;
178 :
179 :
180 , II A kezdd táro16elemben nincs objektum,
181 : 1/ csupán a lista 1ege ls6 el emére mutat
182 : templat e <class T>
183 : class HeadNode : publ ic Node <T>
23. óra • Sablonok 471

184 :
185 : public:
186 : HeadNode();
187 : virtual ~HeadNode() { delete myNext; }
188 , virtual Node<T> * Insert(T * theObject) ;
189 : virtual void Show{) ( myNext~>Show() ; )
190 : private:
191 : Node<T> ~ myNcxt;
192 : };
193 :
194 , / / Mihely 16trejön ti kezd6elem,
195: / / az nyomban maga mögé teszi zár6elemct is
196: template <claGG T>
197 , HeadNode<T>: : HeadNodc()
198: {
199: !lIyN!:lxt : new TailNode<T> ;
200 :
201 ,
202 : /1 A k ezd6elem el6tt nincs semmi, :így az objektumot
203 : II rögtön továbbadhatjuk a k övet kez6 táro16e l emnek .
204 , template <class T>
205 : Node<T> * HeadNode<T> :: Insert (T • theObject)
206 : (
207 , myNext : myNext - >Insert(theObject) ;
208 : return this;
209 ,
210:
211 : II Minden munkát delegálok, de enyém a dics6ség
212 : template <class T>
213 : class LinkedList
214 , {
215 : public :
216 , LinkedList();
217 : -LinkedList() { deiete myHead ; }
218 : void Insert(T ~ theObject) ;
219 , void Showl\l1() ( myHead->Show() ;
220 : private :
221 : HeadNode<T> * myHead ;
222 : };
223 ,
224 : II Születésemkor létrehozom a kezd6eleme t . a mely maga mögé
225 : II h e lyezi a zár6elemet .
226 , II Az üres lista tehát egy kezd6e lcmb6l áll . amelyet azonnal a
227 : /1 zár6elem követ , köztük pedig ninc s semmi .
228 : template <class T>
229 : LinkedList<T>: :LinkcdList()
230 : (
231 : myHead = new HeadNode<T> ;
232 :
233 :
234 : 1/ oelegálj , delegálj, delegálj
235 : template <class T>
236 : void LinkedList<T> :, Insert(T * pObject)
237 : {
472 1VI. rész • KilI6nl'llosségek

238 : myHead->Insert(pObject ) ;
239 :
240:
241 : 1/ Tesztprogram
242: int main ()
243:
244: C~t • pCat;
245 : Data * pData;
246: int val ;
247 : Li nkedList <Cat> ListOfCats ;
248 : LinkedList <Data> ListOfOata:
2 4 9:
250: 1/ a felhaszná16t61 bekérűnk pár értéket
251: 1/ és elhelye2:zük azokat II l i stában
252 : for (;;)
253 : {
254 : std :: cout« "Adj meg' egy értéket (O vége): ";
255 : std :: cin »va l;
256 : i f (ival)
257 : break:
258: pest ~ ncw Cat(val);
259: pOata: new Data (val) ;
260: ListOfCats.lnsert(pCat);
261: ListOfOata.lnsert(pData):
262:
263 :
264 : II végigmegyOnk a listán és kiírjuk az e l emek értékét .
265: std: : cout « ' \n ' ;
266: ListOfCats.ShowAll();
267: std: :cout « "\n",
268: ListOfData.ShowAll();
269 : std :: cout « "\n ****,,***." •• \n\n";
270: return O; II A listák kikerülnek a hatókOrb61 , és megsemmisülnek.
271:

Adj meq értéket (o vége) : 5


Adj
Adj
meg egy
meg egy
""" é'rtéket
értéket
(o
{o
vége)
vége}
:
:
13
2
Adj meg egy értéket {o vége} : 9
Adj meg egy értéket (o vége) : 7
Adj meg egy értéket (o vége) : O
E, a macsk.!! 2 éves
a macska 5 éves
"'
E, a macska 7 éves
a macska 9 éves
"'
E, a macska 13 éves

Data objektum törlése, melynek é rt éke: 13


Dato objektum t ö r lése, melynek é rtéke : 9
Data objektum törlése, melynek értéke: 7
23. óra • Sablonok 1473

Data objektum törlése, melynek értéke: 5


Data objektum törl ése, melynek értéke : 2

13 éves macska t órlés e


9 éves macska t Or lése
7 éves macska törlése
5 éves macska t ö dés e
3 éves macska tOrlé se

Mindenekelőtt, vegyük észre, mennyire hason lít a 23.1 Lista a 19. órán bemutatOllhoz.

A legnagyobb változás, hogy minden osztály- és metódusdcklaráci6 el6tt a következ6


preAx áll:
Lempl ate c 1ass <T>

Ebb6 1 ludja II fordító, hogy olyan típussal parnmétereztük a listál, amelyet c&1.k a példá-
nyosítás pillan:náhan fogunk megmondani. A lárol6elem Node osztályának deklarációja
most például íh'Y néz ki:
template <class T>
class Node

Eszerint :1 Node osztály önmagábrtn nem létezik, de példányosítani fogjuk a Cats és


a Data oszlállyaL A paramélCrczeshez használt típust T jelöli.

Hasonlóail, az Interna lNode helyett most InternalNode<T> áll (T típus Inter-


nalNode - ja). Továbbá, InternalNode<T> nem egy Data objeklumra es egy másik
Node-ra mutat, h.mem egy T típusú objektumra és egy Node<T> objektumm. Ez lámató
a 118. és a 11 9. sorban.

Nezzük meg alaposabban a 133-156. sorban definiált Insert mel6duSl. A logika


ugyanaz, de ahol korábban konkrét típust (Data) használrunk, Olt most T szerepel. Te-
hát, a 134. sorban a paraméter mulató egy T típusú objekrumra. Később, a példányosí-
t{\skor, a fordító T helyére behelyettesíti II megfelel6 típust (Data és Cats).

A lényeg, hogy az Internal Node az adattípustó függetlenül elvégzi a feladatát. Tudja,


hogyan hasonlílSa össze az objektumokat. Egyáltalán nem érdekli, hogy a Cats objek-
tumok pontosan úgy hasonlítják össze egymást, mint a Data objekrumok. Valójában,
a Cats osztályt újraírhatjuk úgy, hogy a kort nem tárolja, hanem a születési id6ból
szükség szerint kiszámítja. Az InternalNode-ól ez egy cseppet sem érdekli.
474 1VI. rész • Külön~g.sség.k

Sablontípus használata
A sablontípus úgy használh:tt6, ahogy a többi típus. Átadható függvénynek paraméte r-
k~n\, de lehet vis.~zaté rési érték is, mindkét esetben énékkel vagy referenciávaL Ezt
mutatja be a 23.2 Lista. Hasonlítsuk össze a 23.1 Listával!

23.21J1111- Pa_1Isto ~......... (16aCOIt- _ _ 6YoI1


f--. l
1 : II 23 . 1 példa
2 : II
3 : /1 Paramétere2ctt lista bemutatása
4: / I
5 : II
6 : II
7, / I
8, / I
9: II Paraméterezctt lista objektum-orientált megva16s1tása.
10, II A List az absztrakt Node táro16elemnek delcgálja a munkát.
11: II Háromf61c táro16elemet használunk: kczd6t. zár6t és
12, II kOzba!s6t. Csak ez utóbbiban tárolunk hasznos objektumot.
13 : /1
14 : II
15 : II A Data osztályt azért hozzuk létre , hogy példányait
16 : II a listában tároljuk.
17 : II
18 : II **** ************** ********* **.**** ****** -******
19 :
20 : 'inc1ude <iostrcam>
21 :
22 ; enum { kIsSma1ler. kIsLarger , kIsSame} ;
23 :
24 : II A listában tárolható Data osztály
25 : II Minden olyan osztálynak, melyet ebben a listában szeretnénk
.. tárolni , rendel kezni e kell a kOvetkez6 két metódussal ,
26 : II Show : Kiírja az objektum értékét.
27 : II Compare: OsszehasonH tja két objektum értéké t, és meghatározza
_ a relatív poziciót .
28 : class Data
29 :
30 , public :
31 , Data{int val),myValue{val){)
32 : -Data ()
33 : (
3 4: std : : cout « 'Data objektum tOrlése, melynek értéke: ';
35 : std: : cout « myVa 1ue « '\n ' ;
36 :
37 : int Compare(const Dat a & ) ;
38 , void Show() ( std : : cout « myValue « std : : endl ; )
23. óra • Sablonok 1475
- - -- - - - - - - - -- -- --=-=---==""'-''----------'..
39 : prlvate :
40 : int rnyValue ;
41 : J;
42 :
43 : II A Compare met ódust arra hasz ná ljuk, hogy egy adott objektum
44 : II listában el f ogl al t helyét megá l l apit suk .
45 : i nt Da t a : : Compa re (const Data & t h e OtherObj ect )
46 : {
47 : i f (myVa l ue < theOthcrObject.rnyValuc)
48 : return kIsSmaller ;
49: if (myValue > theOtherObject . myValue)
50 : retu r n kIsLarger ;
51 : else
52 : return kIsSame;
53 : l
54 :
55 : 1/ Egy másik osztá l y , ami L szint én a listában kívánunk tárolni
56 : II Ebben él láncol t lisLában i s a korábba n említett ké t met6dusra
57 : II l esz szű k ség :
58 : /1 Show: Kiírja az objektum értékét .
59 : /1 Compare : Összeha sonlítja két objektum értékét,
... és relatív poziciót ad vissza .
60 :
61 : class Cat
62 : ( r.~l
63: public :
64 : Cat(int age) , myAge(ag-e) {} ~:;~:<
65 : -Cat()
66 : (
67 : std : : cout « !nyl\ge « ' éves macs ka t Orlése\n" :
68 ,
69 , int Compa r e (const Cat &.) :
70 , void Show()
71 ,
72 , std : : cout « " E'Z II macska • .
73 , std : : cout « myAge « • éves\n" ;
74 , }
75 : private :
76 : int myl\ge ;
77 : } ;
78 :
79 :
80 : II A Compa r e met6 dus t arra ha!:\z ná l juk, ho gy egy adot t obj ekt um
81 : II listában e lfogla l t h elyé t mcgá 11api tsuk .
82 : int Cat : : Compare (const Ca t & theOthe r Ca t)
83 : {
84 : if (myAge < theOtherCat.myAge)
85 : re t u rn kI ssmaller ;
86 : if (myAg e > theOtherCat.myAge )
87 : r e t u r n ki sLarger ;
88 : else
89 : retu rn kis same :
90 : }
91 :
476 1VI. rész· Különlegességek

92 :
93 : II A lista tárol60bjektumának absztrakt adattipusa
94 : /I Minden szá rmaztatott tipusnak felal kell d efiniálnia
.. a z Insert és Show metódusokat
95 : template <class T>
96 : class Node
97 :
98 : public :
99 : Node(){}
100 : virtual -Node () {}
101 : virtual Node * Insert{T theObjectl"'O;
102 : virtual void Show() '" O,
103: private :
104 : ),
105 :
106: template <c l ass T>
1 07: class Int crnalNode : public Nodo<T>
1 08 :
109 : public :
110: Interna lNode( T * theObj ect, Nodo<T> * n e x t) ;
111: virtual -I n ternal Node () ( delete IllyNext ; delete myObject : l
112 : virtual Node<T> * I nser t (T * theObjectl ;
113 : virtual vo i d Show () I I delegálás
114 : (
115 : myObject->Show() , myNext->Show() :
116 :
117 : private :
118 : T * myObject ; II Maga az objektum
119 : Node<T> * myNext : II a lista kOvetkez6 t6ro16clcmérc mutat
120: ) ;
121 :
122: II A konstruktor csak inicializál
123 : template <class T>
124 : InternalNode<T> : : InternalNode (T * theObject, Node<T> * next) :
125: myObjcct(theobject) .myNe x t(next)
126 : {
127 : }
128 :
129: II A lista veleje
130 : II Amikor a listába új objek tumot tes zün k ,
131 : II átadj u k a táro16elemnek , amely kitalálja
132 : II a helyét . és beszúr ja a l iRt ába .
133 : template <c l ass T>
134 : Node<T> * I nternalNode<T> :: Insert (T ~ th eO b jec t )
135 : (
136 : II Az új szerzet kisebb vagy nagyobb nálam?
137 : int rosul t '" myObject - >Compare( *theObject) ;
138 :
139 : switch{rcsu1t l
140 : {
141 : II A tradició szerint, ha egyen16ek vagyunk , 6vé az els6bbség.
142 : case ItlsSame : II ez az ág átve zet arra a z esetre,
.. amikor nagyobb nálam
23. óra • Sablonok 1477
143 : case kIsLarger : /1 Az új obj ek tum e lém ke rul
144 : (
145 : InternalNode<T> * ObjectNode :
146 : new InternalNode<T> (theübj ect, this) ;
147 : return ObjectNode;
148 :
149 : /1 Nagyobb nálam. tehát adjuk át a következ6 táro16elemnek
150 :
151 : case kIsSmal1er:
152 : myNext = myNex t->Insert( t heübject) ;
153 : rcturn this ;
154 :
155 : rcturn thic ;
156 :
157 :
158 ,
159 : /1 A Tail táro l óe l em csa k úrszem szerepet játszik
160 : temp] ate <class 'I'>
161: class TuilNode : public Node<T>
162 : (
163 : publ ic :
164 : TailNode() (l
165 : vir t ual ~TailNode() (j
166 : virtual Node.:T> * Insert(T * theobject) ;
167 : virtual void Show { ) { }
168 : privata :
169 : );
170:
171 : II Ha az Object tipu!'Iú objektum hozzám kerül, mindenképptm elém
172: II lesz beszúrva, hiszen én vagyok a zár6e1em, aki mOgOtt
.. m6.r semmi sincs ,
17] : temp1ate <class T>
174 , Node<T> ~ Tai1Node<T> :: Insert(T • thCObject)
175 : (
176 : Interna1Node<T> • ObjectNode ~
177 : new Interna 1Node<T>(thCObjec t, this) ;
178 : return ObjeCtNOde ;
179 :
180 :
181 : II A kezd6 táro16e1emben n incs ob j e k t um,
182 : II csupán a l i s t a lege1s6 e l emé re mu t a t
183 : t e mplate <class '1'>
18 4 : c l ass HeadNod e : p ub l ic No de<'l'>
185 :
186 : p ublic :
1 67 : Head Node{) ;
168 : virtual -HeadNode() { d e lete myNext; }
169 : virtual Node<T> .. I nsert {T * t heübject) ;
190 : virtual void Show () ( myNe xt- >Show () ; }
191 : private :
192 : Node<T> * myNe x t ;
193 : };
194 ,
I
478 VI. rész • KlIlönl.g.sség.k

195 : 1/ Mihelyt létrejön il kezd6 elelll, az nyomban


196 : 1/ maga mögé teszi zár6elemet is
197 : template <class T>
198 : HeadNode<T> : : HeadNode (J
199: (
200 : myNext '" new TailNode<T>;
201 :
202 :
203 : II A kezd6elem e16tt nincs semmi, igy az objektumot rOgtOn
204 : II továbbadhatjuk a következő táro16clemnek.
205 : template <class T>
206 : Node<T> • HeadNode<T> :: Insert(T " theObject)
207 , {
208 : myNcxt ., myNcxt->Inscrt (thcObject) ;
209 : return this ;
210 :
211 :
212 : II Minden munkát delegálok, de enyém a diCsőség
213 : template <class T>
214 : elauG LinkedLi ot
215 :
216 : public :
217 : LinkedList() ;
218 : ~LinkedList() ( delete myHead; )
219 : void Insert (T " theObject) ;
220: void ShowAll() ( myHead->Show() ;
221 : private:
222 : HeadNode<T> ~ myHead;
223 : );
224 :
225: II SzOletésemkor létrehozom a kezd6elemet, am~ly maga mOgé
226: II helyezi a zár6elemt.
227: II Az Ores lista tehát egy kezd6elemb6l áll . am~lyct azonnal a
228 : II záróelem kOvet. kOztOk pedig nincs semmi.
229: templat~ <class T>
230 : LinkedList<T> : : LinkedList()
231 : (
232 : myHead =: new HeadNode<T> ;
233 :
234 :
235 : /1 Oalogál j . delegálj , delegál j
236 : tcmplatc <c la ss T>
237 : void LinkedList<T> : : Insert( T * pObject)
238 : (
239 : myHoad->Inser t (p Object) ;
240:
241 :
242 : void rnyFunction(Lin kedList<Cat>& ListOfCats);
243 : void myOtherFunc tion (LinkedList< Data>& ListOfData);
244 :
245 : II Tesztprogram
246: int main{)
247 :
248 : LinkedList<Cat> ListOf Cats ;
23. óra • Sablonok 479

249 : Lin~edList<Data> ListOfData ;


250 :
251 : myFunction(Lis t OfCats):
252 : myOtherFunction(ListOfData);
253:
254 : II VégignézzOk a listát, és kiírjuk az elemek értékét.
255: std : : cout « "\n" ;
256 : ListOfCats . ShowAll();
257 : std : :cout« "\n" ;
256 : !>lsLOfData . ShowAll () :
259 : std::cout « "\ n *~**~**** .. *. \n\n";
260 : return O; I I A listák kikerOlnek a hat6kOrb6l, és megsel1lllisülnek.
261 :
262 :
263 : void myFunction(LinkedList<Cat>& ListOfCats)
264 :
265 : Cat • pCa t;
266 : int val;
267 :
268: 1/ A felhasznál6 adjon meg néhány értéket,
269 : II amit II listába teszünk
270: for l;:)
271: {
272 : std : :cout « " \nHány éves a macskád? (O vége): •
273 : std: : cin » val;
274 : i f (Ivel)
275 : break:
276 : pCnt .. new Cat(val);
211 : ListOfCats . lnsert{pCat);
218 :
219:
280:
281:
282 : void myOtherFunction (LinkedT"ist<Data>& ListOfData)
283 : {
28-t : Data .. pData ;
285: int val;
286:
287: I I A felhasznál6 adjon meg néhány értéket,
288: /1 amit II listába teszünk
289: f or ( :; )
290 : {
291 : std : : cout « 'Adj meg egy értéket (O vége) : ' .
292 : std : : cin » val;
293: i f (!val)
294: break ;
295 : pData = new Data{val);
296: ListOfData . lnscrt(pData);
297 :
298 :
299 :
I
480 VI. ,ész' Külőnlegességek

Hány éves a macskád? (o vége) : 12


Hány éves a macskád? (o v ége) : 2
Hány éves ,
macskád? (o vége) : 14
Hány éves a macskád? (o vége) : 6
Hány éves a macskád? (o váge) : O

Adj meg egy értéket (o vége) : 3


Adj meg egy értékat (o vége) : 9
Adj meg egy értéket 10 vége) : 1
Adj meg egy értéket (o vége) : 5
Adj meg egy értéket (o vége) : O

E, a macska 2 évec
E, a macska 6 éves
Ez ,
macska 12 éves
E, a macska 14 é ves

Da\.a objekt..um LOrlése , melynek értéke : 9


Dat a ob jektum törlése , melynek é r té ke : 5
Data objektum tOrlésc , melynek értéke : 3
objektum tOrlése , melynek értéke : 1
Dl;ltu
14 éves macska tOrlése
12 ével:! macska tOrlése
6 éves macIJka tOrlése
2 éves macsk", tOrlésc

Ez a példa majdnem ugyanaz, mint az e16z6, azt leszámítva, hogyaLinkedList ob-


jeknunokat refcrendával adjuk át az azokal feldolgozó függvényeknek. Ez rendkívül
fontos tulajdo nság. Miut:ín a listákat péld,ínyosítotluk, teljesen definiált típusú objckru-
mokként kezelhet6k: függvény paramétereként és visszmérési 6nékeként.

A szabványos sablon könyvtár


A szabványos sablonkönyvtán (Standard Template Library - STL) a C+ + szabvány defi-
niálja. Minden fordító , amelyről azt állílják, hogy szabványos, tartalmazza az STL-l .
Az STL sablonnal megvalósílott tárol60sztályok, például tömbök, lisUík, sarok, vermek
stb, továbbá számos közös algoritmus, például rcndez(:s, keresés stb. gytijteménye.

Az STL célja az, hogy ezeknél a gyakran e l őforduló feladatoknál ne kelljen újra feltalál-
nunk a spanyolviaszt. Az STI.-t tesztelik és javítják, jó a teljesítménye, és nem utolsósor-
ban ingyenes! Ennél is fontosabb, hogy az STI újrahasznosítható. Mihelyt megtanulluk
és megértettük a használatát, minden programunkban alkalmazhatjuk, ahelyett, hogy
újra és újra magunk megirnánk.
23.6ra • Sablonol1481

Mindig emlékezzünk rá, hogy a termelékenység és a könnyű karbantarthatóság kulcsa


az újrahasznosítás!

Kérdések és válaszok
Kerdés: Mié11 használjunk sablonl, amikor makróval is mego/dhatunk egyjeladatot?

Válasz: A sablon!"... típuscllcn6r..:és vonatkozik, és integrált részét képezi a nyelvnek.

Kérdés: Mi ti lu1 lő1lbség a paraméterezltetó jüggwllysabloll és a szokvtillyosjligsvény


panl1néterei között?

Válasz: Szokványos függvé ny (nem függvénysablon) előre megadott típusú paraméte-


reken végez /Jniveleteket, A függvénysablon ellenben azl is lehet6vé leszi, hogya pa-
mmétereket paraméterezhessü k.

Kérdés: M/I...'Or hasz náljunk sablol/t, és mikor öröklődést?

Válasz: Használjunk s.1.blont akkor, ha a művel etek halmaza azonos, csak az oszclly más-
más típusm alkalma zz.1. azokaL Ha azon k:-tpjuk m:lgunkat, hogy kóclot m:'isolunk, és
csak egy-két tag típus.1t v:.\ltoztatjuk meg, ideje elgondolkodnunk a s.1.blon haszn:'i.lat1n .

Gyakorlatok
A sablonok megismerése után pr6báljuk megv:.\laszolni, illetve megoldani az alábbi
kérdéseket és feladatokat, hogy a tárgyban szerzett ismeretek megszilárdulj:mak.

Kvrz
1. Honnan tudja a fordító, hogy nem swkványOl:i osztályt, hanem sablont definiálunk?
2. A 22.1 példában szerepl6 láncoltlista-sablon honnan tudja, hogy az új e lemeket
milyen sorrendben kelJ beszúrnia?
3. Hogyan cleklarálunk egy osztálysablonból objektumot, és hogya n jelezzük, mi-
lyen osztályt kell ehhez használnia?
4. Honnan tudja a fordító, mikor kell megsemmisítenie a listában tárolt objekmmokat?
482 1VI. rósz' K11lönl'g,ssóg,k

Feladatok
1. HasonlíL'iuk össze a 21.1 (parmlist . cpp) és 19. 1 (linkedlist . cpp) Hstákat!
Észre fogjuk venni, hogy milyen sze mbetűnó a hasonlóság.
2. Ellenő rizzük a fordítónk dokumentáci6jában , hogy a szabványos sablonkönyvtár
mely osztályai vannak me bJ\la16sítva! Borland fordító esetében, használja a súg6t
(Help) a f6 menüben, majd válasszon témakört (Help Topics) és végül a bal ol-
dali ablakban hivatkozást ( Reference)!
3. Hasonlít.'>a össze II 22.1 példa láncolt listájának sablonját a fordító dokumentá-
ci6j{lban szereplő list STL-oszlállya\1 Keresse mcg a hasonlóságokat és a kü-
lönbségeket!

Válaszok a kvfzkérdésekre
l. A fordító a template <class T> prefIx miatt tudja , hogy sablont defil1i{llunk.
2. A ké rdéses obje ktumok Compare () metódlls:'\n:lk hívása :1 137. sorban ! :ílh~H6.
A függvény é rtelemmel megtöltése a programozók, naz a mi feladatunk (vc-
gyük szemügyre a 44 . és a 81. sorban kezd6d6 filggvényekeÜ. Ha egy kutyákat
rcprczentl'iló osztá lyt is szeretnénk :I listában tárolni, ahhoz is meg kelle ne ír-
nunk az összehasonlító függvényt.
3. Nézzük meg a 22.1 LiSla 247. és 248. sorM A recept a következ(}: oablonnév
<paraméterosztálynév> sablonobjektumnév.
4. Amikor a main függvény visszatér, a láncolt lista elhagyja a l1at6 kört':t, így
:IZ osztály destruktor.! lesz végrehajlv.l. Minthogy a táro lt elemeke t a lista dina-
mikusan hozta létre, a törlésük is a lista feladata. Ezt ot 23:1 Lista 110. soriiban
láthatjuk .
24. ÓRA

Kivételek, hibakezelés
és néhány tanács
Ebben az órában a következőkrőllesz szó:
• Mik azok 1:1 kivételek
• Hogyan használjuk a kivételeket, és használatuk milyen kérdéseket vel fe l
• Hogyan í~llnk hibátlan kódot
• Merre tovább

Programhibák. tévesztések. kódrnegromlás


Könyvünk példaprogramjai illuszlrációs céllal születtek. Szándékosan kerültük a hiba-
források taglalását , hogy ne vonja el figyelmünker az éppen tárgyalt témáktóL Az éles
programoknak azonban figyelniük kell a hibalehet6ségekrcj sőt, a valódi é l etből veU
programkódok legnagyobb részét a hibákra való felkészülés és a hibakezelés teszi ki.
484 1VI. rész • Különlegességek
Azt mondják, hogy ha a városok is úgy épülnének, mint a szoftverek, akkor az első ar-
r.:!. járó harkály romba dömené a civilizációl. Mindenesetre tény, hogy szinte minden
kereskedelmi program, köztük a legnagyobb gyártók termékei is tartalmaznak hibákat.
Komoly programhibákat.

Attól még, hogy ezt tudjuk, ez nincs így rendben. Bárkinek, aki komolyan programo-
zásra adja a fejét, elsődleges fontossággal afnt kell törekednie, ho&'Y robusztus, hib;1
nélküli programokat írjon. Állítom, hogya szoftveripar legnagyobb és kiemelked6
problémáját a hibás, inswbil k6dok jelentik. Az mindenesetre tény, hogya program
megírásának legkölcségesebb rés7.e a tesztelés, hibakeresés és a hibák kijavítása.

Számos olyan apró hiba van, ami gondot okozhat egy progr.lIn futásakor. Az első
a rossz logika: a program azt csinálja, amit a progr.HTloz6 szeretne; csakhogy (5 nem
gondolta végig dég alaposan II mcgval6sítotl algoritmust. A második II szintaktika:
rossz kifejezésm6dot, függvényt vagy struktúrát használtunk. Ez a két leggYllkoribb, és
a legtöbb programozó ezek megt:ll~lására törekszik. Sokkal mvaszabbak azok :1 hibák,
medyek csak akkor ütköznek ki, amikor a felhasználó valami váratlan dolgot múvel.
Ezek a piciny, logikai tapos6aknák csendesen és zavartabnul meglapulhatnak. Amikor
már úgy tűnik, minden rendben van, egyszer csak hirtelen - BUMM! - valaki rossz
helyre lép, és progr.uTlunk megfeneklik.

A kutatások és a gyakorlati tapasztalat egyaránt azt mutatjá k, hogya fejlesztés minél


kés6bbi stádiumában kerül el6 egy hiba, annál drágább a kijavítása. A lego1csóbbak
azok a problémák és hibák, amelyeket sikerül még a progmm megírásakor elkerülni.
Csak alig drágábbak azok, melyeket a fordít6program kidob. A C++ szabvány afra tö·
rekszik, hogy .1 fordít6progmmokb61 a lehet6 legtöbb hibajelcntést és fi gyelmeztetést
kipréselje (még fordítási időben).

A IcfordílOu programoknak azok a hibái sokkal olcsóbbak, amelyek rögtön az e l ső fut-


tatáskor kideriiinek (amelyek mindig összeomlást okoznak), mim amelyek csak nag)'
ritkán okoznak problémát.

A logikai és szintaktikai hibáknál nagyobb gondot jelentenek az esetleges hibák; ami-


kor például a programunk remekül múködik, ha számol ad meg a felhasznál 6, dc
összeomlik, ha belűt ír be, Más progmrnok akkor fagynak le, ha kevésnek bizonyul
a mem6ria, vagy ha kint felejtettilk a hajlékonyJemezl, esetleg ha a modem éppen
bontja a vonalaI.

A programozók az effajta törékenység ellen úgy küzdenek, hogy próbálják programju-


kat ~golyóállóvá ~ tenni. Golyóá1l6 az a progmm, amely minden futásidóben elképzel-
het6 esetre fel van készítve, kezdve a bizarr felhasználói bemeneuől egészen a memó-
riahiányig. Ez a fajta programozás hasonlít a "defenzív vezelésre~, amely abból áll,
hogy nem bízunk a másik sofőr (esetünkben felhasznál6 vagy rendszer) udvariasság:i-
_ __ _ ..é...'."""I....~,.h""ib..,.k"eze=lé...
_ _ _ _ _ _ _ _ _ _"'24"-"",6ra,,-0-"Kiv s ..,
és"n..,éh..,é...nyL'...an...' ... I,,48"''s--__--"""
csCJ

ban, hanem azt feltételezzük, hogy bármilyen meglepá esemény is megtönénhet.


Ha azonban elórelál6ak vagyunk és felkészülünk az effajta dolgokra, akkor elkerülhető
az összeütközés.

Fontos különbséget tenni, a progrdmhibák közön. Vannak ol}"dnok, amelyek abból


adódnak, hogy a programozó szintaktikai hibát vételt. Vannak logikai hibák, amelyek
onnan erednek, hogy a programozó félreértette a feladatot vagy a megoldás módját. Vé-
gül vannak olyan kivételek, melyek szokatlan (ám nem megjósolhatatlan) események
miatt lépnek föl, mint például az erőforrások (memória vagy merevlemezhc\y) hiánya.

Váratlan események kezelése


A programozók hatékony fordít6progr.lmoka t haszn:ílnak, és külö n böző biztosítékokka l
rakják tele a kódot, amely Icbct6vé teszi a programozási hibák felfedezését. Újra és újra
áuekintik a tervet, 6s kimcrít6 tesztelést folytaUlak, hogy megtaláljlik a logikai hibákat

A kivételek azonb:m más jelleguek. A kivételes körülményeket nem lehet teljesen kikü-
szöbölni, csak felk(!szülni lehet clJuk, Felhasználóink számítógépein id6nként belelik
amemória; ilyer,kor az il kérdi!s, hogy mit csináljon a program. Az alábbi lehet6ségek
közül lehet váJaszt:mi:

• Hagyjuk összeomlani a programot


• Jelezzük a helyzetet il relhasználónak, és elegánsan lépünk ki
• Jelezzük a helyzetet a relhasználónak, és lehetővé tesszük s7Jlmára, hogy megkí-
sérclje a helyreállításI éS a folytatást
• A progrnm maga javítja a problémáI, és tovább fut a felhasználó megzavarása
nélkül

Nem muszáj, S61 nem is kívánatos, hogy minden programunk automatikusan (!5 csend-
ben maga küszö\)öljön ki minden váratlan helyzetet, az azonba n nyilvánvaló, hogy
többet kell tennünk, mint hogy hagyjuk csak (Igy összeomlani a programot.

A CH kivételkezelés típusbiztos és szervesen beépülő módszert biztosít arra, hogy


megbirkózzunk a program fu tása közben fell épő ritka, de megjósolható eseményekkel.

Kivételek
A C++ nyelvben a kivétel egy olyan objektum, amely a kód egyik részér61 a másikra
adódik ál: a hibát kiváltó reszr61 a lubát kezelő részre. Amikor bekövetkezik a kivételes
esemény, akkor azt mondjuk, hogy a program .továbbdobon egy kivételtH; a kivétel
kezelését pedig .a kivétel elkapásának mondjuk. H
486 1VI. rész· Különlegességek

A kivétel típusa dönti el, hogy melyik kódrészlet fogja kezelni a problémát; a tovább-
dobott objektum tartalma (ha van), visszajelzést nyújthat a fdh<tszná I6nak. A kivétdke-
zelés mögöu húzódó gondolat meglehelősen egyszerű:

• Az efÓforrások kezelése (például a memóriafoglalás vagy az állományok zárolá-


sa) általában a program igen alacsony szintjén zajlik
• Annak eldöntése, hogy mi történjen akkor, ha meghiúsul egy alacsony szinnl
művelet, azaz, ha nem leheL a szükséges mem6riát lefoghl lni v,lgy egy állományt
z5rolni , általában magas;lbb szintG Imlvelcl, és elképzelhető , hogya ft!lhasznM6
segítségét is igénybe kell vennie a k6dnak.
• A kivételek gyors átjárást biztosítanak a kód erőforrás-foglaló részeit61 a hibake-
7..elő részbez. Elképzelhető , bogy vannak közbenső rétegek, melyek függvényei
lehetőséget teremtenek például a mem6riafoglalások felszámolására, de ezek
nélkül is lehet olyan k6clot írni, amelynek nincs más célja, mint végigné.mi a hi-
b:tfe1LéLeleket.

Hogyan valósítsuk meg akivételkezelést?


A try blokk segítségével körbe lehet venni a veszélyesnek ígérkez6 k6drészletet. Eb-
ben a blokkban v5rjuk ti kivétel továbbdobás5t.. Például:
try
(
Ves~edelmesFüggvény();
}

A catch blokk közvetlenül a try blokk után következik; ebben kezelhet6ck a kivéte-
lek. Például:
try
(
valamiVes~élyesFüggvény{) ;
}
catchfOutOfHemory)
(
II Akkor lép életbe, amikor a ross~ memóriahelyzeten kell segíteni
)
catch(FileNotFound)
(
II Akkor lép életbe. amikor nincs meg a keresett fájl
)

A kivételkezelés alapvető lépései a következ6k:

1. Keressük meg a program azon részeit, amelyek olyan műve l etet kezdenek,
melyben kivétel keletkezhet, és vegyük körbe try blokkal.
24. óra • Kivételek. hiba kezelés és néhány tanács 1487

2. Hozzunk létre alkalmas catch blokko kat, hogy le.gyen hol elkapni a továbbdo-
botL kivé teleket; kezeljü k a mem6ria p roblémáka l és je1ezzünk vissza a felhasz-
nál6nak, ha ez szüb;éges .

Apropó Továbbdobás ós elkapás


Amikor kivétel keletkezik, a vezérlés azonnal az aktuális try blokkot kö vető catch
blokkra kerül.

A 24 .1 Lista szemlélteti a try és a ca t ch blokkok haszná latát.

24.1 Usta - Kivétel továbbdobása (exceptions.cpp)


o: /1 24 . 1 Lista Kivételek továbbdobása
l : #include <iostrearll>
2,
3 : const int DefaultSize " 10 ;
4,
5 : /1 definiáljuk a "kivételes" osztályt
6 : class xDoundary
7: (
8 : public :
9 : xBoundary () {}
10: -xBoundary () ()
ll : privatc :
12 : ) ;
13 :
14 : class Array
15 :
16: public :
17 : II konstruktorok
18 : Array(int itsSize = DefaultSize) ;
19 : Array(const Arr ay &rhs) ;
20 : -Array () ( delete [ l p'J'ype ; )
21 :
22 : II operátorok
23 : Array& operator=(const Array&);
24 : int& operator[) (int offS€:t) ;
25 : const int& operator[ ] (int offSe t ) const ;
26 :
27: 1/ hozzáféro ft1ggvények
28 : int Get it sSize() const { return it sSize ; J
29 :
30 : II barácf üggvé nye k
31 : fri end st.d : : os t. ream& operat.or« (std: : ostream& , c onst Array&) ;
32 :
33 : private :
34 : int. *pType ;
488 1VI. rész • Kiilönlegességek
35 : int itsSize ;
36 : } ;
37 :
38 :
39 : Array " Array(int size} :
40 : it sSize (size )
41 : (
42 : pType '" n e w i n t [ size) ;
43 : for (int i '" O; i<sizc ; i++)
44 : pType[i] '" O;
45 : )
46:
47 :
48 : Array& Array :: operator= (const Array &rhs)
49 : {
50 : i f (this ;: &rhs)
51 : return · this ;
52 : delete [ 1 p'J'ype ;
53 : itsSizQ " rhs.GctitsSize() ;
54 : pTypc '" new int [ i tsSize) ;
55 : for (int i • O; i<itsSize; i++)
56 : pType[iJ .. rhs[i] ;
57 : rcturn *this ;
58 :
59 :
60 : Array : :Array(const Array &rhs)
61 : (
62 : itsSize " rhs . GetitsSize () ;
63 : pType = new jnt[itsSize] ;
64 : for (int i = O; i<itsSize ; i++)
65 : pType[i] = rhS [i) ;
66 : }
67 :
68 :
69 , int& Array : : operator[] (int offSet)
70 : (
71 : int size = GetitsSize() ;
72 : i f (offSet >: O && offSet < size)
73 : r eturn pType[offSetl ;
7 4: throw x Boundary() ;
75 : return pType[offSetl ; 1/ Az MSC lecsillapítására .
... A Borland figyelme ztetés t ad.
76 :
77 :
78 :
79 : const int& Array : : operator[] (int o ff Set) con s t
80 :
81 : int mysi ze = GetitsSize () ;
82 : if (offSet >= O && offSet < mysize)
8] : return pTYPe[offSetl;
84 : throw xBoundary();
85 : return pType[offSet ] ; /1 Az MSC lecsillapítására .
... A Borland figyelmeztetést ad.
86 :
87 :
24. óra • Kivételek, hibakezelés és néhány tanács 1489

8B : std : : ostr e am.& operator« (std , : Qstream& output ,


89 , const Array& t heArray)
90 : {
91 : for ( int i = O; i<theArray.GetitsS ize () ; i ++ )
92 : out put« ' ( ' « i « " l " « theA rray[il «std :: endl;
9] , re turn output ;
94 :
95 :
96 : int main()
97 :
98 , Arr<ly intArray(20) ;
99 : try
100 , {
101 , for (int j = O; j< 100; j++l
102 , {
10] : intArray[jl = j ;
104 , std :: cout « 'intAr ray { ' « j
105 , « " l okay . «std , : endl ;
106 :
107 :
108 : cotch (xBou ndary)
109 : (
110 , std :: cout« "A bemen6 adatot nem sikerült feldolgozn! . \n";
111 , l
112: std :: cout« "Kész.\n';
11] : return O;
114 :

not
intAr ray[O j okay ...
intArray[l l okay .
intArray [2 J okay .. .
intArray[31 okay .. .
intIIrray [4) okay .. .
intArray[51 okay .. .
intArray(6) okay .. .
lntArray [7 l okay .. .
intArray(8] o kay . . .
intArray [910kay .. .
intAr ray [ 10] okay .. .
intArr8y(ll] okay .
intArray[12] okay ...
intArray[131 okay .
intArray[14] okay .
intArray[151 okay .
intArray[16] okay .
intArray[171 o kay .. .
intArray[18] o kay . . .
inLArray[191 okay . . .
A bemen6 adatot nem sikerOll feldolgo zni .
Kész .
I
490 ~. rész ' Killönl.g.sség.k

A 24.1 Listában egy lecsupaszított Array osztályt hozunk létre egy egyszeru kivt:tdkc-
lelés bemutatására. A 6-12. sorban egy igen egyszenl kivétel osztályt deklarálunk
(xBoundary). Figyeljük meg, hogy semmi, de senuni nem teszi ezt ;IZ osztályt speciáli-
san kivétel osztállyá. Voltaképpen bármilyen osztály, bánnilyen mel6dusokkaJ és (ag-
változókkal vígan viselheti a kivétel szerepét. Pusztán atl61lesz ez kivétel oS7.tály, hogy
eZI használjuk a továbbdobásnál (thr ow) a 74. sorban, és ez kerül e lk:lpásrJ. (catch)
II 108. sorban.

Az eltolási (offset) operátor akkor dobja tovább az xBound-,ry-t, amikor ,IZ osztá ly
ügyfélprogramja a tömb határain kívüli elemet próbál elérn i (74. &s 84. sor). Ez messze
túlmutat <1Z0n, ahogy a normál tömbök kezelik az efféle k éré!';~ k ~t; 6k visszandnak bár-
mi szemelet, ami ti hivatkozott mem6riacímen található. Ez alkalmasint garantáltan
összeomlaszlj:1 :1 programol.

A 99-107. sorban ta lálh ató a try utasításblokk, amelyben sz{tz egész számot próbálunk
meg bevinni II 98. sorban dekl:.n1h LÖmblx:.

A 108. sorban kezd6dik a catch blokk, melyben a kivételek el kapása l~het6vé v:í lik.

Apropó try blokkol<


A try blokkal különbözó ~veszélyes- utasitásokat fogunk közre, kezdve a try
kulcssz6val, és körbevéve kapcsos zárójelekkel. például Igy:
tn'
(
Filggvény ( ) ;
l
catch blokkol<
A catch blokkban utasítások helyezkednek el, kezdve a catch kulcssz6val és a be-
zárójelezett kivételtipussaI. valamint körbevéve kapcsos zárójelekkel, például Igy:
try
(
r' ü gg v ény () ;
l
catch (Ou t OfMemory )
(
II védelmi tevéke nység
24. óra • Kivételek, hibakezelés és néhány tanács 1491
--------------------~~~==~==~~~==~-~------,--

A try és catch blokkok használata


A kivételek használatának legnehezebb része valószínűleg annak kitaláJása, hogy hová
is tegyük a try blokkot. Nem mindig nyilvánvaló, hogy mi válthat ki kivételeket. A kö-
vetkező kérdés az, hogy hol kapjuk el a kivételeket. Elképzelhető , hogy olt lenne jó to-
vábbdobni a mem6riagondokat okozó kivételeket, ahol amemória lefoglalása történik,
míg az elkapásuk a program egy későbbi részén lenne ideális, ahol a felhasználói felü-
lettel foglalkozunk.

Erdemes ott elintézni a try blokkokat, ahol a mem6ria és más erőforrások lefoglalá-
sa, használata zajl ik Más dolog a lömbhatárok kezelése, a rossz bemenő adatok elbí-
ráL.lsa stb.

A kivételek elkapása
A következő módon za jlik a kivételek elkapása: a program a kivétel dobása után meg-
vizsgálja a hívási vermet. Ez nem 111.1s, mint az adott pillanatig meghívoll rüggvények
időre ndi listája.

A hívási verem tárolja a végrehajtás útvonal{tt is. Ha a main () meghívja


az Allat : : KedvencÉteltSzerez ( ) függvényt, majd a KedvencÉteltSzerez () meg-
hívja az Allat ; : SajátBeállitások() -at, amely a következő lépésben
az fstream: : operator » ( ) -t hívja meg, akkor mindez eltárolódik a veremben. Egy
rekurzív függvényt több példányban is megtalálunk a veremben.

A kivételt a hívási verem az összes aktuális blokkban érvényesíti. Ahogy a veremben


levő függvények soriit felgombolyít juk, a veremben lévő helyi objektumok destnlktorai
érvénybe lépnek, és az objektumok megsemmisülnek.

Mi nden try blokk után van egy vagy több catc h utasítás. Ha a kivétel ilJcs:.:kedik va-
lamelyik ca tch utasításra, akkor az ahhoz tartozó utasítÍlsblokk végrehajtásÍll tekinti
a program a kivétel kezelésének. Ha egyikre sem illeszkedik, akkor tov{lbb folytatódik
a verem felgöngyölítése.

Ha a görgetés elér a főprogram (main) legelejére, és a kivétel még mindig nincs elkap-
va, akkor a t erminat e () (befejezés) függvény hívódik meg, ez pedig az abort ()
meghívásával vet véget a program futásának.

Fontos tudni, hogy a kivételek fe\gombo lyítása egyirányú folyamat. Előrehaladásakor


a verem objektumai febzámolód nak, és ebbő l nincs visszaút; miután sikeresen kezel-
tük a kivételt, a program a kivételt kezelő catch utasításhoz tartozó try utasításblokk
utÍln folytatja a végrehajtást.
492 1VI. rész· Különlegességek.

így a 24.1 listában a végrehajtás a 11 1. sorban fo lytatódik, a mely az els6 sor


az xBoundary-t kezeld catch utasításhoz tartozó try utasításblokk után. Emlé ke zzünk
csak vissza: a kivéte l ke letke zése után a vezé rlés ne m a kivétel továbbdo bása utáni
po ntról folytatódik , hanem a catch blokk után.

Több catch megadása


Le hetséges tö bb catch feltételt is megadni; ez eselben a megfeleld prog ramrészek úgy
sorako znak egymás utá n, mint a switch utasítáS elágazásai. Az alapértelme zeu
(default) e lágazásnak, vagyis a . kapj el minde nt" eselne k a catch( . . . ) fe le l meg.

Flgyeleml Vigyázatl
Érdemes nagyon meggondolni, hogy egymás után sorakoztassunk-e fel két catch
utas ítást, melyben az egyik egy myExeption bázisosztályhoz, a másik pedig en-
nek specializált, származtatott osztályához kapcsolódik (mint például
mySpecificExeption). A végrehajtás mindkét utasításblokkba be fog lépni. El-
ké pze lh ető, hogy bizonyos esetekben pont ez a szándékunk, de mindez nem várt
következményekkel is járhat. Jobb e lővigyázatos na k lenni.

Hivatkozás elkapása - többrétílség akivételkezelésben


Kihasználhatjuk a kivé tele k a zo n tu lajdonságát, hogy 6 k tulajdo nképpe n többré t{íen
ke ze lhe l6 osztályo k. A kivé te l cím sze rinti átadásávaJ haszmílbalu nk egy örö kl6dési hi-
erarchiát is a futá si id6ben ke letkezett kivételtípus Icgmegfe le l6 bb kezeléshe. A 24 .2
listában töb bré t(l"en kezeljük a kivéte le ket. A kimene lta núsága szerint háro mszor fllt~
[a ttuk II progl"J.mo t: egyszer egy S elemű , másodszor egy SOOOO e l e mű, harmadszor pe-
d ig egy 12 e le mG tö mbbel do lgozrunk.

24.2 Ulla - Többrótll kivót8lkozelés (pulya.coptions.cpp)


0 , II 24.2 Lista II A kivéte l ek tObbrétG elkapása
1 , #include <iostream>
2,
3 , const int DefaultSize = 1 0 ;
4,
5 : II A kivételosztályok de f ini álása
6 : class xBoundary (l ;
7,

,,
8, class xSize

10 , public :
ll : xSize(int size) ,itsS ize(sizel (l
12 , - xSize() (l
13 : virtual int GetSize () ( return itsSize ;
14 : v irtua l void PrintError ( )
24. 6ra • Kivételek. hibakezelés és néhány tanács ( 493
- - - - - - - - - - - - ="""-'-""="-""'=""'-'''-'''''''''''-=''''-'''''-----: ....
15 : ( std : : cout « 'Size error . Received :
16 : « its Size « std : , endl ; )
17 : protected :
18 : int it sSize;
19 : l;
20 :
21 : class xTooBig : pub l ic xSize
22 :
23 : public :
24 : xTooBig(int size) , xSJ.ze(size) (j
25 : virtual voi d PrintError()
26 : {
27 : std : : cout « "Too big! Received : ";
28 : std : : cout « xSize : : itsSize « std , : endl;
29 :
30 : );
31 :
32 : class x TooSmal l public xSi ze
33 : (
34: public :
35 : x'I'ooSmall(int size) : xSize(size} ()
36 : virtual void PrintError()
37 : (
38 : std : : cout « "Too small! Received : ' ;
39 : std :: cout «xSize::itsSlze« std :: endl ;
40 :
41 , );
42 :
43 : class xZero public xTooSma l l
44 : (
45, public :
46 : xZc r o( int size) , x TooSmal1{size) {}
47: virtual void PrintError()
48 : {
49 : std : : cout « ·Zero!! . Received : ";
50 : std : : cout « xSize : :itsSizc « std : :cndl;
51 :
52 : );
53:
54 : class xNegative public xSize
55 :
56 : public :
57 : xNegativ e(i n t size ) :xSizc(si z e) (J
58 : v i r t ual v o i d PrintError()
59 : {
60: s t d : : cout « "Negativel Rece ived : " ;
61 : std :: cout« xSize :: itsSi ze « std :: endl;
62 :
63 : };
64 :
65 : class Array
66 :
494 1 ~ ...sz • Kü~nleg,sségek

67, public:
68 : II Konstruktorok
69 : Array{int itsSize " DefaultSize);
70 : Array{const. Array &rhs) ;
71 : -Array () ( delete [l pType;)
72 :
73 : 1/ Operátorok
74 : Array& operator= (const Array&) ;
75: int& operator[) (int offSe t ) ;
76 : const. int& operator[) (int offSet) const ;
77 :
78 : /1 Hozzáfér6 fÜ9gvények
79 : int GetitsSize() const { rcturn itsSize; }
80 :
81 : /1 Barátfüggvényck
82 : friend std : : osLrea.m& operator« (std : : ostream&, const Array&);
83 :
84 :
85 : private :
86: int *pType:
87 : int itsSize;
88: };
89:
90: Array::Array(int. size} :
91: itsSize{size)
92: {
93 : i f (size -- O)
94 : throw xZero(size) :
95 :
96 : i f (size < O)
97 : throw xNegative(size) ;
98 :
99 : i f (size < 10)
100: throw xTooSmal1(size);
101 :
102: i f (si%e :> 30000)
103 : throw xTooBig(size);
104 :
105 : pTypa : new int [size] ;
106 : for (int i • O; i<size: i H )
107 : pType[i ] = O,
108:
10 9 :
110 : int& Array: : operator[ ) ( int offset)
lll :
112 : int size = GetitsSi ze () ;
113 : if (offset :>= O && offset < size)
114 : return pType[of fset);
115 : throw xBoundary();
116 : return pType[offsetJ: II Az MSC boldogítására van így ;
.. a Borland figyelmezte t
117 :
118 :
__________________________~24~.~6~~·~KW~~~.I.~k~.h~ib~.~k.~m~lé~s~és~n~é~hj~~Lm~ná~cs~I~4~95~____~.,

119 : const int& Array : : operator!) (int offset) const


120 :
121 : int si ze = GetitsSize ():
122 : if (of fs et >= O && offset < si ze)
123 : return pType{offsetJ;
124 : throw xBoundary{);
125 : return pType[offset): 1/ Az MSC boldogitúsára van igy:
... a Borland figyelmeztet
126 :
127 :
128 : int main{)
129 :
130 : try
131 : (
132 : lnt choice ;
133 : std : : cout « 'Enter the array size : "i
134 : std : : cin » choi.ce;
135 : Array intArray (choice) l
136 : for (int j = O; j< 100; jH)
137 : (
138 : int.ll.rray[jl '" j:
139 : std : : cout « • iotArra}' [ . « j <c 'J okay ... •
140 : « std : : endl;
Hl :
142 :
143 , catch (xBoundary)
144 : (
145 : std: : cout « 'Unable to process your input!\n" ;
146 :
147 : catch (xSize& t heEx cepLion)
148 : (
149 : thcException.PrintError();
150 :
151 : catch ( ... )
152 : {
153 : std :: cout « 'Something went wrong,'
154 : « 'but I've no idea what!' «std : : end1 ;
155 :
156 : std : : cout « 'Done . \n ';
157 : return O;

-
158 :

Enter the array size : S


Too sma11! Received : S
Done .
Enter the array size : 50000
Too big ! Received : 50000
Done.
Enter the array size : 12
intArray[O] okay .. .
intArray [l] okay .. .
496 1VI. rész • Különlegességek

intArray[2] okay .
intArray[31 okay .. .
intArray[4] okay .. .
intArray[51 okay .. .
intArray[6] okay .. .
intArray [710kay .. .
intArray[BI okay .. .
intArray [910kay .. .
int Array [10] okay .. .
intArray[ll] okay .. .
Unoble to process your input!
Done .

A 24.2 Listában az xSize osztályb<l11 megadunk egy virtw'ilis függvényt: a PrintError () -


t, amely kiír egy hibaüzenetet és az osztály pillanatnyi méretét. Ezt minden kf:s{íbbi osz-
t.'ily felülí~<l.

A 147. sorban il kivételt hivalkozásként adjuk meg. Amikor a PrintBrror ()-t egy ob-
jekrumra hivatkozva meghívjuk, akkor a többrét"Úség miau a megfelel6 PrintError ()
verzió kerül funatáSI"<l. Els6 <lIkalommal egy 5 méretU tömböt kélilnk, amely
a TooSmall (Tti/Kicsl) kivétel továbbdobását eredményezi. Ezt az xSize kivételt el is
kapjuk 3 147. sorban. A második alkalommal már egy 50000 mérelÚ tömböl kérünk j
ekkor a TooBig ( TlílNagy) kivételt dobja tovább a program. Ezt is a 147. sor kapja el,
de a többrélÚség miatt a helyes hibaüzenet jelenik meg. V~gü l , Ilmikor egy 12 mérelÚ
tömböt kérünk, akkor a program már benépesíti a tömböt, egészen addig, amíg
az xBoundary (:xHa/úlVol1al) kivétel továbbdobásra nem kenil - eZl a 143. sorban
kapja el a program.

Hogyan írjunk professzionális minőségű programokat?


A sablonokkaJ és kivétcJekkel felvértezve immár jól mega lapozott, profi C+ + eszköztár-
ral rendelkezünk. Mielőtt a legközelebbi sarokba hajítanánk a könyvet, tekintsünk még
át néhány dolgot a professzioná lis m in őségű progmmok írása érdekében. Mihelyt
a kedves olvasó már nemcsak kedvtel ésbő l progmmozik, hanem egy fejieszt6 csapat
tagja lesz, olyan módon kell megírni a kódot, hogy az ne épp csak mt1ködjön, hanem
mások számám is érLheL6nek kell lennie. A megrendelő kéréscinek megfelelően a kó-
dot leglöbbszőr a sze rzőnek kell karbantartania és módosítan ia, majd a későbbiekben
esetleg másnak is, ha a szerző elhagyja a projektet
I
24. óra • Kivétele~ hib.kezelés és néhény tanác. 497

Igazából nem túl lényeges, milyen kódolási stílust választunk, csak az fontos, hogy ez
legyen következetes. A következetes stílus segít kilalálni, mi voll a progf'dffiOz6 célja
az adott résszel , és megszabadít az olyan id6igényes tevékenységektől , hogy például
azt kelljen keresgélni, hogy mennyire volt nagybetűs a múltkor valamelyik függvény.

Az in következő önkényes, de jól múköd6 irányvonalak olyan a projekteken alapul-


nak, melyekben magam is dolgoztam. Minde n programozó kialakíthatja persze a saját-
ját stílusát - ezeket tekinLSünk elindulásnak.

Ahogy Emerson mondja, ~a rend a kis elmék támasza ft, a kód következetes stílusa azé n
nem egy rossz dolog. Nagyban megkönnyíli a progrdffiozó &s munkatársai életét, ha
egy csoport közmegegyezéseit betartják. Ez nem jelenti azt, hogy a stílust teljesen rög-
zíteni kell , his".en az új ötle te k és a továbbfejlesztések menet közben születnek, de
a következetesség minclcnki számára megkönnyíti a közös munkát.

Fontos megé rteni, hogy többféle kü lönböz6 stílus van, és hogy kemény elle nállást vált-
hat ki az alábbi iránymutatás né hány pontja. Ne feledjük , hogy ezek alka lmazása csak
egy l e hetőség.

Zárójelek
A zárójelek igazítása az egyik legellentmondásosabb téma, ami szembeállítja a C és
C++ progm moz6kat. Én az alábbiakat javaslom:

• Az összetartoz6 zárójelek legyene k fOgg6legesen egymás alatt. : <


/"2·4' .,.
" , .y, '
y
• A definíciók és deklarációk legküls6 zárójelpárja legyen a bal ma rg6nál. A bels6
utasításoknál használjunk beljebb kezdést. A belső záfÓjelpárok igazodjanak
"
, .. . .,..
.
I. .'
-.
a megfelel6 f6 utasításhoz.
• Az igazán hosszú utasításblo kkok eselén tegyünk egy megjegyzést a zá r6 zár6-
jeihez, hogy könn)'ll legye n azonosítani a blokk célját. Ha ránézünk egy zjr6 zá-
rójeire, és nem látjuk, melyikhez tartozik - ez már egy "igazán hosszú utasítás-
blokk ~. Például:
if (feltétel~- true)
(
II renge t eg sor , kóztük akár más utasitásblokkokk
II renge t eg sor , kö ztük akár más utasitásbl okkokk
/1 renge t eg sor, kó ztük a kár más utasitásblokkokk
) II i f (feltétel"", tr ue)
Ne írjunk a zár6jel sorába más kódrészletet. Például:
if (feltétel==true)

j ,. k ;
ValamiFüggvény() :

m++;
498 1 ~, rósz • Különl'g...ég,k

Hosszú sorok
Úgy írjuk meg a programk6d sorait, hogy ne 16gjanak lül a sorok a képcrny6 egyszerre
látható t3rtományán. A kilógó sorrészekel könnyt'i szem elől téveszteni, és bosszantó, ha
víz.';zinlesen kell görgeulünk a szövegel. Sortörés esetén kezdjük beljebb az új SOlt. Tö-
rekedjünk ésszeru töréspontokr.a, és lehet61eg hagyjunk az eredeti sor végén valamilyen
műveleti jelet (ahelyett, hOh'Y az új sor elejét kezdenénk vele), hogy világosan látsszon,
hogy annak a sornak még folytat6Unia kell, és még nincs vége az adott ponton.

A CH függvények rövidebbek C nyelV11 társaiknál, dc II régi jótanács itt is megállja


a helyét: törekedjünk egyetlen képerny6n elfér6 fi.lggvények írására.

A tabulátorok három vagy négy szóköznyi mérel(iek legyenek. Győződjünk meg róla,
hogy szerkesztóprogramunk valóban így alakítja-e át II tabulátorokat.

A switch utasftások
Az elágazásokat azonos Ví7~<;zinles pozíciókban érdemes megtartani:
switch(variablcl
(
case ValueOne:
l\ctionOne{) ;
break;
case ValueTwo:
ActionTWo() ;
break;
default :
assert("wd Action");
break ;

A program szövege
Álljon itt néhány jótanács, hogy olvashatóbb legyen a kód, Könnyebb a kód karbantar-
tása, ha az alábbiakat megtartjlIk:

• Használjunk bátmn térköz karaktereket az olvashatóság elósegítéshe.


• Az objektumok és tömbök egy dologra vonatkoznak; ne tegyiink sz6közöker
az objektumokra utaló operátorok közé ( . ,->, [l).
• Az egyoperandusú operátorok közvetlenül az operandushoz kapcsolódnak, így
nem kell közéjük szóközt tenni. Tegyük a szóközt az opedtorok másik oldaláf3.
Az egyoperandusú operátorok a ! , _, H , - -, -, * (mutatók), & üípusátalakításl
és a sizeof .
24. óra • Kivételek, hibakezelés és néhány tanács 1499

• A kélOpemndusú operátorok mindkét oldalára tegyünk szóközt: +, =, .. , I, %, »,


«,<,>,==, ["'.&, 1,&&, 11,? : ,=,+=stb.
• Ne akarjuk a szóköz hiányával kifejezni a műveleti sorrendet (4+ 3 *2).
• A vesszők és pontosvessz6k után (és ne elé) tegyük a sz6közl.
• A zár6jelekhez (belül) ne írjunk sz6közt.
• A kulcsszavakat, mim például az if, válasszuk el sz6közzel: i f (a == bl.
• A megjegyzést határoljuk el egy sz6közzel a II jeltől.
• írjuk a mutat6 és hivatkozás opcrátorokat közvetlenül a típusnév után, ne pedig
a változ6névhez. igy legyen:
char" foo;
int& the Int ;
és ne így:
char .. foo;
int &thclnt;

Azonosrt6nevek
Néhány ötlet az azonosít6nevek célszení használatár61:

• Válasszunk hosszú azonosít6neveket, hogy kell ően kifejez6ek legyenek. .


• Kerüljük a rejtélY($ rövidítéseket.
• Szánjunk kellő időt és energiílt a megfe l e l ő szövegezésre.
• Rövid változ6neveket (i, p, x stb.) csak Olt használjunk, ahol a rövidst:g o lvas-
hat6bbá teszi a kódot, és ahol olyan nyilvánvaló a szerepük, hogy felesleges
lenne őket megmagyarázni.
• NL azonosít6név hossza legyen arányos a hat6körével
• NL azonosít6nevek különbözzenek kellő mértékben egymástól leírva és kiejtve
is, hogy ne lehessen 6 ket könnyen összekeverni.
• A függvények (és mel6dusok) nevei legyenek igék vagy igéből képzett főnevek:
Search (), Keres (), Reset () , Visszaállít () , FindParagraph ( ),
BekezdésKeresés () , ShowCursor ( ), KurzortMutat () . A változ6nevek legye-
nek elvont f6nevek, lehet6leg egy másik f6névvel e!:,'Yütl, példáu! count,
dbszám, state,állapot,windSpeed, szélSebesség,windowHeight,
abl akMagasság.

A nevek belÚZése és a nagybetúk kérdése


A betűzés és a nagybetúk kérdései majd rögzü!nek, amikor a kedves olvasó rátalál saját
stílusára. Néhány ötlet következik ebben a témában:

• Érdemes az azonosít6k neveit következetes módon megválasztani; használjunk


felváltva kis- és nagybetűket, ahogy az alkalmas. A függvénynevek , met6dusok,
I
500 VI. rész · Különlegességek

osztályok, a t ypedef és struct nevek kezd6djenek na~,'ybetúkkel (ez[ gyakran


Pascal Stílusnak hívják, mint például MyFunction). Az adattagok vagy helyi vál-
tozók nevei kezdódhetnek kisbetűkkel (ezLgyakran Camel Stílusnak hívják,
mint például myVariable)
• A felsorolásos típus ko n:"itansait néhány olyan kisbetűvel érdemes kezdeni, ami
az enum név rövidítése (például ts a TextStyle helyetú:
enum TextStyle
(
tsPlain.
tsBold,
tsItalic,
tsUnderscore,
) ,
Megjegyzések
A megjegyz6sek nagyban megkönnyíthetik a kód megértését El6fordulhat, hogy
a programozó hetekig-hónapokig nem dolgozik egy progl"d.mmal, esetleg nagyobb fon-
tosságú projektekkel van elfoglalva. Ez idei alatt könnyen feledésbe merillhet egy-egy
kódrészlet szerepe. Ha másnak kell majd foglalkoznia progl"amunkkal , akkor még na-
gyobb nehézségel jelenthet egy-egy részlet megénése. A jól átgondo lt, következetes
stílusú megjegyzések írása megéri a befektetett energiát. Néhány ötlet a megjegyzések-
re vonatkozóan:

• Aho l csak lehet, haszná ljunk inkább II meg jegyzéseket, semmint / * * I


stílusú:lkat.
• A magas szintű megjegyzések nagyságrendekkel fontosabbak , mint a feldolgo-
zás részletei. Programunk tényleges értéké! növeljük a megjegyzésekkel, ne csak
a méretét. Ennek például semmi értelme:
n ++ ; II n értékét eggye l megnövelj\1k

Ez nem éri meg a rászánt időt. Inkább a függvényck és kódrészletek értelmét kell ki-
emelni. Fogalmazzuk meg emberi nyelven, mit csinál egy függvény. Jelezzük a mellék-
hatásokat, a paraméterek és a visszatérési érték típusát. Jegyezzük fel feltételezéseink et
(vagy amil nem várunk), példi'iul: . n feltehet6leg nem negatív", vagy .- l-et ad vissza,
ha x élVénytelen". Összetett logi kai szerkezetben jelölhetjük az adott kódrészletben ér-
vényes feltételezésünket.

• Használjunk teljes mo ndatokat, megfel e l ő közponrozással. Meghálálja magát


az erre szánt idő. Ne legyünk rejtélyesek, ne használjunk rövidítéseket. Ami
a kód írása közben napnál világosabbnak l11n ik, az néhány hónapon belül arcpi-
rítóan ködösnek és érthetetlen nek fog látszani.
24. óra • Kivételek, hibakezelés és néhány tanács 1501

• A program, a függvények és a fej lécállományok elejére írjunk összefoglal6


megjegyzéseket, amelyból kiderül az adott modul szerepe, bemeneti és kime-
neti jellemz6i, paraméterei, az eredeti szerz6je és mindenféle változás (szerz6-
vel és dálummal).
• Nyugodt szívvel használjunk elegend6 üres sort, ha ez segíti az olvasóI annak
megértésében , hogy mi is történik. Különítsük el a logikailag összetartozó k6d-
részleteket.

Hozzáférés
Érdemes következetesen kezelni .t progmm e~>'Ycs részeihez tartoz6 hov..áfér&si szinteket.

• Mindig írjuk kl a public, protected és private kulcsszavakat; ne támaszkod-


junk az alapértelmezések használatára.
• El6ször soroljuk fel a nyilvános adatokat, majd a védetteket, végül a priv{tl válto-
zókat. Minden csoportban a metódusok után sorakozzanak az adattagok.
• A megfelel6 k6dr6szben először álljanak a konstnlktorok, majd a destmktorok.
Az azonos nevt1 túlterhelt függvények egymás után következzenek. Egy CSOIJOlt-
ban soroljuk föl a hozzáfér6 függvé nyeket, ha csak lehet.
• Fontoljuk meg a mel6dusok és az adauagok csoportonként tőrt(:n 6 névsorba
rendezését. A beemelend6 (íf/ duda) állomá nyneveket azonb,m mindenképpen
névsorba n soroljuk fel.
• Bár a túlterhelés esetén a virtua l kulcsszó használata ne m kötelez6, mégis ér-
demes h:lsználni. Ez segít emlékezni annak virtuális szerepére, és a deklar.'ici6 is
következetes rnal"'.ld.

Osztálydefinfci6k
Törekedjünk arra, hogya metódusok definídóinak sorrendje egyezzen meg a deklará-
ciók sorrendjével; így könnyebb ők et megtalálni.

A függvénye k clefinícióinál a visszatérési típust és az egyéb módosítószókat helyezzük


cl egy külön ,~orban, hogya függvények és osztályok nevei a bal margón kezd6dhesse-
nek. Így sokkal könnyebb megta lálni egy-egy keresett függvényt.

Beemelendó állományok
Amennyire csak lehel.Séges, kerüljük a fej lécállományokba való állomány-beemelésl.
A legjobb minimális választás az, ha csak annak az osztálynak a fej lécállomá nyát emel-
jük be, amelyb61 az aktuális osztály származik. Kötelező még beemelni azon objeklu-
mokét, amelyek a deklar.ílt osztály tagjai. Az olyan osztályok esetében, melyekre csak
mutató vagy hivatkozás irányul, elegendő a megfelelő típusú hivatkozás.
502 1VI. rész • Különlegességek
Ne felejtkezzünk el semelyik szükséges fejlécállományról azzal a feltételezéssel, hogy
egy másik . cpp állománynak majd úgyis szüksége lesz rá, abba majd bizonyára
beemel6dik.

Figyelem! Vigyázzunk a fejléc.kre


Minden beemelendö (include) állomány elejére írjunk beemelési tanácsokat.

assertO
Haszná ljuk báln:tn az assert () utasítást. Segít a hibák megkeresésében is, de amiatt is
hasznos, mert a küJs6 szemlé16 számára is világosabban látszanak a programozó felte-
vé.~ei, valamint az, hogy mit tarl a szerző érvényesnek 6s mil érvénytelennek.

const
llaszná!juk a const kulcssz6t, ahol csak lehet. Paraméterekre, váll0z6kra és met6d~I­
sokl'~1.
Egy fOggv6nyb61 gyaknln szükség van konstans és r,em konstans változalra; nc
spóroljlLk mcg egyiket sem, ha ténylegesen kell mindkett6. I.együnk m.gyon körülte-
kintl5ek , amikor konstansból n(;!m konstanssá történő típusfltabkítást végzünk (vagy
fordítva). Vannak helyz.etek, amikor csak ez jelent megoldást, de &'Yőz6djil nk mcg egé-
szen biztosan arr61, hogy van értelme a dolognak , és lássuk cl megjegyzl!:sscl is.

További források
Keml!:ny munka áll az olvasó mögött; és ezen információk birtokában már alkalmas
C++ programoz6nak tekintheti magM - ezzel együLL még nem tek inthető befejezennek
a tanulási folyamat. Még rengeteg mindent el lehel sajátítani, és még sok könyvet végig
kell rágni, amíg a kezdő C++ progmmoz6b61 professzionális lesz.

Hol kapunk segftséget és tanácsokat


C++ progmmozóként a legels6 teendő bekapcsolódni egy vagy több C++ konferencia
online szolgáltatásába. Ezekben a csoportokban közvetlenül el lehet érni C++ progf".J-
moz6k százait~ezreit , akik válaszoJbatnak kérdéseinkre, tanácsOl adhamak, és esetleges
ötleteinknek mélyebb alapot biztosítanak. Vannak megfelelő levelezőlisták és
internetes megbcs z6lő f6nullok is, valamint értékes hírcsoportok (például
comp.std.C++ és társaik).
24. óra • JGv,tel,k, hib.k",I's és néhány tanács 1503

Ajánlott olvasmányok
Tömémelen sok jó C++ (és általános programozási) könyv ];ÍloLt napvilágol. Néhány
ezek közül:

• SCQ1T MI!YERS: Effectil.'e C+ + (Addison-Wesley, ISBN: 0201924889, 1997). Második


kiadás.
• MJCKEY WIU1AMS et aL: C++ UlIlcashedCSams, ISBN: 0672312417, 1999). A kifino-
mult C++ technikákhoz.
• JESSI! LUlF.R1Y: Clouds To Code (\YIrox Press, ISBN: 1861000952, 1997). Betekintést
nyerhetünk egy valódi alkalmazásba, melyet C++ nyelven írtak, objektumköz-
ponlú elemzéssel és tervezésseL
• JESSE LIBERlY Cl al.: SC/ms Tcacll YO/lrseIfC++ for liNUX ill 2 / Days(JSBN:
06723 18954, 2000). További tudnivalók a C++-ról és a progrJ.!l1ozásr61 Linux
környezetben.

Az alábbi forrásokat is jó szívvel ajánlom:

• BJAJU-';Il STROUSTII.UI'; nu.! C++ Prosramming LallgL/age(Addison-\'(lesley, ISBN:


0201700735,2000); magyarul a Kiskapu Kiad6 gondoz<'ísáb:tn: A C++ progra-
mozási Ilyc//JOSBN: 963930]1 75, 2001).
• BJUAN W. K.1!1t.\lIGHAN és P.). PlI\UGER: 711C Elements of Programmillg SI)'le
(McGraw-Hill, ISBN: 0070342075, 1988)
• STEVI! OUALLlNE: C Elements of Slyle: 771C Pl"Ogrammer's Slyle Mawull fal" ElegaJlI
C (fnel c ++ Programs (Hl.lngl)' Minds, ISBN: 1558512918, 1992)
• DONALD E. KNUTH: 17IC Art of Computer Programmillg (3 M /e/es sorozat):
Fllndamelllal Algorilhms, Samillllmen"cal A/gor/tllms, és SOl1ing a nd Saarcllill/5
CAddison-Wcsley, ISBN: 0201485419, 1998)
• 711C Praclice aj Prog rammillg by Briall \17. Kernighal1 and Rob Pil..'e(Addison-
Wcsley, ISBN: 02016] ,86X, 1999)
• BRIAN W. KERl'ilGHAN és P.J. Pl.AUGER: Software Tools(Addison-Wesley, ISBN:
020103669X, 1976)
• ClC++ Usersjoumal Chavi!ap), http://www.cuj.com
• Végü l talán haszonnal forgaljákJpssE LmERTY: 711C Complete Idiol's Gu ide to
a C(lraer ln. Co mpu/er Programlllil1g dmú írását (Que, ISBN: 0789719959,1999),
melyben szó esik arról, hogy hogyan érdemes beveLni képességeinket egy re-
mek munka megszerzésének érdekében.

Figyelem! Maradjunk kapcsolatban!


Örömme! fogadunk megjegyzéseket, javaslatokat vagy ötleteket ezzel a könyvvel
(vagy más könyvekkel) kapcsolatban. Látogassanak el honlapunkra
a http://Www.LibertyAssociates.com címre (Jesse Libertyj és
a http://www.cobs.comoldalra(DavidB.Horvath.CCP).Alig várjuk, hogy kedves
olvasónktól visszajelzést kapjunk.
504 1VI. rész· KOlönlegességek

Kérdések és válaszok
Kérdés: Miért használnánk try blokkokat, ha van általános kivételkezelés is?

Válasz: A try blokk speciális kivételkezelést tesz lehet6vé az adon k6dblokkban. Jó-
magam általános kivétclkezclést használo k, ha az egész k6dm vonatkozóan kell meg-
oldani valamit, viszont try blokkokba helyezek nunden speciális e,sete.

Kérdés: Mi a fccnd6m még, hogy elértem a k6nyu ue8ére?

Válasz: Folycassa a tanulást! Próbálja ki más példaprogramjait, írjon saját progmmokat,


és olvasson sokat a nyomtatott vagy a hálózaton találhat6 fo rr~iso k b6l!

Gyakorlatok
Véget ért a 24 tanóra. Válaszoljunk meg néhány befejez6 k6rd6st a kivételkezeléssel
kapcsolatban és lássunk neki egy feladatnak tudásunk meger6sítésére!

Kvfz
l. Mi a try blokk szerepe?
2. Hogy lehet definiá lni a kivétel-továbbdobást?
3. Mi a különbség a throw és a try közöttt?
4. Hogyan lehet megadni az alapértelmezeuen életbe lepő catch blokkot?

Feladatok
Ehhez:I fejezethez csak egyetlen feladat caItozik: Iránya nagyvilág és a programozás!
Próbá lj:l ki a könyvhöz mellékelt összes példaprogramot, és a fordít6programmal érke-
zetteket is.

Válaszok a kvfzkérdésekre
1. A t ry blokkban találhat6 ut.'lsításokra kiváltód6 kivételeket az azt követő ca tch
blokk kezeli.
2. Minden programozó clkészítheti saját kivétel-oszt.ályait, melyet egy-egy alkalmas
továbbdobással (throw) hívhat életre. A kivétel kezelését a hOZ7...á kapcsolód6
catch blokk végzi el.
_ _ _ _ _ _ __ _ _ . 6:::ra=-"-=Kiv"é:::'."I.",k..::h",
_ _ _ _...:2:.:4:.: I
iba:::k:::9Z8:::I::::és:.:é:::s.::"::éh::::á"",y...:ta::"::::ác:::s.J.",
50:::5_ _ _ .-

3. A t hrow egy kifejezetten kért kivétel, melyet maga a program hív meg C Hous-
ton, egy kis baj van~). A try blokk viszont azt tudatja a fo rdíl6progranunal,
hogy ha az adon k6clrészben kivétel keletkezik (bármilyen meghívott könyvtári
alprogmmban, az operációs rendszer szintjén vagy akár egy throw révén), ak-
kor ezl kezelni szeretnénk a kapcsolódó catch blokkban.
4, Ha olyan catch blokkoL szeretnénk megadni, amelyben azokat a kivételeket ke-
zeljük, amelyeket más ca t ch bJokkok nem kaptak el (a s witch-beli de f ault
analógiájá ra), akkor a paramétcrnév helyett három pontot írjunk a zárójelbe:
catch( ... ) .

J
VII. RÉSZ

Függelékek
A függelék A bináris és a hexadecimális számrendszerek
B fUggelék Gyakran használt kifejezések
A FÜGGELÉK

A bináris és a hexadecimális
számrendszerek
Az ember az iskolában már régen megtanulta, nu fán terem az aritmetika. Fontos is ez
a tudás, elvégre mi lenne velünk nélküle a hétköznapokban. Ha rnnézünk egy árc('<.!u·
Jára és azt látjuk rajta ~ 145 ~ , rögtön ki tudjuk olvasni, miszerint ~száznegyvCnÖln . Nem
is volt ezzel semmi gondunk egészen idáig.

De most, hogy programozni tanulunk, jön II keltes meg II tiz(,!nhatos számrendszer, így
újra M kell gondolnunk, mi is az a 145. Ez persze továbbra is cgy számol. jelent, de ma-
ga II leíl'l jelsoroz31 már nem maga a szám, hanem annak a k6dja. És ez nagy különbség.

Na de kezdjük az alapoknál. Nézzük cS:lk meg, mi is II különbség a h:lrmas szám és


a ~3n között. A 3" az tulajdonképpen egy olyan kunkori jel a papíron. A három Illint
szám ezzel szemben egy fogalom. A papírra ín számjegyet ennek a fogalomnak a képi
megjelenítésére használjuk.

A különbségtétel talán még nyilvánva16bb, ha belegondolunk, hogy a hármat számos


más módon is leírhatjuk. A megfelelő szövegkörnyezetben a .', I I I, lU , és .... jelek va-
lamennyien ugyanazt jelentik: három,

j
I
510 VII. rész· Függelékek

A (ízes számrendszerben összesen tízféle számjegyet használunk: 0,1,2,3,4,5,6,7,8,


és 9. Ennek a tíz jelnek a kombinálásával bármely számot leírhanmk. De nézzük csak
meg közelebbről, hogyan reprezentáljuk például a tízet.

A fentieknek megfelel6en hasznáihatnánk nlindenféle kőkorszaki módszereket is, te-


szem azt leírhatnánk egymás után tíz darab A-t, vagy írhatnánk azt hogy 111111111 1. Ezek
is megtes7.ik. Az ókori rómaiak például azt írták: X. M. általunk manapság használt
rendszert az arabok találták ki. Lényege, hogy a számok megadásában nem csupán
a számjegyek számítanak, hanem azok pozíci6ja is. A szám végén, vagyis a jobb szélén
vannak az ~egyesek~, őket követik a ~tízesekn és így tovább. A tizenötöt tehát e rend-
szer szerin két darabból kell összeraknunk veszünk egy dar.lb tizest, meg öl d<lr.lb
egyest, (:s aZl írjuk: l S <.egy", .öt").

A módszerb6l következik néhány egyszen1 szabály:

I. A Líles szá mrendszerben tíz számjegyet használunk (O-9).


2. Al oszlopok (hdyiértékek) tíz harványai, vagyis vannak egyesek, Lízesek, száza-
sok, és így tovább .
.~. ri::! a harmadik pozíció <I szálasoké, akkor kél számjeggyel legfeljebb a 99-el
tudjuk ábriÍzolni. Avagy kissé általánosabban megrogalmazva: n szá mjegy segít-
séghellegfeljebb O és 10n-1 közé es6 számokat ábrázolhalunk. Ilárom szám·
jeggye! péld:"iul O és 10~- 1 vagyis 999 közt: cs6 s:dtmok áb~zollm6k.

Más számrendszerek
Ha jól meggondoljllk, nincs abban semmi véletlenszen1, ho!,'Y a mindennapi életben
éppen a tízes számrendszert használjuk, hiszen tíz ujjunk van. Ebből a felbmerésból vi·
SZOnt rögtön az is következik, hogy ugyanilyen jól hllsználhatnánk bármilyen más
rendszert is. Nézzük csak, miféle szabályokat használ nának a nyolc ujjú emberek:

l. Összesen 8 szá mjegy használható (O-7).


2. A helyiérlC:kck nyolc halványai, vagyis vannak egyesek, nyolcasok, hatvannégye-
sek és így tovább.
3. n .~zámjeggyel l egfeljebb O és 8 n·1 közé es6 számok írh:n6k Ic.

Persze ha mindenféle számrendszerekkel dolgozunk, akkor egy papírra leírt jelsorozat


mást és mást fog jelenteni attól függ6en, hogy milyen helyiértékek szerepel m.:k benne.
Megelőlendó a félreértéscket ilyenkor a szám után jobb alsó indexként oda szoktuk ír·
ni, hogy milyen alapú számábrázolásra vonalkolik az adott forma. Ha tehát tízes ala-
pon akarjuk ábrázolni a tizenötöt, akkor ezt írjuk: 1510.

Ugyanez a sz.1m nyolcas számrendszerben így fest: 178. EZl persle már nem tizenhét-
nek olvassuk, hanem valahogy így: "c!''Y bét nyolcas alapon". Hasonlóan az "igaz i ~ ti-
zenötöt is olvashatjuk így: "egy Öl tízes alapon ~. A lényeg persze mindkét esetben a .ti-
zenöt~, hiszen ez az a szám, allut nlindkét fomla reprezentál.
I "-5',,'_ ___
_ _ _ _ _ ________---'A""fii,,'g"'g."'le""k'-'-""A"b,"in",á..ris"",ás".".'"he"x".d"."c"im"á,"lis"","'","-m"re",n"dsze""r",."kL 0 /

Na de a nyolcas számrendszerbeli ábrázolás az miért is éppen 17? Az 1 az jelenti, hogy


vegyünk egyszer nyokat, a 7 pedig az egyesek helyén áll, vagyis ehhez még hozzá kell
tennünk hétszer egyet. És mivell~ét meg nyolc az éppen ti7:enöt, rendben is volnánk.
Vagy képzeljük el a dolgot még vizuálisabban. Vegyünk 15 csillagot:

** ** .. * *** ..
*****

Az emberek többsége ezt eleve úgy rajzolja le, hogy csinál kél csoportol. Először leraj-
zol10 csillagot, aztán még ötöt. Ez tulajdonképpen nem más, mint a tízes számrend-
szerbeli reprezentáció, hiszen vettiink egyszer tízet és ÖL<;zör egyet Na de léteznek más
csoportosítási módszerek is, mint például ez itt:

Ez itt nyolc csillag, meg még hét, vagyis a tizenöt nyokas számrendszernek m egfe l elő
reprezentációja (17g),

A számábrázolási alapokról
Látható tehát, hogyatizenötöt mindenf(!le számr<:!ndszerben fel tudjuk írni. A szokásos
tízes szá mrendszer aZl írhatjuk, hogy 15, vagy ha precízebbek <lk<lnlnk lenni, <Ikkor
15 10, kilences szá mrendszerben ugyanez 169, nyolcasbrln l71h helesben 217' Ez az utol-
só talán érdemel némi kiegészílŐ magyaráza\ol: hetes szá mrendszerben ugyelúr nincs
nyolcas számjegy, vagyis a tizenötöt két hetesbea és egy egyesb61 kell "összeraknunk".

Nézzük, hogya n lehelne általánosítani a folyamaIot, vagyis a számok különböző szárn-


rendszerek közti Mrását. Vegyi.ink egy példát , Tegyük fel , hogy egy tízes sZ{I1l11'end-
szerbeli számol akarunk átírni hetesbe. A hetes szátnrendszerben van egyes, hetes,
negyvenkilences, háromszáznegyvenhármas helyiérlék, és így tovább. Na de mire is
valók pontosan ezek a helyiértékek, vagy ha úgy telszik oszlopok? Ezek mlajdonkép-
pen a 70, 7 1, 72, 73 hatványoknak felelnek meg, és így tovább. Talán foglaljuk táblázal-
ba a dolgot az {Itlekimhelőség végett:
,
4 3 2 1
7' 7'
343 49 7

Apropó Bánnely szám nulladik hatványe


Az matematika egy alapszabálya szerint bármely szám nulladik hatványa eggyel
egyenlő: 70 = 1, 100 = 1,217,549,3430 = 1.
I
512 ini. rész • FOgg,lék,k

Az els6 sor a helyiérték sorszáma . A másodikban láLható hét megfelelő hatványa , a har-
madik pedig maga a helyiérték, vagyis hét megfelelő hatványának értéke decimális for-
mában. Ha egy decimális szá mot szeretnénk hetes alapra konvertálni, a következő al-
goritmus szerint kell eljárnunk. Először is keressük ki azt a legnagyobb helyiértéket,
amivel még osztható a kérdéses decimális szám . Ha például a decimális 200-Z.1t szerel-
nénk átahlkítani , a kko r a 4 oszlop (343) már nem jó, mert ezzel osztva az eredmény
egészrésze nulla lesz. Egyszóval ezzel a helyiértékkel már nem is kell foglalkoznunk.
A lege l ső, ami részt vesz az algoritmusban a 49-es helyiérték. Osszuk tehát el 200-at 49-
cel. Az eredményegészrésze 4, vagyis a hetes számrendszerbeli számnak ezen a pozí-
ci6ján a négyes sz{im áll majd. A2 iménti osztás maradéka 4. Ezt a soron következ6
hclyiénékkcl, vagyis 7-tel nem lehet osztani, pomosabban le hetni éppen lehet, csak
nulla lesz az egészrésze. Így a hetes számrendszerbe li szám hetes hdyiértékén nulla
fog állni. Végül maradtak :I l egyesek. Az nyilvánvaló, hogya négybcn az egy négyszer
va r' m(.!g, vagyis az egyesek helyé n 4 lesz. Az eredmé ny tehát 404,.

Nézzünk t:d{111 még egy példát Tegyük fe l, hogya 968 10 decimális számol szeretnénk
hatos számre ndszerben ábrázolni. Ehhez a következő tábl:izatra lesz szükségünk:

5 4 3 2
64 6' 6' 6' 60
1296 216 36 6 l

A 968-b:lIl nincs meg az 1296, (ehát ezzel a helyiénékkel már nem kell fogla lkoznunk.
/l.z eredmé ny ötödik pozícióján már nulla áll . 968-lIt 216-t:rl Q.o;ztva az eredmény egészre-
sze 4, :l maradék pedig 104. A negyedik helyiénéken tehát 4 áll. A 104-et36otal o.o;ztva
az e redmény 2 a marndék 32 . A harmadik helyiénéken szere pl6 számjegy tehát 2. a 32-[
60tHI osztva az erwmény 5 II maradék 2. a végeredmé ny tehát 425~ avagy táblázatosan:

5 , 3 2 l
6' 63 62 6' 6'
1296 2 16 36 6 l
O 4 2 5 2

li a val:HI1i1yen más számre ndszerból konvertálunk IrZeSfi:!, a m6dszer va lamivel egysze-


rűbb, hiszen csak szorozni és összeadni kell. Példaként végi:!zzük cl :I fen ti konverziÓ(
visszafelé:

4·2 16 864
2·36 72
5•6 30
2•1 2
968
A fOggeiék · A bináris és a hexadecimális számrendszerek 1513

Kettes számrendszer
A kettes számrendszer a lehetséges számrendszerek amolyan "alsó határát" képviseli,
hiszen ill már csak kétféle számjegy képzelhető el, egyes b nulla. A fem bemutatott
konverziós táblázat kenes s7.áml'endszerre a következ6képpen fest:

Oszlop: 8 7 6 5 4 3 2 1
lIatvány: 27 2' 2' 2' 2' 2'
Ilelyiérték: 128 64 32 16 8 4 2 1

Nézzünk meg iu is egy példát a konverzi6m. Tegyük fel, hogy a 88 10 sz!imot szerel-
nénk viszontlátni hináris formában. A 128-as helyiértékkel már nyilván nem kell roglal-
koznunk, mivel ez a 88-ban nincs meg. A nyolcadik helyiértékcn tehát les7.. °
A 64 a 88-ban egyszer van meg, v:lgyis II hetedik helyiérték 1 lesz, az oszt!'IS maradéka pe-
dig 24. Ebben a 32-es helyiérték nincs meg egyszer sem, teMl " hatodik pozíci6n O lesz.

A következő helyil':rték a 16. Ez megvan egyszer a 24-ben, vagyis az ötödik helyiérték


1, a maradék pedig 8. Ebben éppen egyszer van meg a következ6 helyiérték, vagyis
a negyedik helyen 1 szerepel majd, az össze többi helyen pedig 0, hiszen ennél a rész-
lépésnél nem keletkezett maradék, a konverziónak tehát vége. Az eredmény:

o 1 Ol 1 OOO
Az eredmény helyességét természetesen le is ellenőrizhetjük, ha visszaalakítjlIk a femi
kt!llCS szil. rnrcndszerbe!i SZlÍmot decimálissil.:

l
O
l


..
32
16

~
"
O
16
l
O

,•
8
~
8
O
O • 2 O
O • l O
88

Miért éppen a kettes alap?


A kettes S7.1unrendszen az teszi minden másnál megfelelőbbé a szá mítógépekkel kap-
csolatban való használatr.t, hogy rendkívüli módon hasonlít a gépek alapvető logikájá-
ra, arra, amit egy számít6géppel egyáltalán meg \ehet oldani. A gépek alapvetően na-
gyon buták. Fogalmuk sincs sl.iJ11okról, betűkr6l, utasításokr6l mcg programokról.
A szá mítógép némiképp leegyszerűsítve nem más, num egy csomó áramkör, amelyek-
ben vagy folyik éppen áram, vagy nem, egy·egy csatlakozáson vagy magas a feszült-
ségszint, vagy alacsony.
514 1VII. rész • Függelékek

A logika egyszerusítése végen a mérnökök nem különböztetnek meg köztes fes7.üILSég-


szinteket, vagyis a számítástechnikában nincs o lyan, hogy kis feszülL'iég, kicsit na-
gyobb feszültség, közepes feszültség, magas feszültség meg óriási feszültség. Itt csak
annyi számít, hogy .elég feszültség" van-e, vagy éppen "elégtelen". A rendszer tehát
röviden szólva bin{lris. Kicsit továbblépve az ahsztrskd6k felépítésében az .elég" meg
az .elégtelen" helyett egyszeruen annyit szoktak monelani hogy "igen" vagy .nem".
Esetleg megfelel ő az .igaz és a . hamis" is. Akármelyik terminológiát használjuk is,
H

a két különböz6 szintel kiv{116an lehet reprezentálni l -gyel és O-vaL Megegyezés sze-
rint 1 jelemi az .igen" vagy . igaz" énéket, O pedig a ~nem" vagy "hamiS" megfele16je.
Fontos azonban hangsúlyozni, hogy ez csak egy megállapodás, vagyis elvi ak:ldálya
nem volna annak sem, hogy a dolog épp fordítva legyen.

Most, hogy tisztában vagyunk az alapelképzeléssel, talán már az is világos mindenki


szá mára, miért é ppen a kettes számrendszer a "nyer6". Eggyel és nullával éppen aZT
lehet reprezel1lálni, amire egy áramkör a számitógépben képes: van feszü ltség, vagy
nincs. Minden, amit a számítógép "tud" nem egyéb, mint hogy melyik áramkör van ép-
pen bekapcsolva, és melyik ki. Ami be van kapcsolva, az neki 1, ami nincs,:LZ 1.

Bitek, bájtok, nybble-ek


Miután megegyezlük magunkkal és a hOZ7..ánk hasonlókkal, hogy 1 fogja az igazat és O
a hamisat jelenteni, a bináris sz.1mok, és azok számjegei, il bitek fontosságukban a többi
szám fölé emelkedlek. Mivel az els6 számítógépek egyszerre 8 bitet voltak képesek ke-
zelni , magától adódott, hogya biteket nyolcasával állítsuk be programoz{ls közben. 1.11-
nen pedig már csak egy lC:pés voll, hogy n bitnyokasok nevel is kapjnnnk: ők a bfijtok.

Apropó Kis bájtok


A bájt felét, vagyis négy bitet szokás nybble-nek nevezni. Talán azt is érdemes
megemlfteni, hogy ezt egyes helyeken nibble-nek frják.

Egy bájton, vagyis 8 biten összesen 2S6 különböző énéket ábrázolhatunk . Hogy mjénJ
Nézzük meg a lehetséges helyiénékeket. Ha minden bitet l-re állítunk, akkor a kelet-
kező bináris szám decimális értéke 255. Ha minden bit O, nkkor -természetesen - O
lesz a decimális érték is. Mivel pedig ebben a számábrázolási módszerben nincsenek
lyuknk, O és 255 közölt bármely egész szá mOl képesek vagyunk 8 biten megjeleníteni.
Ez pedig éppen 256 különböző érték .

Mi az a KB?
J Ja meggondoljuk 210 (vagyis 1024) úgy nagyjából egyenlő l ()3~nal (vagyis WOO-rel).
Nos, ez egy olyan "döbbenetes hasonlóság" amit egyszeruen nem lehetett kihagyni
a terminológia megalkotása során, így aztán az informatikusok a 21 0 byte adatm ennyi~
séget elkezdték kilobájtként emlegetni, és 1 KB·nak leírni. "Normális" esetben - énesd
a tudomány egyéb területein - a .kilo" előtag ugyebár ezret jelent, az a maradék 24
meg végül is kit érdekel.
_ _ _ __ __ _ _ _ __ _ _"A"fü..,"g"g,..I..é"k_"-'A"b"in...á..."...s ..é..
s ..
'"h"'
.x"....d...c...im I..
.."ál'i..s..s"zá...m..re...n..d=Sle=re...kJ 5..
15_____ .;,

Hasonlóan laza szemléletet követve az 1024 * 1024 cl 048576) elég közel esett az egy-
millióhoz, így ő lett a megabájt, vagy 1 MB . És ha már itt taltunk, miért hagynánk abba
a dolgot. 1024 megabájt az - kis e1hanyagolással- lehet 1 gigabájt (a giga előtag ezer-
milliót, vagyis egymilliárdot jelent).

Bináris számok
A számítógépek tehát egyesekből és nullákból álló mintázatokat használnak llunden-
nek a kódolására. Egyesek és nullák kóJolj5k azokat a gépi utasításokat, amelyek
az alapár:clmköröket vezérlik. Tgaz ugyan , hogy egyesek és nullák mindenféle halmazai
számokká alakíthatók a megismert szabályok szerinL, hiha volna azonban azt képzelni,
hogya számítástechnikában ténylegesen minden szám. Jócskán vannak olyan bináris
jelsorozatok, <lmelyek jelentésüket tekintve semmiféle kapcsolatban nem állnak
a számfoga lommal.

Az. [ntel xR6 processzorok egyik utasítása például így néz ki: 10010101. Természetesen
senmli meg nem akadályozhat bennünket abban, hogy tm "lefordílsuk" dccimálisra és
azt mondjuk: 149. Ennek a sz,'mnak azonban ebben az összefüggésben az égvilágon
semmi jelentése nincs . III egyszerűen másról van szó.

Vannak bináris számok, amelyeket UlasításkénL kell értelmezni, mtisokat adatként, VHgy
kÓdkénl. Az egyik fontos kódolási rendszer például az ASCII. Ebbe n a kódrendszerben
minden betűnek, számjegynek és írtísjelnek megfelel egy-egy 7 bites bin{lris szám .
A kis "a~ betűnek például a 01100001 bináris k6d felel meg. Ez ebben az összefüggés-
ben nem szám, mert bár mondhatjuk rá, hogy ez nem más mint 97 10 (64 + 32 + cle n,
azért itt mégis a kis ,,<1" betűről van sz6, nem m{lsr61. Sokan szokták nt is mondani,
hogy az ASCII k6doi<ísban a kis "a" betűnek a 97 felel meg, az igazság azonba n az,
hogy cz csak egy olyan apró tévedés, amit nem szoktunk sz6vá lenni. A helyes kijelen-
tés valahogy úgy hangzik, hogya 97 bináris rcprezcntílciój:t, vagyis a 01100001 az "a"
betű kódja. Az, hogy mi a 97-et emlegetjük, csak kényelmi szempont, semmi több.

Tizenhatos (hexadecimális) számábrázolás


A bináris számokat elég nehéz olvasni, így szükség volt egy kényelmesebb, áttekinthe-
tőbb írásmódra. Ha bináris sZámollízes sómrendszerbe akanlnk átírni, akkor - amint
láttuk - az algoritmus nem bonyolult ugyan, de hOSSlú. A 16-os számrendszerbe való
átírás eZle! slemben egészen egyszerű, van benne u!:''Y~mis benne egy kézenfekvő egy-
szerusítési lehelőség.

Ennek megérréséhez azonban először meg kell ismerkednünk a lilenhatos, vagy köz-
kel etű nevén hexadecimális számrendszer logikájával. Alizenhatos sómrendszerben
tizenhat számjeg!:''Yel kell dolgoznunk, amelyek a következők: 0, 1,2,3, 4,5,6,7,8,9,
A, B, C, D, E, és F. Az utols6 hal ~számjegy" kiválasztása tulajdonképpen tetsző l eges
51s 1VII. rész' Függelékek

volt. Azért pont az A-F betúket használjuk erre a célra, mert ezek minden billenty\1ze-
ten megtalálhatók. A hexadecimális számrendszer helyiétték-táblázata a következőkép­
pen fest:

4 3 2 1

256 16 1

A hexadecimálisr61 decimálisra történő átalakítás természetesen továbbr.!. is csak szor-


zás! és összeadást igényel. Az FSC 16 .szám decimális értékér például a következőkép­
pen kaphat juk meg:

F * 256 = 15 • 256 : 3640


8 * 16 = 126
C * 1 = 12 * 1 • 12
3980

lia az FC l6 számot binárisra szeretnénk lefordítani, .lkkor .1 lcgcgyszeníbb módszer ta-


lán az, ha elóbb kiszámhjuk a decimális értékét, majd azt konvertáljuk ál kettes szám-
remlszerrc:

F * 16 = 15 * 16 ~ 240
C * 1 = 12 • l = 12
252

A 252 10 decimális érték bináris al11km konvertálása a következő - korábbr61 már ismert
- táblázat és algoritmus szerint történik:

Oszlop: 9 8 7 6 5 3 2 1
Hatvány: 28 27 26 25 24 23 22 21 20
Helyiérték: 256 128 64 32 16 8 4 2

A7. 256-os hclyi(;nék"n o $ZercpeJ.

l 128 maradék 124


l 64 maradék 60
l 32 maradék 28
l lG·maradék 12
l 8 maradék 4
l 4 maradék O
O
O
l l l l l 100

A végeredmény tehát a következő bináris szám: 11 11 1100.

A feladatot tehát tulajdonképpen megoldottuk, de felfedezhetü nk az egészben egy


egyszeru trükköt. Tekintsük ugyanis a fenti nyolc bitet két négybites egységnek, és ve-
gyük észre, hogy ez lehet6séget ad e,gy "mágikus" átalakításra.
A függelék · A bináris és a hexadecimális számrendszerek 1517
--------~=~~=-=-=======~- ~---,,'

A jobb oldali bitnégyes az 1100, ami decimális 12-nek, vagyis hexadecimális C-nek fe-
lel meg. A bal oldali bitnégyes 1111, ami decimális 15, vagyis hexadecimális F. Röviden
tehát a következő megfeleltetést sikeIÜl találunk:

1111 1100
F C

lia a két hexadecimális értéket egyszeruen egymás mellé írjuk, visszakapjuk az FC ér-
téket, vagyis azt, amiből az 111111002 bináris értéket kaptuk. Némi fejtöréssel belátha-
tó, hogy ez a négyes csoportosítás mindig ml1ködik, vah'Yis tetsző l egesen hosszú biná-
ris számot átírhatunk hexadecimálisra úgy, hogy a be l őle alkotott bitnégyeseket egyen-
ként lefordítjuk, és a kapott hexadecimális digiteket egyszeruen egymás mellé írjuk.

Mi több, a dolog visszafelé is tökéletesen mííködik. Ha tehát hexadecimálisr61 szeret-


nénk binárisrJ átírni egy számot, nincs más dolgunk, mint a tizenhatos számrendszer-
beli alakot különálló digitekre bontani, ezeket egyenként átírni binárisra, majd a kapott
bitsorozatokat egymás mellé írni. Ha visszaemlékszünk rá, hogya kettes szá mrendszer-
ben az első négy helyiérték rendre a 8, 4, 2 és 1 decim,í lis értékek nek felel meg, a he-
xadecimális digitek egyenkénti átírása egész egyszeníen e lvégezh ető . Lássunk talán
egy hosszabb példát:

101 1 0001 1101 0111

Az ebben a bináris számban előfordu l ó helyién6kek a következők: l, 2, 4, 8, 16, 32,


64, 128,256,512,1024,2048,4096,8192,16384, és 32768. Ha tehát átírjuk clecimálisra
az ismert algoritmus szerint, a következ6t kapjuk:

1 x 1
1 x , ~
,1

1 x 4 4
O x 8 O
1 x 16 ~ 16
O x 32 ~ O
1 x64 ~ 64
1 x 12' 128
l x
255
" 256
O x
512 " O
O 1024
x O
O x
2048 O
1 x
4096
" 4,096
l x
8192 8, 192
O x 16384 O
l x 32768 32 , 768
Összesen : 45 , 527

Ennek a decimális értéknek a hexadccimálisra való átírása során a köv e tkező helyiérté-
kekkel kell foglalkoznunk:

6 5535 4 096 256 16 l


518 1VII. re" • Függelékek

A 65535--ös helyiértéken nyilván O lesz, hiszen 45527 nem osztható ezzel a számmal.
Az első "érdekes· helyiérték tehát a 4096. Ez ll-szer van meg az eredeti számban,
a maradék pedig 471. A következő hclyiérték a 256, ami 47 1-ben egyszer van meg,
a marddék pedig 215. Ebben a következő , 16-os helyiérték 13-szor van meg (ez össze-
szorozva 208), vagyis a maf"dd6k 7. Összességében tehát a fenti decimális illetve bináris
számnak megfelelő hexadecimális érték a 81D7.

Biztos ami biztos, ellen6rizzük le, hogy valóban így van-c:

B (11) * 4096 = 45.056


1*256=256
o (13) * 16 .. 208
7 * 1 = 7
CnS2cscn 45.527

E7. VOll tehát::l "gyalogos" módszer. Most nézzük meg, hogyan fest mindez
az imént felfedezett "négy bites rövidítéssel", Az átvált:tnd6 bináris érték ugycb:ír
az 10'11000111010111, amelynek a következ6 bitnégyesek felelnek meg: 1011 0001
11010111. L,í.ssuk, hogyan festenek ezek 3 négyjegyíi bináris számok egyenként hexa-
decimális reprezt::ntáci6ban:

1011
1 , 1 ·• 1
,
1
o,
2
,• 2
o
1 , 8 • 8
Oss zes en : 11
Hexadecimális : B
0001
1 ,1 • 1 ·
O ,2 O
O ,4 • O
O 8 • O
Osszesen : 1
Hexadecimális : 1
1101
1 , 1·• 1
O , 2 O
1 , 4 • 4
1 x 8 8 •
OS6 zesen : 13
Hexadecimális: D
0111 •
l x l l
l x 2 2
l x 4 II
O x 8 '" O
Osszcsen? 7
Hexadecimá lis: 7
A végső hexadecimális alak : BlD7

A módszer tehát in is működött.


.'

B FÜGGELÉK

Gyakran használt kifejezések


Ebben a függell:!kben összegyt1jtötlük a leggyakrabban el6fordu16 szakkifejezések a rö-
vid meghatározását Tankönyvr61 lévén szó a rendszerezés tekintetében ~I felbukkanás
sorrendjét tekintettük elsődlegesnek, vagyis az órák szerint csoportosítOLtuk a meghatá-
rozásokat. így ha az olvasó a könyv használata során olyasmivel ta l:'i.lkozik, amit nem
én, ehhez a függeJékhez hátralapozva gyorsan információhoz juthal.

,
,. Ora
Olyan linkc[het6 (bináris programjainkhoz szerkeszthet6) fájlok
K6 11)'Vfiir (lib l"01yJ -
gyűjteménye, amelyek érkezhetnek a fordít6progmmmal egyillt, de külön is mcgvásá-
rolhatjuk, vagy mi magunk is létrehozhatjuk 6kel.

Fiigg vény (jilllction) - Egy viszonylag önálló kódrészlet, amely egy bizonyos feladatot
hajt végre. Ilyen feladat lehet például két sZ;'Ím összeadása vagy v:llaminek a képernyő­
re való kírása.
I
520 VII. rész • FOggelók,k

Osztály (e/ass) - Egy új típus definíciója. Az osztály adatok, és a rajtuk vagy velük dol-
gozó függvények gyűjtemé nye.

Fordítóprogram (compjler)~ Olyan program, amely képes az ember sz.ámám is o lvas-


ható k6dszövegb61 (forrásk6dbóO gépi kódú á llományt el őá llítani. A fordítás kél lépés-
ben történik: el6bb egy tágykódú állomány (object cooe) jön létre, amit aztán a linker
(lásd a megfelelő címszöt) futtatható programmá alakít.

lilll...>er (szerl. . esztő) - Olyan program, amely végrehajtható (futtatható) állományt hoz
létre a fordítóprogram által el6állitott tárgykódú állománybó l (object code).

Objektum-MzjJonttí (object-orie1lted) - Programozási metodol6gia, amely a proce-


durális és a strukturális programozás utáni következő fejlődési lépcsőnek tekinthet6.
Amint arra neve is utal, ez a megközelítés úgynevezett objeklumokk;l l (osztályelemek-
kel) dolgozik, ezekhez rt!ndellulajdon.ságokat és viselkedésformákat. Gyakori, hogy
ezt a kifejezést c1s6sorb:ln markeLing célokkal nem megfelelO" összefüggésekben is
használják.

00 - Lásd az objektum-központú címszót.

ANSI (A/I/(-!I"Cfllll Natiol1al Standards Illslffute) - Nonprofit SZCIVCz.et, amely az Egye-


sült Államokban használatos szabványokért feJel6s. Számos országnak, iIle(ve régió nak
(például az Európai Uniónak) is van hasonló szeIVezete. Ezek egyes esetekben a kor-
m:'m yzat részeként mtlködnek, ám az ANSI nem kormányzati szeIV. További informáci-
Ót a http : //www . ansi. org címen találhatunk.

ISO (Imemationtll Orgtlllizationfor Slalldardfzat;Ol1) - Egy az ANS I-hoz hasonló, de


a nemzetközi szabványokért fclcJ6s szeIVezeL Az ISO szintén nem kormányzati szeIV.
B6vebb információt a http : //www . iso. org címen találh:uunk.

2. Óra
Fordítás (co li/piliny - A forráskódot futtatható állománnyá alakító folyamat első lépés.
ilye nkor a fordítóprogmm eh'Y úgynevezett tárgyk6dú állományt hoz létre (. obj fáj!),
a mit, aztán a linker abkít fullatható programmá.

Szerkesz/és (/i nkeiés; lillkillg) - A forráskódot futtatható állománnyá alakító folyamat


második lépése, amelyben a linker a fordítóprogram által eJ6állított tágyk6dú ;.momá-
nyokból összeszerkeszti a nmatható programot.

Futtafharó program (execulable program) - Program, amely az operációs rendszer fel-


ügyelete alatt közvetlenü l futtatható.
B függelék' Gyakran haSl1lá~ kifojezések 1521

Él1e/mező (interpreter) - Az imcrpreler olyan program, amelyek a forráskódot a p rog-


ram futása közben, soronkém alakilja gépi k6ddá.

Pmcedulrilis programozás (procedl/ral p rogramm ing) - Olyan fejlesztési paradigma,


mely szerint a program egy adathalmazon végrehajtott műveletek sorozata.

Slmk/uráll programozás (strclured programming)- Olyan programfejlesztési paradig-


ma, melyné l a megoldandó fekldatot szisztematikusan egyre kisebb részekre tagoljuk,
és a részfeladatok megoldás<íra írju k meg a megfelelő eljárásokat.

Beágyazás (ellcapsulalioll)- Önmagukat meghatározó, önálló objektumok létrehozása.

Ada/rejtés (data hiding) - Az objektum állapotának elrejtése a külvilág e161 privát adat-
tagok használ<lt:lval.

Órok/ődés (in herilance)- Új típus létrehozása úgy, hogy az csupán kiterjeszti egy m5r
meglevő típus lulajdor,ságait,

T6bbafakríság, polImorfizmlIs (jJolyI/JOIphism) - Az a ké pesség, amikor számos altí~


pust CtípusváltozalOl) tudunk úgy kezelni, mintha azok egyetlen alaptípus vál t07..atai
lennének.

Előf(!/dolgoz6 (preprocessor)- Olyan program, amely a fordítóprogmm clótt fut Ic, és


változtatásokat végez a forrásk6don, az abban található il jellel kezdődő sarok (dircktí-
vák) a lapján.

Megjegyzés (commenl)- Olyan szövegrész a forrásk6dban, amely nem képezi annak


integráns részél, csupá n II programozó s7.ámára hordoz információt.

Aláírás (sz/glia/lÍra; sigllature)- Eb'Y függvény neve és annak argumentumai együttVéve.

,
3. Ora
Vciltozó (va ritibie) - Névvel ellátott hely a me móriában , amelyben egy é rtékel tíÍrol-
hatunk.

RAM (Random Access Memo /y) - Véletlen hozzáférésű memÓria.

rrpus (type) - Egy o bjektum mérete és egyéb jellemz6i.

Előjeles (sig l/ed) - Olyan változ6típus, amely pozitiv és negatív értékeket egyaránt
tárolhal.
522 1VII. rész · Függelékek

E/ójelllélkt7/i (1I115i8,I1OO)- Olyan változó[Ípus, amely kizárólag pozitív sz:'lmokat tárolhat.

ASCII (Americali Stalldrd Code/or biformaliOlI E:.\:change)- A legtöbb számítógéptípus


állal h:lszn:ílt kódolási eljárás, amellyel számok, betúk és írástelek írhat6k le numerikusan.

Érzéke1lység a kis- nagyhe/ űkre (case-sensitiue) - Olyan helyzet, amikor logikai kü-
lönbség van a kis- és a nagybelúkkelleílt információ között. CA C++-ban például
a myVal és a Myval nem azonos objektumokat jelent.)

7iJmsdefll1ícl6 (type definilion)- E&'Y adattípus meghatározása. Az így megadott típus


kés6bb ugyanúgy használható, mint a beépített típusok.

Állal/dó (cO I1Slalll) - O lyan tárolási egységek, amelyek tartalma a progr::lIn ruLása során
nem változik.

Beta szeriIIf é/1elll/fJz(Jndő cíflandó (/irem' cOl/stant) - Olynn érték, ,unit közvetlenül
a fo rrflskódba gépe ltünk be. Ha például a kódban fe lbukkan valnhol n 35, a kkor ez
egy liler!i lis 1\ ll3ncló.

Szimbo/il.ms álfclI1dó (symbolic cOllsf(mt) - Olyan típussal és névvel rendelkező érték,


amit 1\lIandónak deklar.''ilunk. Ilyen lehet péld(1U 1 egy anyag fo rr!tspontja (BoilingPoint).

Fe/sorolt ál/'IIId6k (ellllll/ef'(/ted constants) - Állandók névvel rende[kez6 ha[ma2.1.

,
4. Ora
Utasítás (statemel1t) - Olyan módszer, amivel a végrehajtás menetét, vagy egy kifejezs
kiértékelését tudjuk befolyásolni.

Ores karakter (wftiteS/XIce)- Ilyen a szóköz, a tabulátor és az újsor karakter.

Összetett /ltasítás (co mpoul1d statement) - Egy nyitó és egy záró kapcsos zárójel között
megadott utasít(\ssorozat, a me ly bárhol felbukkanhat, ahol egyetlen utasítás is.

Kifejezés (e;\p ression) - Bármely olyan utasítás, ame ly vissza;ld vn lamilyen é rtéke t.

Operátor (opercuolj - Olyan szimbólum, amely egy bizonyos utasítás végrehajtására


készteti a forclít6progrdmot.

Operandus (operand)- Matematikai szakszó, amely egy kifejezésnek azt a részét jelöli,
amely a benne szereplő operátor által feldolgozott adatnak vagy objektumnak felel meg.
B függelék· Gyakran haszná~ kifejezések 1523

Hozzárcndclő operátor (assignmenl opel"Cltor; " ) - Hatására az operálor baloldalán


szerepl6 operandus a jobb oldalon szereplő értéket veszi fel.

Ba/é/1ék (l-lx/lue) - A bal érték olyan openmdus, amely egy oper.ítor baloldalán
szerepell1ct.

jobbtJrték (r-vahwJ - A jobbérték olyan opemndus, amely egy operátor jobb oldalán
szerepelhet.

Relációs operátorok (relatiollal ojJCnors)- Olyan operátorok, amelyekkel megállapít-


hatjuk, hogy kél szám egyenl6-e, vagy hogy az egyik kisebb vagy nagyobb-e, mim
a másik.

IlIkrcmCllláléis (il/crementil1g) - Egy változó értékének eggyel való növelése (a ++


opcr:1torra l végezhet6 művelet).

Dckrememáltls (dccramcllting) - Egy vállOZÓ értékének eggyd való csökkentése (a --


o perátorral végezhet6 művelet).

E/ő/ag opcrálor (prefu: opera/or)- Az inkrementáló vagy dekrementáló operátor olyan


használata , amikor el6bb jut érvényre az opeditor, s csak aztán énékel6dik ki a kifeje~
zés (például ++nlyAge).

Utótag operátor (posrfix opera/ot) - Az inkrementáló vagy dekrement51ó operátor


oly:ul használata, amikor cJ6bb kiértékel6dik a kifejezés s csak aztán jut élvényre
az oper{tLor (p(:ldául myAg e H).

ho~,.y a fordító-
Mi/ve/eff rangsor (precedencia,. precedence) - Ez a rang határozza meg,
progrJ.mmlk - zárójelezés hiányában - milyen sorrendben kell kiénékelnie az egyes
operálorokat.

,
5. Ora
Verem (stack) - Olyan speciális mem6riaterület, amit az operációs rendszer automatiku-
san lefoglal a futó program számám, és amely a függvények és az 6ket hívó k6drészle~
tek közti kommunikációra szolgál. A vermet szokás UFO~nak is nevezni (lásd ott).

UFO (/..asI III First Out) - Az ilyen szervezésú mem6riából kiolvasáskor legel6ször
a legutoljára beín érték kerül e16.

Függ/Jénydek/aráció (function dec/araNoll) - Egy függvény formális leírása, ameJyool


a fordítóprogram megrudja annak nevét, visszatérési típusát, valamint hogy meiUlyi és
mi lyen típusú paramétere van.
524 1VII. rész • FOgg,lók,k

Prototípus (prorot)'pe) - A függvénycleklaráció másik elnevezése.

Fiiggwnydefillíci6 (fimcfiOll definilion) - A függvény működésének, kódjának leírása,


vagyis az a forma , amely nem csak a deklarációban megadott információkallartalmaz-
za , hanem a konkrét megvalósíLásl, a függvénytörzset is.

Filggoony paraméterlistája (fu nClion parameter lisO - Egy függvény be menő paramé-
tereinek fe lsorolása, amely vesszőve l elválasztva tartalmazza az argumentumok tipusát
és esetleg nevét.

Helyi változók (local vtl1iables) - Olyan változók, amelyek csak egy adott függvényen
belül léteznek.

Érvényességi Mr (scape) - A k6dnak az a területe, amelyből egy adott vá ltozó látható


és e l érhető ,

Globális változók (g loba! varlabfes) - Olyan vá ltozók, amelyek egy program bármely
pontjflr61 elé rllct6k,

6. Óra
Iteráció (ileralfoll) - Olyan múvelelSor, amely során ub'Y:mazt a tev{: kenys~get végez-
zük újra és újra.

Végtelen ciklIIs (fl/fil/ ite loop) - Olyan múvelelSor, amely során ugyanazt a tevf:kenysf:-
gCt v{:gezzíl k vég nélkül, Ez általában olyasmi, amit a programozók igyekeznek elke-
rülni, bár bizonyos helyzetekben megvan a maga haszna,

SpagclrI/..>6d (spaghcll i codc)- Kevéssé stnlkturált módon, ösSZeViSSL'l megírt program ,


:Imely nehezen olvasható, Ncvét onnan kapta , hogy logika ilag (Igy néz ki, mint egy tál
,~pagctl i , :unelyben gyakorlatilag lehetetlen követni egy-egy szál lefutását.

,
7. Ora
Kliellsck (eliel/IS) - Olya n osztályok vagy függvények, amelyek az álmlunk fejlesztett
osztályt használjiik,

Tagvállozók ( member vun'ables) - Ezeket szokás adattagoknak (dala members) is ne-


vezni. Gyakorlatilag egy osztály saját változ6készletét szokás összefoglaló néven így
említeni.

Adal/agok (dala members) - Lásd a Tagvállozók címsz61.


B függelék · Gyakran hasmált kifejezések 1525

Tagfüggvények (membIT functions) - Ezeket szokás tagmetódusoknak (member


methods) is nevezni. Egy osztály saját függvényeit szokás ilyen összefoglaló néven
említeni.

Tagmetódusok (member methods) - Lásd a Tagfüggvények címsz6t.

Objektum (objectJ - Egy osztály egy eleme vagy példánya.

Nyilvános hozztífén'js (public access) - Nyi lvános hoz7.áférésr61 akkor beszélünk, ha


egy tagváltozóhoz vagy tagfüggvényhe7. bármely más osztály bármely met6dusa hozzá-
férhet.

Privát hozzáférés (private access) - Privát hoz:láférésrGI akkor beszélünk, ha egy tag-
vál~ozóhoz vagy tagfüggv6nyhcz kizárólag egy osztály saját met6dusai, illetve a belőle
származtatott osztályok met6dusai férhetnek hozzá.

Hozzéiférési mefódusok (acccsso r mCfliods)- Olyan metódusok, amelyek kifejezetlen


a priváladattagokhoz való hozzáférés biztosítása végett készültek.

Mefódusdefiuíció (mefl/od defi nit/on) - Olyan függvénydefiníci6 , amely egy osztály


nevével kezdőd i k , majd két kettőspont után a függvény neve és a paraméterei követ-
keznek.

Afapé/1elmezett konsfmklo r (defal/It constn./ctol:) - Olyan konstruktor, amelynek nin-


csenek bemenő paraméterci.

,
8. Ora
Állandó lagfi./ ggwny (constant member fUl1ction) - A konstans ragfüggvény olyan
függvény, amely nem változtatja meg egy osztály egyetlen tagválroz6jának érlékét sem.

IlIfcrjész (ínterJace) - Szokás osztályfeJületnek vagy osztályintcrfésznek is nevezni.


Azoknak az ac1atoknak és metódusoknak az összességét jelenti, amelyekhez más o.~ztá­
Iyok és függv ényck hozzáférhetnek. Ez tehát nem magának az osztálynak a kódja, ha-
nem csupán azt mondja meg, hogyan használható az kívülr61. Ezt az információt rend-
szerint egy különálló, kifejczcncn az adott osztályhoz tartozó fejlécállományban tárol-
ják, amit az összes az osztályt használni kívánó moclulnak be kell szúrnia.

ImPlementáciÓ (implementation) - Szokás oS7.t.ályme!,'Valósításnak va!:,'}' osztá lyimple-


menláci6nak is nevezni. Egy adott osztály kódját és deklarációit nevezzük tb'Y-
Ez az a kód, amihez az interfészen keresztül hozzáférhetünk. Az osztályok megvalósí-
tását rendszerint egy külön . epp fájlban tároljuk, amit aztán tárgy kódú állománnyá le-
526 1VII. rész • Függelékek

fordítva, vagy könyvtárba helyezve lehet használni. Sok esetben a felhasználónak csak
a tárb'Ykódú állomány és az interfésztleír6 fejléci llomány áll rendelkezésére, amelyek-
kel használhatja az adott osztályt, de nem módosíthatja a kódját.

,
9. Ora
Mu/aló (pointel) - Olyan változó, amely egy mem6riacímel tárol.

lndirekció (illdoreclioll) - Az a műve l et, 3nlikor egy értékhez az annak a címét L1ro16
mutató segítségével férünk hozzá.

DinCtmikuSCIII kezelt mell/ória (heap) - A mem6rián:-l k az a területe, amely a k6dleti.i-


Jel, a globális névtér és a verem lefoglalása után mcgm:-lmd. Szokás eLL a részt "szabad
tárnak" is nevezni. Ez az a mem6riaterület, ameJyb61 a new operátor és a mal loe ()
függvény kihasítanak egy-egy darabot a dinamikus tárkezelés során,

,
10. Ora
Eltévedt mutató (s/ray poil/tCr, dangling poillfer) - Olyankor beszélünk általálxm err61
a jelenségről, amikor a már fdszabadítOltuk azt a mem6riaterü letet, amit II kérdéses
mutató címez, mégis újra megprób,ílunk onnan olvasni, vagy oda írni. Ez egy általános,
ugyanakkor rendkívül nehezen felderíthet6 hiba, mivel a mem6riához (örtén6 t6v(;!s
hozzáférés általában jóval II lörlési utasítás mán következik csak be.

11. Óra
llivatkozás (re/eret/ce) - Egy objektum álncve.

,
12. Ora
Hatékonyság (efficiellcy) - A programozók körében meglehet6sen - talán tiHzottan is-
népszerű téma. Általában arról van szó, hogy heroikus küzdelmek árán óriási átalakítá-
sokat végzünk egy programon azért, hogy elenyésző mértékben gyorsabbá tegyük azt.
Talán ez a legfontosabb információ, amit kezdő programozónak észben kell tartania
a dologgal kapcsolatban, persze hacsak nem va lós idejű rendszereket, orvosi műszerek
beágyazott szoftverél, vagy rakétairánytó központok vezérlókódjál fejleszti. Általában
jobb, ha a fordítóprogramra hagyjuk, hob')' a hatékonysággal tör6djön.
B függelék · Gyakran hasmált kifejezések 1527

,
13. Ora
Felszíni másolat (shallow copy) - Olyan máve\et eredménye, amely egy objektum ak-
tuális értékeit (adattagjai t) másolja ál egy másik objektumba. Szokás tagonkénti máso-
Iá.snak is hívni.

Mélységi máso/ar (de,p copy) - Olyan másolási művelet eredménye, amely nem csak
az adattagok aktuális értékét másolja ál egy új objektumba, hanem a mutatók által cím-
zeLl memóriaterületeket is.

,
14. Ora
EgyopertmdusIÍ operátor (Imal)' operato /o) - Olyan operátor, amelyegyel1en kifejezé-
sen végez valamilyen nniveletet. lIyen például az inkrementálás operátora (aH ). Ellen-
téte a kélOperandusú (binary) operátor, amelynek két dolgot kelJ megadnunk. Ilyen
például az összeadás operátora, aminek a jobb és a baloldalán is kell legyen egy
operandus (a+b).

KétoperclI1dllslí. operátor (binal)1 operatolo) - Olyan operátor, amely két dolgon képes
valamilyen műveletet végezni. Ilyen például az összeadás opeflÍlora (a+b).

Háro1lloperandusIÍ operátor (ten/alJI opcmtor) - Olyan operátor, amely hflrom objek-


tum megadását igényli. 1\ C++ nyelvben mindössze egy ilyen van, a ? operátor, amit
a következő formában keH használni:
a < ? IG AZ , HAM IS ;

Ha a megadott kifejezés értéke igaz, akkor az IGAZ ágban szereplő utasitÍls fut le, el-
lenkező esetben a HAM IS ág lépé érvénybe.

Qperand1.lsszám (arity) - Egy operátor által igényelt operandusok száma. A C++ eseté-
ben ez az érték lehet egy (unary operator), kettő (binary operator) vagy három
(rernary operator).

,
15. Ora
Tömb (array) - Azonos típusú objektumok halmaza.

Tömhindex (subscript)- Az eltolás nagysága e,gy tömbön belül. A C++-ban a myArray


tömb negyedik eleme a 3-as tömbindexen keresztül érhető el így: myArray [3 ] .

Karakter/ánc (string) - Karakterek tömbje, amelynek végét egy null karakter jelzi.
528 1VII. rész· Függelékek

16. Óra
Ell/agyo/ás (stubbing) - Az a módszer, amikor egy függvényb61 éppen csak annyit
írunk meg, hogy le lehessen fordítani a kódOl, a lényeget pedig későbbre hagyjuk.

Fellilbí,-álás (ovcrriding) - Az az cset, amikor egy származratotl osztály eltérő funkci0-


nalitással megval6sít egy olyan füg&>vényt, amelynek már az alaposzrályban is volt
megva16sítása. lIyenkor fontos, hogy a felülbírál6 függvény visszatérési típusának és
pamrnéterl istájának meg kell egyezni az eredeliével.

,
17. Ora
Virtuális lII e/ódlls (vll1l1 a/ lll etl/od) - Az egyik azon módszerek közül, amelyeken ke-
resztül :1 C+. mcgv:dósítja a többalakúságot (polymorphism). Lényege, hogy lt!11t!16vé
teszi a sz:irma;.::LatOll objektumok alaposztályba tartozó objektumként való kt!ztdését.

Vil1//álls mel6t/l/stálJla (/J-tuble) - Olyan be l ső mechanizmus, amdy lt:hetóvl! teszi


az objektumokon belüli virtuális fliggvények kezelésél.

,
18. Ora
Absztrakt admlíplls (abstract dala lype) - Angolul gyakran rövidítik ADT-nek.
Az absztrakt adatrípus inkább koncepci6t, elképzelést képvisel, nem pedig egy konkrét
dolgol. Az "alak ~ példáu l absztrakt adattipus, míg a . kör~ konkrél.

Tisztán virll/álfsjiiggvény (pl/re tlint/al fU/lction) - Olyan virtuális függvény, amil


a származtatott osztálynak kötelezően felül kell bírálnia, mivel az alaposztályban egyál-
talán nincs megvalósítása, nem tartozik hozzá kód. Az ilyen függvé ny tehát az alaposz-
tályb:1Il egy teljesen absztrakt funkciót ír le.

,
19. Ora
Láncolt lista (linked list) - Olya n adatszerkezet, amely logikailag egymáshoz kapcsolt
csom6polltokból áll.

Egyszeresenlállcolllisla (singly linked list) - Olyan láncolt lista, amelyben a csom6-


pomok csak a soron következő csomópontra tartalmaznak hivatkozást, visszafelé nem.

Kettősenlál/colt lista (t/oubly linked list)- Olyan láncolt lista, amelyben a csom6pom-
ok az 6ket követ6 és megelőző csomópontra is tartalmaznak hivatkozásl.
B függelék • Gyakran haszná~ kifejezések 1529

Fa (tn>e) - Olyan összetett adatszerkezet, amt:lyben a csomópontok kettő, vagy anná


több másik csom6pomra tartalmaznak hivatkozást.

,
20. Ora
f r iend - Kulcsszó, amely azt írja elő, hogy egy osztály hozzáférhessen egy másik osz-
t:í ly privát tagjaihoz is, ad,magokhoz és metód usokhoz egyaránt.

Sratik/ls adattag (static member data) - Olyan adaltag, amely az általános adattagoktól
eltén5en nem replikál6dik a példányosítás (objektumok létrehozása) sor.:in. Az ilyen
elemekből csak egy példány létezik, ehhez azonban valamennyi az adou osztályba tar-
toz6 objektum hozzáférhet. Az ilyen adattagol általában a rrd használják, hogy segíts6-
gével nyilvántartsák a létrehozott objektumok számát, de lerm~szctcsen tárolható ben-
ne bármilyen más olyan info rmáció is, amely valamennyi objcktumra vonatkozik.

Statikm tagft'/ggvéll)'ek (staIIc member fi/llctiolls) - Ugyanúgy, ahogya statikus adatIa-


gok, ezek is csak egy példányban léteznek, és csak az adott Osztállyai kapcsolatban
haszná lhatók, az osztály elemeivel kapcsolatban nem. Természetesen meghív:ísuknál
sem kell egy adon objektumr.t hivatkoznunk.

,
21. Ora
IIdefine - EI6feldolgoz6i direktíva, amely va lamilyen karaklerláncokkal kapcsolatos
helyeuesitési műveletet ír elO:.

Token - Speciális jelentéssel felruházott karakterlánc.

22. Óra
Vízesés úllater/all) - Olyan elj:írás, amelynél minden egyes részmtlvelet végreh .. jtásá-
nak megkezdéséhez be kell fejez6dn i az összes, őt megelőző résznllivelet végrehajtá-
sának. Valamennyi állapot diszkrét és önmaga által meghatározott. Ezt a megközelítést
nem csak szoftverfejleszlésre lehet alkalmazni, hanem számos más dologra is. (Jlyen
logika alapján történik például egy ház vagy egy autó megépítése.)

Szimuláció (simulalion) - Egy valós rendszer vagy részrendszer múködését leíró szá-
mít6gépes modell.

Koncepció/elV (col1ceptllalizalioll) - Egy szoftverfejlesztési projekt alapötlete.

Felhasználási esettanulmány (use case) - Egy rendszer fe lhasználási módjának leírása.


I
530 VII. rész • Függelékek

UMl (Ul1ificd Modeling Lallguage) ~ Szabványos grafikus ábrázolási rendszer, amely


a rendszerekkel szemben támasztott igények leírásában, illetve a tervezésben segít.

Problématér (problem space) - Azoknak a Feladatoknak és problémáknak a halmaza,


amelyeket egy programmal meg kívánunk oldani.

Megoldiistér (solu tion space) - Egy probléma lehetséges megoldásainak halmaza.

Mcghajtóprogram (d/ivel· program)- A tesztprogram másik megnevezése.

23. Óra
Sob/Oll ((emp/ale) - Olyan eszköz, amivel általános met6duso kat vagy osztályokat hoz-
hatunk I~trc. Ezekb61 az általános leírásokból aztán egy tírusnak paraméterként törté-
n6 megadásáv., 1 {llIíthatunk el6 konkrét oszt:llyokat vagy me16dusokat.

Példtmyosilás (fllstantialion)- Az a mOvelel amellyel egy o.'iztály a lapján egy objekm-


mot, vagy egy sablon alapján egy lípust (oszlályt) hozunk lélre.

,
24. Ora
KitXJt(J! (e:;(CepUO'l) - Olyan objektum, amit probléma esclén az egyik kódrészlet a má-
siknak átad, A kűld6 a problémás k6drerület, :l fogadó pedig az a kód, amely a problé-
mát kezelni képes.
TÁRGYMUTATÓ
532 1Tárgymutató

#414 ASCII 41
lf1f 414 assertO 416, 502
#defi ne 50, 99, 404 azonosíL6nevek 499
#else 405
#endif 405
#ifder 405
#jfndef 405
B
#indude 377 balérték 59
~ndef 409 !r.uáli viszonyban 381
.c9 BASIC 20
.cbx 7 báziskonslruktor 300
.cp 9 bázismet6dus meghívása 309
.epp 7, 9 bázisosztály 293
.cxx 9 bázisosztály inicializáci6j:1 304
_DATE_415 bázisosztály metódusainak eJfedése
_FILE_415 307
_LlNE_ 415 BCC32 11
_TIME_4 15 beágyazás 22
-- 257 beépítet! makró 416
8O/ 80-as szabály 451 beszúrás 407
binary 251
Borland C++BuilderX 5
A,Á break 111

adattag 133
adattípus 38 C, Cs
address of 164
alapértelmezett é rtékek 234 , 235 C++ 20
alapértelmezett konstruktor 141 case 124
alias 196 casting down 329
alkalmazásfejleszl6i interfész 452 catch blokk 486, 490
á!landó 49 célobjekrum 196
ANSI 4 char 39
ANSI szabvány 25 ci klus 107
ANSI/ISO CH 4 cím 164
API 452 cím szerinti paraméterátadás 214
Application Programming Interface class invariant 420
452 COBÜL 20
a rgumenmm 32 compiler 5, 20
argumentumok 90 concatenation operator 414
a rity 254 const 50, 502
Tárgymutató 1533

continue III érte[mez6k 20


cOUl45 érvényességi kör 88
csomópont 354 escape karakter 165
csökke ntés 60 eseményhurok 441

D F
DEHUG 409 fa 354
deep copy 238 fájlfo rmálumok 417
default 125 fcj[(:cállomány 150
definíció 81 fejlécállományok 25
dekrementálás 60 fejlesztési ciklus 435
<lelete 177 felel6sség átru házása 355
dereference 169 fe lsorolt .'illand6k 51
destruktor 141 fe lszíni másolás 238
destruktorok 298 fe lillbírálat 305
dinamikus kötés 318 [elillet 148
dinamikus mem6ria 174 fe lülírás 305
do ... while lvi float 41
double 47 for 117
fordílóprogram 5
fordil6programok 20
E, É free 180
friend 381
egyenl6ség operátor 257 futásidejú kötb; 318
egymásba ágyazott ciklusok 123 futóindex 265
egyoperandusú oper.ítor 251 függvény 30, 79
egységbezárás 133, 382 függvény argumentumai 89
egységes modellez6 nyelv 437 függvény definíci6ja 83
egyszercsen láncolt lista 354 filggvény prototípusa 81
e l6feldolgozási direktív{lk 25 függvény túlterhelése 97
e lőfeldolgozó 25, 403 függvénydeklaráció 81,82
e l őjeles egész 48 függvények többalakúsága 231
el6tag 61 rüggvénymutatók 382, 389
else ág 67 függvénymutatók tömbje 386
elvont adanípusok 341 füg&ovényparamérer 32
encapsulation 22, 133,465 ruggvénytúlterhelés 231
endl45
enum 51
érték szerinti paraméteráwdás 213
534 1Táryymutlrtó

G, Gy interfész 150
intcrpereter 20
InvariantsO 424
gazdátlan mUlató 190 iostream 45
globális névtér 175 ISO 4
golyóálló 484 iteráció 107
goto 107
gyökeres struktúm 447
J
H játék 110
jobbérték 59
h:lsználaLi esetdiagmll1 437
heap 174
helyi változók 86 K
hilxlkeresés 149
hivatkozás 196 karakterlánc-osztályok 286
hivatkozások 221 karaktertömbök 282
hosszú serok 498 kategória 131
hozzMérés 501 kerílésoszlop hibák 268
hozzáférés-szabályozó m6dosí16szó kétoper-d.ndusó operátor 251
296 kétszeresen láncolt lista 354
hOZ7..árendelési vagy értékadó kezd6énék 44
operátor 57 kifejezés SS, 57
kivétel 177
kivételek 485, 486
I kivérelek e lkapás:1 491
kivételkezelés 485
IDE5 kóbor mutató 190
időzített bomba 190 k6cltér 175
if utasítás 66 koncepcióterv 437
IilNK3211 konstans mutatók 191
implementáció 149 konstans lllutat6val 217
indude 45 konstans tagfüggvény 147
indusion guards 408 konstruktor 140
indireclion 168 konstruktorok 236, 298
inherilance 22 konstruktorok rlllterhelése 236
inkrementálás 60 konzolos alkalma:.:ás 5
inliJle f'i\ggvény 99 közvetett elérés 168
intO operátor 260
intelligens fordítóprogramok 223
Tárgymutat6 1535

mutatók tömbje 278


L mutal6lömbök 276

láncolt lista 353


leak 176 N, Ny
lecsupaszítás 293
linker S na hrybclűk 499
lista farka 354 nevesített állandó 49
lislafej 354 névtér 113
literdl 49 new 176
lógó mulató 190 növelés 60
lokális változók 136 NULL 167
long 39 null pointer 167
jwva lue 59 nullmuta16 167
nyelvi állandó 49
nyomkövetés 418
M nyomkövetés! szinte k 426

makr6fOggvény 410 ""


makrók 4]5 0,0
makr6k mellékhatásai 418
maJloc 180 objecl code 20
másol6 konstluktor 237 objektum-központú paradigm:l 22
másoló met6dus 323 objektumok iniciali7..álása 237
MAX 410 objektumtömbök 270
megjegyzés 28 operandus 57
megjegyzések 500 operátor 5$, 164
megoldástér 438 operátor operandusainak szám:. 254
megval6sítás 149 operátor túlterhelés 245, 247
mellékhatás 419 operator+ 251, 252, 380
mélységi másolás 238 operator- 254
memóriacím 164 osztály kezel6felülete 150
mem6riafoglalás 31:1 oszt:'ilydefi níd6k 501
memóriaszivárgás 176 osztályinvariánsok 420
mctódus 133 oszrálymetódus megval6sítása 150
MIN 410 osztályok 131
nmemonikok 20 overloading 97
modulo 59 örökl6dés 22, 291. 329
modulus 59 összefúz6 operátor 414
mulató 183 összetett utasítás 56
mutatók 163
53s 1Tárgymutató

p S, Sz
paraméterlisla 81 sablon defi níci6ja 466
paraméternek alapénelmezett értéke sablon példányosítása 466
95 sablonok 465
paraméle r-szignaníra 305 sablonosztályok 466
Pareto szabálya 452 sablontípus 474
példányosítás 135 s hallow copy 238
polimorfizmus 22 signed 40
polymorphism 22 sizeofO 40
postfIX 61 slicing 320
postfix inkrcme l'tál6 operátor 248 spagetti kód 108
postfix operátor 248 stack 101, 175, 276
precedencia 63 Standard Template Library 480
prefIX 61 stalikus 370
prefIx operátor 246 statikus tagfüggvények 372
preprocessor 25 statikus tagváltozók 369
preprocessor directive 25 STL 480
private 296 su cpyO 284
problém atér 438 stringizing aper.hor 414
procedurális 21 strncpyO 284
program szövege 498 subscript 265
programblokk 88 .switch 124
projekt 6 switch utasítások 498
protected 296 symbolic 49
prototípus 8 1 szabványos sabJonkönyvtár 480
public 296 származtatás 292, 293
szeletelés 320
szignatúra 32
R szimuláció 436
szöveggé ala kító operátor 414
RAM 38
Random Acces Memory 38
referencia 174, 195
regiszter 175
T, Ty
reláci6s operátorok 65 tagfüggvény 133
rövidített írásmód 386 tagmetódus 138
r-value 59 tagok 132
tagonkénti másolás 238
tagváJtazó 133
Tárgymutató , 537

tdrgykóOu jlh)Jll~lll\ 20
tartdlm,lzntt ')szt~lly 5)O\()
[clt:pítés (J
v
[hb JRR vad tllU1Jtó 1(1-
llli~ mUIJtó 5--i változú ~-
típu,:, 1.'\1 ":lltm:Ú értéke .~~
tíPllSdh:ís 329 dlto:t:ú lipus::l :.R
többajakúsig 22, ~I-f. .~.!') "ár.1I1an e~emény ... k -f}O\:;
többclimenziós tömbök T2 "égtehm ciklus 113
többclimenzi 6~ tömbök dekbr.1cióFl "életlen elérésű memóna _\8
273 \crem 101, l-5. 2-6
tömb 265 virtuális destruktorok 322
tö m b mut~Hój;l 278 viml:'ilis függvények 313, 314, 329,
tömbök elemei 266 342
tömbök c1<1készífése 269 virtuális másoló konstruktorok 322
lömbök lürll:~(: lSl visszahiv:llkozá.s 169
lömbök túHdsa 268 vil>Szatérési é rtékek 26
If)' blokk 4H6, 490 visszatérési típus 92
Iypeclel' 4'5, :W I vízesés modell 435
.. v-mutató 318

U,U v-tábMzat 318


v-t ábl~zat köllségei 326

UML 437
unaf)' 251
IlnH'cd Modeling L1nguage 437
w
un.'>igncd 40 ,,";II·nin.~ II

ula~ít;í.'> 55 whik' 101{


utols(mak be eJs6nek kin 03sl-in- whih· .... lxIL'L· <;6
tlrsl-out) elv 101 \\ dd puintL'f 167
utó tag 61

You might also like