Professional Documents
Culture Documents
é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
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
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_~
. . 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 .. 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
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 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.
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
- 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.
-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.
-/)cwid B. Horoath
Tanuljuk meg a C++ programozási nyelvet 24 óra alatt xviiI
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.
Email: feedback@samspublishing.com
Maii: Michael Stephens
Associate Publisher
Sams Publishing
800 East 96th Street
Indianapolis, IN 46240 USA
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.
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.
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.
A CD tartalma
A könyvhöz mellékelt CD-n a következőket laláljuk:
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.
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ó.
_-\ 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 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.
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:
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)
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 •
-
-- ",
~.:.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.
A fordft6program és a szövegszerkesztó
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ó.
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.
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
,.
2 , int main()
,.
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á.
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!
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
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.
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ú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.
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.
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
Feladatok
Válaszok a kvfzkérdésekre
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.
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
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:
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.
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.
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
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.
Hello World!
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.
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
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.
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.
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.
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
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
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.
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
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)
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.
monat
A main () fü ggv ény b e n v agyun k!
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.
Kérdések és válaszok
Kérdés: Mi a szerepe az #include utasítéisllak?
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.
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
Válaszok a kvlzkérdésekre
Változók és állandók
. . lisz szó ebben az órában:
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.
:
:~~~-'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.
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
Á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.
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 .
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
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
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
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 ;
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ó.
[ 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
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.
Ö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 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 : 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 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.
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
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
ICiIllll1ll
Szélessóg : 5
Hosszúsúg : 10
Terület : 50
-
men kívül hagyhatjuk.
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.
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.
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.
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()
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
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
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.
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 ) ;
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.
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.
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.
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
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.
temp'" a ;
a b-
b " temp;
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
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
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
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.
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.
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.
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.
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
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++).
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.
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
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.
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.
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
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ő.
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- _ _-
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.
Az else ág
Feltételvizsgálatkor sokszor használunk az igaz ág mellett egy hamis ágat is.
,.
3: int main()
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.
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
Összetettebb if konstrukciók
else
utasitás4 ;
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.
-
33 :
• 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.
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
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 .
A 4.6 Listában javítotluk a hibát a megfele16 helyeken dheJyezell kapcsos zár6 jelekkel.
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
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.
4.2 T6bIúat-LotIlkII ~
Logikai ~S
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.
Logikai NEM
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
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
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.
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;
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?
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 )
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 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.
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.
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
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
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
•.2. ábra
q:yfriggvimydeklanici6 részei
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~:
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 ;
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
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
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 .
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
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.
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.
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á-
""""'
=;..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 :
..... 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.
~ : 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.
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 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.
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):
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.
-
26 :
•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.
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:
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 ;
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.
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
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.
•
96 11. rész· Bevezetés a C++ programozási nyelvbe
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 :
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) ;
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
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) ;
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.
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.
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.
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 :
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
A függvények és a verem
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.
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
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ő:
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
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.
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
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 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
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.
,.
2: ~includc <iostream>
4: int main ()
5: {
6. unsigned short small ;
7. unsignod loog large ;
,.
8. const unsigoed short MAXSMAIJIJ",65535 ;
•
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 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.
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.
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 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.
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.
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
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:
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.
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.
Kimenet
Há ny hello legyen? 2
Hello
Hello
fo. s zámláló á l lása : O
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 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: 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 :
menet
POrg:Ok! POrgO k! POrgOk l POrgOk ! PO r gOk !
A szám1616 : 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.
Kimenet
i : O j: O
i : 1 j: 1
i: 2 j: 2
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 .
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.
,,
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!
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
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.
-
10:
L o
"L 1
2
3
"
l , 4
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
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.
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.
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;
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
É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 .
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
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.
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
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.
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.
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
Ú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).
• Amemóriaigénye
• Milyen jel1egll információt tárolhanmk benne
• Milyen mavclctcket végezhetünk rajta
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
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.
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.
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.
Friaky .i tsWeight - 50 ;
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.
public :
unsigned int
Iln!ligncd int
Meow() ;
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
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
Osztálymetódusok létrehozása
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.
Meow.
Fri s ky is a cat who is 5 years old .
Meow.
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
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 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.
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;
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.
](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 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<
Kérdések és válaszok
Kérdés: MilyeJ/ Hagy egy osztálypéldány?
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.
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ő:
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:
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.
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.
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·
Ú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) ;
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.
"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 , };
m
Mcow .
Frisky egy 5 éves ma c9ka .
Mcow.
Frisky most már 7 éves .
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).
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.
,.
1: lincludc <iostrcam>
,.
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 ;
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 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 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 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?
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
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.
Á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.
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
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 :
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!.
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'
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
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.
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
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 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 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. ~
int theVariable : 5;
int * pPointer = &theVariable
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
,.
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.
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
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.)
• Glob{ilLo; névtér
• A dimmikus memória (heap)
• Regiszterek
• Kódlér
• A verem
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 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.
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;
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.
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.
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
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
localVariable: 5
*pLocal: 5
*pHe ap: 7
*pHcap: 9
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 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?
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
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
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.
Kimenet
SimpleCa t Frisky . . .
Constructor called .
SimpleCat * pRags = new simpleCat ...
Constructor called.
de1ete pRags ...
Dcstructor ca1led.
Exiting, watch Fdl;ky go .
Destructor ca1led.
(·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.
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.
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 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 .
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.
Á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ó.
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 :
-
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.
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.
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 .
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.
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 :
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ó.
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.
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?
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
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.
intOne: 5
rSomeRef : 5
intOne: 7
rSomeRef : 7
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.
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 :
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.
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.
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.
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.
,.
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.
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.
,,
8: int x =5,y=10;
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.
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.
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.
...
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
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.
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.
,.
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)
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 :
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 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:
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-
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).
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.
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
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.
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
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 12.4 Lista rámutat, milyen veszélyes is lehet egy már nem létező objektum hivatkozá-
sának II visszaadása.
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
(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)
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
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.
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.
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
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?
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?
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.
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.
menet
OrawShape(O,O , true) ...
~ ••••••••••• • * •••• ** •••• *** •••
• _•••••••• * •••••••••••••••••••
••••••••• •••••••••••••••••••••
DrawShape(40, 2) ...
~ •• •••• • * ••••••••• *.* •••• ** •••• * •••••••••••••••• o • • • • • *. 0 •• *
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.
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:
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.
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 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.
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.
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
,---"":'::A,,,,,-...Jf-' I\sAge
13.1 ábra
Az a/t/{Jértc/mczell m{/so/ó kol/slmklol" hasz1lálara
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.
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 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
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?
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.
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:
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 •
value
The
The value
value
of i
of i
of i
"" O
1
"' "
The 2
The value of 3 and i : 3
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.
. "' .
46 : Counter a ~ ++i;
47:
48:
I:Itd :: cout «
std : : cout « ."The value of
and i:
« a . GetItsVa,l();
« i .GetItsVal () « std : : endl;
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.
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.
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;
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.
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
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.
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
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.
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.
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.
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.
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 :
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.
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 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.
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.
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
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 ;
az eredmény pontosan ugyanaz lesz, mim az imént, amikor explicit módon megadtuk,
hogy öt elemb61 álljon a tömb.
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.
,,
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
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.
."
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-
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.
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.
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.
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 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
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.
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 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.
Kimenet
Cat U: 1
Cot 1/2 : 3
Cot B: 5
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.
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.
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
• 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.
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 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.
Az alapértelmezett "bemenet vége jel az újsor. A módosított kód a 15.7. Listában látható.
menet
Enter the sLri ng : Hello World
Here ' s the buffer : Hello Wo r l d
) .
2 : #inc l ude <string h>
4: int main ()
5: {
6. chnr Stringll] = "No man is an island";
,.
7: char String2lBOJ ;
JGmanot
Stringl : No man iy an island
String2 : No man is a n island
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 :
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.
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.
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.
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
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.
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.
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.
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 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 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
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.
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: *include <iostream>
,.
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
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.
65 : {
66 : std :: cout« 'Oog destructor . .. \n ';
67 : }
68 :
I
300 V. rész • Önl~6dés és többalakúság
...-.-
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 \.~."""-"._ . '_ _
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
-
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":
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 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).
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.
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 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
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.
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
Kimenet
Mammal move one stcp
MaJnlUal move 2 Gt ops .
Dog move 5 s l epl:l .
Be s
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
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.
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.
H81y81 H8lytelen
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ó:
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.
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:
A 17."1 Lista szem l ~ lteti II vimlális függvényck által megva l6sítható többalakúságot.
,,
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 :
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.
mazOllrd hivatkozna, akkor is mind-mind sorban a neki szánt függvényt hívná meg.
A 17.2 Lista ezt mUlalja be.
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
, -----,,,
~
Em", "" I--_E_m_"_,_-.j
Kutya objektum
Kutya
17.1 i b",
KI/f)vl objektulII (/ létrel/OzáStI utáll
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.
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
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 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.
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 .
-
1 : (l)dog (2)cat (3)Mammal: 1
2 : Hammal constructor .
3 : Dog Constructor . ,
17. 6ra· A és a szánnaztBtott
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 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.
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
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?
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?
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ó:
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.
,.
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
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
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.
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 :
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.
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.
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:
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.
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:
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.
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 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.
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 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
Kérdések és válaszok
Kérdés: Miko,. beszéhi.nk a ,, 1I117k6désjelszivárogfCIttisár61''l
"á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.
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
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.
19.1 ábra
LlÍllcolt listák
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.
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.
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() {}
58 : class Node
59 : (
60 : public :
61 : Node () ()
62 : virtual -Node () ()
6] : virtual Node" Inscrt(Data .. theData) ~ O ;
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
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
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 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).
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 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.
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).
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).
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.
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é.
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).
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.
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.
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
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.
] 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
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.
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 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 .
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.
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.
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
Kimenet
EI. 8
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
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 :
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 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 GetFirstName () cgy konstans $tring-et ad vissza, amire nem lehet az operator ...
műveletet meghívni.
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.
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á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.
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.
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 ,)
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 .
Kimenet
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).
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)
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 :
menet
Elemzés
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.
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:
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!.
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
•
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 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).
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.
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.
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 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.
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.
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;
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.
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.
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
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
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 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.
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
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.
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
5 + (7 .. 5) -I- (7 .. 5) + 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.
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.
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.
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.
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 )
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.
menet
First assert :
Second assert :
ERROR !! Assert xl .. 5 flIIiled
on line 24
in file E:\adisk\cppin24 new\Hour21\simpleassert . cpp
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 ..
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
"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.
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
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.
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
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 •
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ó
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
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.
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ó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?~
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.
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.
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?
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.
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.
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.
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.
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.
• 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.
Ü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.
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.
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.
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.
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
I
pMeuage
22.1 ábra
A l...>c:zdcli 6r6k1ődési hierarchia
22. óra • Objektum-orientált elemzés ás tervezés 449
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.
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;
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.
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.
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.
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 •
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.
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
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.
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 .
,.
8: class String
10 : public :
11 : 1/ konstruktorok
12: String();
22, 6ra • Objektum-orientált elemzés és tervezés 1457
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.
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 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?
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.
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?
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
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.
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 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.
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 •
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:
Mindenekelőtt, vegyük észre, mennyire hason lít a 23.1 Lista a 19. órán bemutatOllhoz.
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
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!
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
E, a macska 2 évec
E, a macska 6 éves
Ez ,
macska 12 éves
E, a macska 14 é ves
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
Kérdések és válaszok
Kerdés: Mié11 használjunk sablonl, amikor makróval is mego/dhatunk egyjeladatot?
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
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 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:
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.
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ű:
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
)
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 .
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.
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.
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.
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.
,,
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~____~.,
-
158 :
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 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.
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.
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:
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 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:
Azonosrt6nevek
Néhány ötlet az azonosít6nevek célszení használatár61:
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.
Hozzáférés
Érdemes következetesen kezelni .t progmm e~>'Ycs részeihez tartoz6 hov..áfér&si szinteket.
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.
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.
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.
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:
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.
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.
j
I
510 VII. rész· Függelékek
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:
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 /
** ** .. * *** ..
*****
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".
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
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.
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
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.
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.
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
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
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.
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:
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.
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
B FÜGGELÉK
,
,. 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.
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).
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á.
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,
,
3. Ora
Vciltozó (va ritibie) - Névvel ellátott hely a me móriában , amelyben egy é rtékel tíÍrol-
hatunk.
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
É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.)
Á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ó.
,
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.
Ö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.
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
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.
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.
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.
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,
,
7. Ora
Kliellsck (eliel/IS) - Olya n osztályok vagy függvények, amelyek az álmlunk fejlesztett
osztályt használjiik,
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á.
,
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.
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á.
,
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).
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.
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.
,
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.
,
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.
,
19. Ora
Láncolt lista (linked list) - Olya n adatszerkezet, amely logikailag egymáshoz kapcsolt
csom6polltokból áll.
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
,
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.
,
21. Ora
IIdefine - EI6feldolgoz6i direktíva, amely va lamilyen karaklerláncokkal kapcsolatos
helyeuesitési műveletet ír elO:.
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.
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.
,
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
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
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
UML 437
unaf)' 251
IlnH'cd Modeling L1nguage 437
w
un.'>igncd 40 ,,";II·nin.~ II