136 32 76MB
Hungarian Pages [542] Year 2008
Az operátorok precedenciája és asszociativitása
ACésaC+ + kulcsszavai
A táblázatban elóbb szere p lő operátorok magasabb precedenciával rendelkeznek, mint a kéKulcsszavak 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' (&&) 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,,' (&,, ) rend szerint folytatja. tv. egyoperandusú plusz (+) és minusz (-) a precedencia-lista maso- .,m dik szintjén szerepelnek. vagyis - teljesen logikusan - mege l őzik az aritmetikai megfele l ői a u to sőbb
ket, amelyek az ölödik szinten kaptak helyet. A második szinten található & operátor a "címe" operátor, míg a g, szinten látható ugyanilyen jel a bitenkénti AND művelet operátora, 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 utalnak zárójelek, úgy az azonos precedenciaszinthez tartozó műveleteket a program jobbról balra. vagy balról jobbra haladva hajtja végre a táblázatban megadottnak megfelelöen, Szint 1 (magas)
2 3
'0"
static
friend'
statiC, cast'
goto
struct switch
bool'
"in l ine'
break
'ne
template ' this '
case
long
throw'
mut a ble'
true '
Kiértékelési sorrend
( ) ::
balról jobbra balról jobbra
char
names pace
try'
class'
new'
typedef
campl' (-)
not '
const
not,eq' ( I,,)
typename '
const, cast '
operator'
union
continue
or' (I I )
unsigned
default
or, eq' (I ,, )
us!n g '
delete '
private'
virtual'
do
protected'
void
· [ l
-> ++ --
type i d keyword, typecast * & ! - + ... -- ... aizeof new delete
)
«
17 18 (alacsony)
bitor' ( I)
size, t
Operátorok
,-
16
(&)
s izeo f
float
catch'
4 5 6
8 9 10 11 12 13 14 15
bitand'
false'
jobbról balra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra jobbról balra jobbról balra
· * ->* • /% »
< >= ='" I ",
••
••
"
?,
= * = /= ... = -= %= «=»;&=A=I= throw , (vessző operator)
balról jobbra balról iobbra
(l)
public' double dynarnic, caet' re gister
type id'
volatUe' wch ar, t
else
reinter -
while
on=
pret, cast'
xor '
explidt'
return
xor, e q'
export'
short
extern
eigned
TIpus
16 bit
32 bit
Tartománv
uneigned short int short int uneiqned long int long int int
2 2 4 4 2
2 2 4 4 4
unsigned int s i ze_ t char wchar_t bool float double l ong d ouble
2 bájtoe 2 bájtoe 1 bájtoe 2 bájtoe l bájtos 4 báj t os 8 bájtos 10 bájt o s
O-tól 65 535-ig -32 768-t6l32 767-ig O-tól 4294967 295-ig -2147483 648-t612147 483647-ig (16) :-32768 - tóI32767 - ig; (32) :-2147483 6 4 8 - tól 2147483 647 - ig (16) : O- tó165 535 - ig; (32) : Q-tól 4 294 967295-ig (16) : 0 - tó165 535 - ig; (32) : Q- tól 4 29 4 9672 9 5 - ig 256 karak ter 65535 karakter Tr u e va gy False 1, 2*1 0·>1 tó13,4"10 " ig 2, 2 *10 -"'- tól 1, 8 * 10'''- i g 3 , 4 *10 ·"" -t6l l, l *10"" -ig
bájtos bájtos bájtos bájtos bájtos
4 bájtos 4 bájtos l bájtos 2 bájtos 1 bá j tos 4 b ájtoe 8 b ájtoe 10 bájtos
(A;; )
, Csak a C++ nyelvnek része, Azok a kulcsszavak, amelyek után zárójel szerepel, a zárójelben található operátor szinonimái.
Adattípusok a C+ + nyelvben bájtos bájtos bájtos bájtos bájtoe
(A)
TARTALOMJEGYZÉK I. rész
Bevezetés a C+ + programozási nyelvbe
l. óra
Kezdő
lépések
E!őkészülctck a programozáshoz ....... . A C++ nyelvjárásai (C++, ANSI C++, ISO C++ és a Windows) A fordító telepítése és beállítása Telepítés a CD-r61 . , ................ . A Borland C++BuilderX fordító beállítása ...... .
Fordít.'Ís a Borland C++BuilderX-szel
A fordítóprog ram és a szövegszerkesztő
Fordítás és linkelés Fordítás az integrált fejlesztői környezetben
linkelés (ősszeszerkeszl(:s) A fejlesztési ciklus. , .. HELLO.CPP -
Az els6 C++ programunk
Fordítási hibák ...... . Kérdések és válaszok
Gyakorlatok
Kvíz Feladalok Válaszok a kvízkérdésekre .
2. óra Mitő!
· 3 .4
.5 . ..... 6 · .. 6 ..... 8
..... 9 ......... 10
.10 .......... . .......... 11 . ..... II
· 13
15 16 16 17 17 17
Egy C++ program részei
lehet jó választás a C++ nye lv. · . 19 Procedurális, strukturált és objektum-központú programfejlesztés . · 21 A C++ rwelv és az objekmm-központú programozás . 22 Beágyazás (encapsulation) . . . . . . . . . . . . . . . . . . . .. . ......... .. .... 23 Öröklődés és újrahasznosítás .................... . . .... 2.~ Többalakúság (polimorfizmus) .... . .. . . ..• . . . .. · . 24 Egy egyszeru program részei .......... . · . 24 Az #indudc utasítás viz.vényekre vonatkozó mutatók . . . . . . . . . . . . . . . . . . . . . .. 394 Tagfüggvényekre vonatkozó mutatókhól álló tömbök ....... . ..... 397 Kérdések és válaszok .. ............... . ........ 400 Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . .. . .. 400 Kvíz. . . . . . . . . . . ....... . ........•............ . ......... 400 Feladatok ....... ... ... ..•.. .•.. . . •... . ...... 401 Válaszok a kvízkérdésekre . . ..................... . ......... 401
21. óra
Az előfeldolgozó
Az előreldolgozó és a fordító ......... •.. .......... . ................ 403 . . . . . . . . . . . . . . . . . • . . . . . . . . . . . . . . 404 .......................... . .... 404 A . define használata álland6kkal kapcsolatban . . . . . . . . . . . • . . . . . . .. 405 A -*define és az '#ifdcf használata vizsgálatok végzésére. . ........ 405 Az #else direktíva .............................. ..... .... 406 Beszúrás és annak vezérlése .. . . . . . . . . . ........... • .......... 407 Szimbólumok definiálása a parancssorban .......... . .... . .......... 109 A meghat~irozotts{lg rnegszüntetésc .. . . . ................ 409 Feltételes fordítás ......................... . ... . .... . . . ........ 409 Makróként megvalósított fü~>vényck ........ . . . ........ 410 Mi ez a rengeteg zárójel? ..... .•.......... 41 1 Makrók, függvények, sablonok .. . .......... 413 . ............... 414 Karakterláncok kezelése .......... . A szöveggé ala kító (slringizing) operátor. . . . . . .• . ..• . . . . . . . . .414 Az összefúző operátor. . . . . . . . . . . . . . . . . . . . . . . . ... 414 Előre meghalározoa makrók . . . . .. . . . . . . . .. . . . . . .. . .. . .. 415 Az aS5ertO beépített makró . . . . . . .. .......•.... . ..... . .116 Nyomkövetés az assertO makr6 segítségével .418 A makrók mellékhatásai ....... . . ................ 418 Osztályinvariánsok ............ . . ... 420 Köztes értékek kiíratása ...... . . ... ... 425 Nyomkövetési szintek .. ... .... .. ... •. •... . . .. •. . . . . . ... 426 Kérdések és válaszok ...................... .432 Fe1adatok . .... .. ......... ... . . ..... . ...... . .... . . 4.l3 A köztes állapot mentése . A #de nne direktíva használata.
I
Tanuljuk meg a C+ + programozási nyelvet 24 6ra alatt xiii Kvíz , ...... . Gyakorlatok . , ...... . Válaszok a kvízkérdésekre ..
22. óra
........ 433 .. ... .... ..433 . ............ ........... . ........ 434
Objektum-orientált elemzés és tervezés
A fejlesztési ciklus . . . . . . . . . . . ........ , . , 435 E~,')' riaszt6rendszer szimulációja ... ..... . ............ , . . . . , 436 Konce[Ki6lelV ..........• ... •... .. •..• . .. ........ 437 Elemzés és az igények felmérése ........ • ......•..... . ........... 437 Magas és alacsony szinnl teJVezés ....... ••.• .... .. ... •. . .•.. • .... 438 Egyéb objektumok . . . . . . . . . . . . . .•. .. ..•... . . .. 439 Milyen osztály.link lesznek? . . . . . . . . . ............... 4,,9 Hogyan jelezzük a ri:lsztásokat? ........•. , . 440 Eseményhurkok ............ . 441 PostMaster: egyesettanulmány . . . . . . . . . . . .... . . • ' , 443 Mérj kétszer, vágj eb'Yszer . ......... .. ... .. ... .•. .. , ... 444 Oszd meg és uralkodj. . ... ... .. . ..•..•.. , ... 444 Üzenetform;'itum ................. . ........ . , .. , ...... 445 Az osztályok kezdeti telVC ....... . .•...... .. . . , ...... 446 Egy vagy több kiindulópontú hier'dfchia .. . ...•..... . .......... 447 Interfészek tervezése .................. . . . ... ...• , .•. . . ....... 449 Prototípus létrehoz.1S:l ................. ... ... ..•.. . .. 450 A 8O/80-as szabály .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . . . . . . . 451 A PostMasrerMessagc os;t.t{ily tervezése ......... . . 452 Az alkahnazásfejle.~;t.tGi interfész. . ..... . ..... . , . .. , .... 452 Progmmozás nagy csoportokban ......... , .... , . • . . . . . . ... 454 A tervezési folyamauaJ kapcsolatos szempontok. . . . . . . • . . . . . . ....... 454 TelVezési döntések. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .... 455 Mikor hozzunk döntéseket? ..... . . . . . . . . . . . . . . . • . . . . . . . 455 . . 456 Munka a vezé rl őprogramokkal . . . . . . . . . . . • . . . • . . • . . . . . • . . . . Kérdések és válaszok .... . •......•..... • .. , . . . 463 Gyakorlatok. ........ . . . . . . . . . . . . . . •. . . . . ..... 463 Kvíz .. . .... , . . . . . . . . . . . . ...•.. , . . . .. ...... 463 Feladatok . . . .. ........ ... .. ..•.. ....... 464 Válaszok a kvízkérdésekre ..... ....• . .. .. . .. , . . . ....... 464
23. óra
Sablonok
Mik azok a sablonok? ........ . . . .. . ... .. .. . .. 465 A sablon példányosítása ....... . ... .. . • ' . . .. . ..... , 466 A sablon definíciója . . . . . . . . . . . . .. . ...•... , . . ........ , 466 .......... . ..... .. . • ' . . . . . . . , 474 Sablontípus használata A szabványos sablonkönyvtár .......... ... ..•. , 480
xiv ITanuljuk meg a C++ programozási nyelvet 24 6ra alatt Kérdések és vála.o;zok
Gyakorlatok ..... . Kvíz . . ...... ........ .
Feladatok Válaszok a kvízkérdésekre
24. óra
.... . .... .. ......... . 481 .. .. .. .. .. .. .. 481 .. .. 481 ..... . . ... 482 ..... " ..... ".482
Kivételek, hibakezelés és néhány tanács
Programhibák, tévesztések, k6dmegromlás . . . . . . . . . . . . . . . . . . . ... 483 Váratlan események kezelése ... ... .. ..•.... .. ... .. . . 485 Kivételek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . • . . . . . 485 Hogyan val6sitsuk meg a kivélelkezelést? . . . . . . • . . . . . 486 ..... 491 A try és catch blokkok használata A kivételek elkapása ................ . . . . . . . . . . . . . . . .. . .. 491 Több catch megadása. . . . . . . . . . . . . . 492 Hivatkozás elkapása - többrétúség a kivélelkezelésben ....•. . . •.... .. 492 Hogyan írjunk professzionális minőségű programo kat? . . . 496 Zár6jelek ......................... . . .• . . .•...... 497 Hosszú sorok . . ........ . . .. .. . . • .......... 498 A switch utasítások. . . . . . . . . . . . . •. . .. . . . . •.... . . 498 A program szövege ................. .. 498 Azonosít6nevek . . . . . . . . . . . . . . . . .. . ....• . .. .. . . . . •...... 499 A nevek betO"zése és a nagybetuk kérdése . . . . . . . . . . . . . . . . 499 Megjegyzések ... .. ... .... ..•...•. . 500 Hozzáférés ....... ................ . 501 Osztálydefiníd6k .......... .•.. ... ... ... . 501 Beemelend6 állomá nyok .. ..• .. . .. •.... . ... • .. . ... . . •. ...... 501 assenO . . . . . . . . . . . . . . .................... . . 502 const ... . .. . ... . ........ . . . . . . . . . .. . . . • . .. . . . 502 További források . . . . . . . . . . . ... ... ... .. ... .... .. .. . .. . . . 502 Hol kapunk segítséget és tanácsokat . . . . . . . • . . . . . • . . . . . . .. 502 Ajánlott olvasmányok ....... ... ... .•. ... . .... . 503 Kérdések és válaszok ....... . . . . . . 504 Gyakorlatok. . ........ 504 Kvíz. . . . ..•... . . . ..... 504 Fe1adatok ... ... ... ... ..•.. . . ....... 504 Válaszok a kvízkérdésekre . ... ... ... ...•. ....•. . . .. 504
VII. rész A függelék B függe lék
Tárgymutató .
Függelékek A bináris és a hexadecimális számrendszerek ..... . .. . ...... 507 Gyakran használt kifejezések. . . 519
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égnek, amely egyedi programok fejlesztésével , tanácsadással és oktalással foglalkozik. David B Horvath eCCp)
vezető
tanácsadó Philadelphiában, Pennsylvaniában éL Több
mint 15 éve dolgozik az informatikában illetve
részmunkaidőS
egyetemi tanár néhány
helyi ilIerve távoktatással m(íköd6 egyetemen. Az áilala oktatott tá rgY'd.k el sősorban a C++ nyelvvel, a Unix/Linux operációs rendszerrel, valamint adatbázisokklll kapcsolatosak. Diplomáját szervezeti dinamikából szerezte a University of Pennsylvanián 1998ban. Több nemzetközi szemináriumot és szakmai összejövetelt vezetett már SZgy dedikált példányt, de ellltíz6doll a projekt. - David B. HOIVa/h
Köszönetnyilvánftás Minden könyv kivál6 alkalmal teremt arm, hogy köszönetet mondjunk benne azoknak, akik nélkül garantáltan nem jÖll volna létre. Esetünkben a Staccy, Robin és Rachel Liberty állnak az élen. Ezen kívül szeretnék köszönetet mondani mindazoknak, akik a különböző kiadókSams, Que, O'Reilly, \'('rox - munkatársaiként segítenek abban, hogy eddigi ffitlveim megjelenhessenek. A Sa ms Kiadó szerkesztői valamennyien kiváló munkál végeztek, ám külön szeretném közülük kiemelni Carol Ackermant, Chrisly Fr..mklint és Pau l Striclandel. Szintén hálával ta!lozom Rieh Halpertnek. Végezetül szeretném megköszönni Mrs. Ka1is munkáját, aki 1965-ben, hatodik oszt{llyos koromban megtanítolIa nekem, hogyan kelJ keHes számrcndslerbcn összeadni és kivonni, miközben sem 6, sem mi nem tudtuk, hogy ez pontosan mire is jó. -Jesse liben)'
Mindazok mellett, akiknek Jesse az imént köszönetet mondoLL valójában még sokan lehetnek II Samsnél, akiket kifclejtcttlink. Tőlük cz úton is elnézést szeretnék kérni. Jómagam Songlin Qillval és Lorella Yatcs-Slel dolgoztmn ezen ;1 projekten. Ez a könyv természetesen nem jöhetett volna létre a csa!{Ic.!orn, killönösképpen pedig feleségem Mary támogatása és megértése nélkül. Sajnos a könyvír.'is már csak ilyen. Az embernek leginkább esténként meg hétvégén van ideje és hangulata az alkotáshoz, amikor a csa láddal kellene lennie. Ilyenkor jön aztán a ~bocs de most nem tudok velelek menni, mert a könyvön kell dolgoznom" szöveg. -/)cwid B. Horoath
I
Tanuljuk meg a C++ programozási nyelvet 24 óra alatt xvii
Kfváncsiak vagyunk az Ön véleményére! Mint a könyv olvasója, Ön, kedves olvasó a mi legfőbh kritikusunk. Éppen azért nagyr.! értékeljük az Ön véleményét. Tudni .szeretnénk, ha valamit jól csináltunk, és persze a7.t is, Ita nem. Tudni szeretnénk, milyen más teriilctekr61 olvasna szívesen könyvekct ebben a sorozatban, és termésZdesen örömmel fogadunk minden egyéb megjegyzé.
5)
&&
(y :> 5
II z > 5)
)
A korábbi éltékeket behelycltesítve HAMIS lesz a feltétel, hiszen x nem nagyobb 5-nél, így az ÉS nem lehet IGAZ. Az ÉS-nél mindkét oldalnak igaznak kell lennie. Hiába szeretünk egy ételt, ha nem jó az íze, nem esszük meg.
Apropó
Csoportosflás zár6jelezéssel
Érdemes plusz zár6jelekkel csoportositani a kifejezéseket. Ne feledjük, a cél egy olyan program létrehozása, amely működik , könnyü olvasni és átlátni. Bővebben
az igazság természetéről
C++-ban a O reprezentálja a hamis értéket, minden más pedig igaz logikai értéknek felei meg. Számos C++ programozó ki is használja ezt az i t-es szerkezeteiben, Íme erre egy ~szép" példa: II ha x igaz (nem nulla)
i f (x)
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) x =: O;
II ha x nem nulla
Igazából mindkét megoldás helyes, de az utóbbi talán könnyebben átláthatÓ, Érdemes inkább logikai ki€=nékel€=sre használni a CH ezen tulajdonságát, semmint egy változó nem nulla én€=k€=t ellenőrizni vele, Az alábbi két sor jelentése szintén azonos: if if
( !x ) (x
=:=:
O)
II ha x hamis (nulla) II ha x nulla
A második utasítást ezzel együll va lamivel könnyebb megérteni és hiszen sokkal kifejezőbb .
7s l l. rész • Bevezetés a C+ + programomsi nyelvbe
Kérdések és válaszok Kérdés: Mié,-t haszlláljakfe!esleges ziú'Óje!ezést, lia úgyis a precendia határozza meg az operátorok kié,tékelésél? Válasz: Noha 3 fordítóprogram természetesen ismeri a precedenciáját és a progmmozó megtal111lhatja azt, megfele16 zárójelezéssel azért sokkal könnyebben tarthat juk karban a programjai nkat Kérdés: Ha a relációs operátorok él1éJ.>e l-et llagy O-át ad, a t6bbi éltéJ.'Cl lIlié/t tekillfji/k igaznak? Vú/asz; Relációs operátorok l-et vagy O-ál adnak vissza, de minden kifejezés ad vissz:! értéket, így akár az if ut:lsításban is ki én(! kelhelő. íme erre egy példa: if (
(x .. fl
...
b) =" 35 )
Ez egy szabályos CH utasítás_ A bels6 zárójelben szere pl ő hozz::'irende1és még akkor is lefut, ha maga a feltéle1viugálat hamis eredményt ad. Kérdés: II tabuláto/; a szóköz és az 1íjsor milyen. hatással V(lIl a p rogral11unkra? Válasz: A tabulátor, a szóköz és az újsor - ezeket nevezzük üres karaktereknek - nem befolyásolják a program múködésél, csupán olvashatóbbá teszik a forrásk6dllnkat. Kérdés; A negatív számok igaz vagy h(,llllis logikaI é/1éket jelentel/ek? Válasz: Minden nem nulla szám - legyen az pOZitív vagy negatív - igaz értékként viselkedik.
Gyakorlatok MOST, hogy már rudunk egyet s mást a kifejezésekr6l és az utasításokr61, válaszoljuk meg a kvízkérdéseket és a !"'Yakorlatok segítségével mélyítsük el a megszerzett tudást.
Kvfl l. Mi a különbség az XH és a HX között? 2. Melyik más nyelvekben gyakori operátor hiányzik a CH -ból? 3. Mi a különbség a 001- és jobbérték között? 4. Mit csinál a modlllo operátor?
4. óra • Kifejezések é, uta'fiá,ok! 17
Feladatok 1. Írjunk egy programot, amely a három tanult módon (a "'3+1, 3+ =1 ét; aH) növeli egy változó értékét. Fordítsuk [e, linkeljük és futtassuk. Más leLt a méretük? Észleltü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 emenetként. Fúggvényhívflskor argumcnlumké nt bármely a C++ nyelvben értelmes kifejezést meg.dharunk. Ebbe bcll:tartoznak :t konstansok, a matematikai és logikai kifejezések, illel\-e egy bármely mis függvény ;Utal visszaadott érték is. Tegyük fel például, hogy van eg}' li1ggvényünk, melynek a deklarációja a következőképpen fest: nt MyFunctioll(int thclntegerParam. bool theBoolean) ;
Ezt számos különbö7.6 módon hívhat juk meg, amely le het6sC:gckb61 i l l csupán hármat mutatunk be: _nt
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 :;0: ,
X
,.
3 , y " 5:
\z utolsó hívás kirnenelelél lekinlve leljes O && smull < Mi\XSMlILL)
if
(small 1; 5000 == O) II 5000 soronként kiír egy pOttyOt std : : cout « • . • .
23 : 24 : 25 : 26 : 21 :
'khIebb: • «
small+ +;
large-:2 ;
110 II. rész· Bevezetés a C+ + programozási nyelvbe 29 : 30: 31 :
std: :cout « "\nKisebb : " « small « " Nagyobb: " « large « std: : endl; return O;
32 :
írjon be egy kisebb számot: 2 írjon be egy nagyobb számot: 100000 Kioebb : 2 ..... Kisebb:33335 Nagyobb : 33334
• EZ;J program egy egyszen1 játék. Beírand6 egy kisebb és egy nagyobb szám. A kisebb egyesével növekszik, a nagyobb kettesével csökken. A játék célja annak kitalá lása, hogy hol találkoznak. A 10-13. sorban írhatjuk be:il számokat. A 'IH. sor egy olyan ciklusfehételt tal'talmn, mcly esak az alábbi három feltéte l egyidejű teljesülése eserén engedi meg a ciklusmag kfutásál: • a sma11 nem n:!gyobb a large-nál • a large pozitív • a sma11 kisebb MAXSMALL-n{11 (ami a kis egészek
m~rethal1lrd)
A 21. sorban II small SOQO-es maradékát sz.1mítja ki :il program. Ez csak akkor nulla, ha a small egész számú többszöröse 5000-nek. Ne feledjük , hogy ez:! viz..GetAge ( )
d elete Frisky ; return O;
44 :
Frisky 2 éves Frisky 5 é ves
A Simp1eCat osztálynak két tagváltoz6ja van, mindkettő egészeket címez. A konstruk-
tor (a 20-24. sorban) inicializálja ezeket a mutat6kat úgy, hogy a dinamikus mem6 riaterületre mutassanak, és a változ6khoz hozzá rendeli a megadou kezd6érrékekcl.
188 1111. rész • Mem6riakezelés A destnIktor (a 26-30. sorban) felszabadítja a lefoglalt memóriaterOletekcl. Minthogy ez a destruktor, nincs értelme nullázni a mutatókat, hiszen ezek a későbbiekben már nem lesznek elérhetőek. Ez egy bizlonságosan vállalható kivétel azon szabály alól, hogy a törö lt mutatókhoz érdemes null értéket rendelni ; persze nem okoz gondor a szabály követése se m.
A hívó függvény - jelen esetben a main () - mit sem rud arról , hogy az itsll.ge és az itsWeight a dinamikus memóriára irányuló mutatók. Egyszenle n meghívja a GetAge () és a GetWeighL () függvényeket, mint eddig is; a mem6 riake zelés részlet.ei el vannak rejtve az osztály megvalósításában - ahogy annak lennie kell . Amikor F'risky törlődik a 42. sorban, egyből meghív6dik a destruktOJ:l. A deSlmklor minden adattag-mutatót töröl. Ha ezek kimutatnak egyéb objektumokra is, melyek a felIl:lszn{116 {tltal definiált oszt51yok példányai, akkor azok destruklorai is meghfv6dnak. Ezen a p6kl{tn jólláthat6, hogy miért érdemes saját destruktort írni (a fordít6 progr.:tn1 állal fclaj{tnlotl alapértelmezés helyett). Alapértelmezetten a 28. és 29. sorban láthat6 törl(:sek n~m tört(:nnének meg; a programozómik kell ezeket ll1itsLcngth = length ; int GetLength() const ( return this->itsLength; void SetWidth(int width) ( itsWidth = widt h ; ) int GetWidth() const { return itswidth ; } private: int itsLength; int itsWidth ; ); Rectang1e : : Rectang1e ()
( i t s Wid th = 5 ; itsLength;; 10 ;
Rectangle : : -Rectang1e() () int main() ( Ractangle t haRect ; cout « "Tógla 1apom « • láb cout « "Téglalapom « " láb
theRect . $et Length(20) ; theRect . SctWidth(lO); cout « "Tégla!apom • « thcRect . GetLcngth(l « " láb hosszú . " « endl ; cout « "Téglal a pom " « t h cRect . GetWidth() « • láb széles . " « e ndl ;
42 :
43: 44 :
" « theRect . GetLength() hosszú ." « end1; • « theRect . GetWidth() széles . " « endl;
return O,
Téglalapom Tégl alapom Téglalapom Tégl alapom
10 láb hosszú . 5 láb széles . 20 láb hosszú . 10 láb széle s .
190 III. rész • Mem6riakezelés
-
A SetLength () és GetLength() hozzáfér6 fü&!,,,,ények kifejezetten a this mutatót használják a Rectangle objektum tagváltozóinak elérésére, ellentétben a SetWidth (J és GetWidth () hozzáfér6 függvényekkel , melyek másként dolgoznak. Nincs különbség a viselked ésükben, csak abban, hogy a this nélküli metódus kódja talán olvasl1at6bb. Tudta hogy...?
Mire is val6 tehát ez a this mUlat6?
Ha csak ennyi értelme lenne a this használatának, nem lett volna érdemes megemlfteni. A this mutató hordozza az adott objektum memóriacfmét - ez igen hatékony eszköz lehet! A könyv egy késő b bi (14.) fejezetében láthatjuk majd ennek a gyakorlati felhasználását az operátorok túlterhelésénél. Egye l ő re elég annyi is, ha tudunk a this mutató l é tezéséről, és arról. hogy magára a szóban forgó ob jektumra mutat. Semmi dolgunk vele, nem kell létrehoznunk vagy törölnünk - ezt elvégzi helyettünk a fordítóprogram.
Gazdátlan vagy "lógó" mutatók A programhibák egyik forrása a gazdátlan mutatókb61ered, melyeket nehéz és kellemetlen felkutami. Gazdátlan mutató akkor keletkezik, amikor törjünk egy mutat6t (deleteleO - ily m6clon felszabadít juk a hivatkozott memóriaterillelet - és kés6bb anélkül kisércljük meg újra használni ezt a muratót, hogy bármit is hozL1rendeltünk volna. Olyan ez, mintha egy cég elköltözne telephelyér61, és egy ügyfelük a régi sti'imon próbálna telefonálni nekik. Lehet, hogy semmi különös nem történik - csöng egy telefon egy elhagyott irodaházban. De az is lehet, hogy ezt a számot már valaki más használja, aki esetleg v~gigdoJgozta az éjszakát, és ez a telefoncsörgés ébreszti legszebb álmából. Röviden: ne használjunk olyan mutatókat, melyeket e16z6leg töröltOnk. A mulató továbbra is a mem6ria egy bizonyos területére mutat, de ;1 fordítóprogramnak joga van oda más adatokat tenni, így ennek a mtJtatónak a használata II progl"J.m összeomJásához vezethet. Ennél is rosszabb, ha a program vígan fut tovább, és a hiba csak néhány perccel kés6bb következik be. Ezt időzített bombának hívják, és nem túl vicces. A bizlonslig kedvéélt érdemes NULL-ra állítani (és ezzel lefegyverezni) a használaton kívül helyezett mutat6kat.
Apropó
K6bor mutat6k
Az. gazdátlan mutatókat kóbor vagy lógó mutatóknak is hívják.
10. óra • A mutatók kifinomult használata 191
Konstans mutatók Mutatók esetében a const kulcssz61 ti típus előtt , után, vagy mindkét helyen hat juk. Az alábbi deklarációk mind helyesek:
használ~
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 hivatkozou é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 ~p'rwo
..
működik
a következ{}:
&x
pThree konstans mutató egy konstans egészre. A hivatkozott szám értéke sem válloztatható mcg, és a pThree sem mutathat semmi másra. Húzzu nk egy képzeletbeli függ61eges vonalat :l csillag jobboldal án. Ha a cons t szó a vomtlt61 ba lra esik , akkor az objektum konstans, és ha jobbr-.l, akkor pedig a maga mutató változtathatatlan const int· pl ; II A hi vatko.,;ott egé!lz konstans i nt • const p2; I I p2 ko nsta ns , ne m mu tat hat semmi másra .
Konstans mutatók és konstans tagfüggvénv.k A 7. 6rán tanultunk az osztályok alapjair61. Oli volt szó arrol, hogya const kulcsszót lehet tagfüggvényekre is vonalkOZL1.tn i. l ia egy függvényt konstansként dekladlunk, a fordíl6progr-.un hibajelzésselm3sít viSsza minden kísérletet, amellyel az adon függvény változtatni akama az objekrumon. Ha konstans objcklumra hivatkoz6an deklará lunk egy mutatól, akkor ezt csakis konstam; mctódusokkallehet használni. A 10.5 Lista ezt illusztrálja.
10.5 Usta - const objektumot cimz6 mutatá használata (constptr.cpp) o : !! 10 . 5 Lis ta l : I I ko n s tans objektumot c i mzo mu tat6 ha s.,;ná l ata 2 : 'incl ude 3, 4 , class Rec t angle 5: { 6 : public ,
192 1111. rész • M.móri.koze~s 7: 8: 9: 10 : ll : 12: 13 : 14 : 15 :
16 :
Rectangle() ; -Rectangle() ; void SetLength( int l e ngt h) ( itsLength = leng t h ; int GetLength() const { re t urn itsLength ; } v o id SetWi d th {int wi dt h) l itsWid th - wi dth; i nt Get Widt h() c on s t { retu r n i t s Wid th ; )
private : int itsLength ;
int itsWidth ;
17 : 18 : 19 : 20 : 21 : 22 : 23 : 24 :
25 : 26 :
}; Rectangle: : Rectangle () : itsWidth(S) , itsLength (10) (l
Rcc t angle : : _Rectn ng l e () {}
27 : 2B :
int main()
29 :
30 :
31 : 32 :
Rectang!a* pRect = new Rectanglc; const Rectangle * pConstRect • new Rectangle ; Rectangle • const pConstPtr = new Rectangle ;
33 :
34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 , 43 : 44 , 45 : 46 : 47 :
48 : 49 : 50 , 5 1: 52 :
std : : cout «
'pRect szélessége : " pRect ->GetWidth()« láb"« std : : endl ; std : : cout « "pCon s t Rect szélessége : « pCo n stRect->Cctwidth() « ' láb' « std , , endl; stó : , cout « ' pCo n stPt r szé l essége , « pConstPtr->CetWi d th () « ' láb ' « std :: endl ;
«
pRect->setWidth(10) : II pConstRect->SetWidth(lO) ; pConstPtr->SetWidth(lO) ; std : : cout «
'pRect szélessége: pRcct->GetWidth()« láb'« std : : endl ; std , , cout « 'pConstRcct szélessége : « pCo n s t Rect->Get Width() « • l á b " « std :: cndl , s td : : cout « 'pCon s tP t r szélessége : « pConstPt r - >Ge LWidth() « • láb' « s td : : endl ; return O; «
pRect sz élessége : 5 láb pConstRec t szélessége : 5 l áb pConstPtr szélessége : 5 l á b pRect szélessége : 10 láb pConstRcc t szélessége , 5 láb pCo n s t Pt r s zé l essége : 1 0 l áb
10, óra • A mutatók
A 4- 18. sorokban deklarálunk egy téglabpot. A 13. sorban a GetWidth () tagfüggvényt konstansként deklaráljuk A 30. sorban cgy mutatóva! hozzuk IéIre a pRect téglalapot. A 31. sorban deklarált pConstRect egy konstans téglalapra hiv:ltkozó mulató. A 32. sorban létrehozott pConstPtr pedig egy téglalapra hivatkozó konstans mulató. 11.34-39. sorok kiírják a három szélesség-értéket. A 41. sorban a pRect segítségévcl álállitjuk az els6 téglalap szélességét 10-fe. A 42. sorban a pConstRect használatával tennénk u~,'yaneZlJ de ez II mUlató egy konstansként felveti 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élfehozvGetAge (). A ~ m utat " ( - » operátor jobb, mivel ránézésre is nyilvánvalóan mutatja a programozó 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 átvenni 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 lehel 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 egyszení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élobjektum 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.
mUlatók olyan változók, melyek egy másik objekmm memóriacímét tárolják. A hivatkozások ezzel szemben egy másik objektum névváltozatai.
A
Hivatkozások létrehozása Hivatkozást úgy hozh:nunk létre, hogy megadjuk a
20)
value = 1 ; else
35 :
·pSquarcd = n*n: *pCubed n*n*n:
36:
Valuc '" O;
37 : 38 :
39 :
re t urn Value;
1
20e III. rész • Mem6riakezelés
Enter a number (O-20) : 3 number: 3 square : 9
cubed: 27
A 8. sorban lélrehozzuk a number, squared és a cubed (szá m, négyzete, k6be) egész változókat. A number--nek a felha sználó bemenő adata ad értéket. Ezt a számot, valamint a squared és a cubed címét átadjuk a Factor () függvénynek. A Factor () megvizsgálja az érték szerint áladolt els6 p;mun éterl. Ha ez nagyobb húsznál (v:lgyis a maximumnál, amit ez a függvény még kezeln i ~ llld "), akkor II visszatérési vá ltoz6t (va.lue) egy egyszelű hibajelz6 értékre állítja. rigycljük meg, hogy a Factor () visszatérési értéke vagy ez a hibajelz6 érték, vagy nulla (ami azt jelzi, hogy minden rt..:ndben zajlott). A 38. sorban kerill visszakoldésre a Value ért6kc. A két ténylegesen várt ~v isszatérési érték~, a szá m négyzete és köbe nem a return révén jut vissza a fl5program ba, hanem a fuggvénynek átadott mut..tók révén lehet6vé vált közvetlen C:n6kvá ltoztatással. A 34. és 35. sorban bekerülnek a kiszámított értékek a mutatók álUl! hivatkozott mcm6riacímekre. A 36. sorban a visszatérési énéket (value) sikeres állapotra állíljuk, és a 38. sorban vissz..küldjük. A progmmot továbbfejleszlhetjOk azzal, hogy felvesszük az al ábbi deklar.'lci6t: enum ERROR-VALUE ( SUCCESS, FAILURE): Ezek után már nem O vagy l értéket kellene visszaadnia a t"üggvénynek, hanem SUCCESS-t (sike,t) vagy FAILURE-t (híbtit). Ahogy korábban láthattuk, II fcl sorolásos típus első eleme (SUCCESS) O, második eleme (FAILURE) l értéket vesz föl.
Több visszatérési érték használata hivatkozásokkal Bár a 11 .7 Lista programja működik, könnyebb olvasni és karbantartani, ha mutatók helyett hivatkozásokkal dolgozunk. A 11.8 Lista mutatja az átín programot, amely már hivatkozásokat használ, valamint szebb hibakezelést valÓSít meg az ERR-CODE révén.
". ó.. • Hivatkozások 1209
11.81.is1a - A 11.71.is1a úlnlrúa: _ _ ""'"'" _
... h i t _ a l
(-..withnof.cppl o : /I 11. 8 Lista l ; 1/ FOggvények felruháloása 2 : II tObbszOrös visszatérési értékkel hivatko zások révén
3:
•• 5:
tincludc enum ERR-CODE ( SUCCESS, ERROR ) 1
6.
7: ,.
ERR-CODE Factor(int , int&, int&) ;
9:
int m",in() {
10 :
int number , squared , cubed : ERR-COOE res ult :
ll : 12 : 13 ; 14 :
std : : cout «
15 : 16 :
std : : cin »
17 : 18 ,
re!lult • Factor(number , squared , cubed) ;
19 : 20 : 21 : 22 : 23 : 24 :
i f (reGult
25 :
else
26 : 27 :
return O,
,
"Enter a number (O - 20) :
number ;
..
SUCCE$S)
std : : cout «
"number :
std : : cout «
·square : ·cubed:
Btd : : cout
«
std :: cout«
.
« «
number « • \n "; squared « • \n" ;
«
cubed
«
• \n';
"Error encountered!!\n" ;
28 :
29 : 30 :
3L
,
ERR.....CODE Factor (int n, int &rSquared, int &rCubed)
32 : 33 : 34 : 35 :
36 : 37 : 38 :
i f (n > 20) re t ur n ERROR; else
,
r Squared :: n *n ; r Cubed '" n"n*n ; return SUCCESS ;
39 : 40 :
Enter a number (O-20) : ] nwnbe r : ]
square : 9 cubed : 21
II simple error code
210 1111. rész • Mem6riakeze~s
A 11.8 ü sta megegyezik a 11.7 Listával , kél kivételle1. Az ERR-CODE felsorolásos típus átlálhat6bbá teszi a hiba jele ntéseke t (33. és 38. sor) , valamint a 19. sor hibakezelésél.
A jelentősebb vá ltozás azonban abban áll, hogya Factor () függvényt most úgy deklaráltuk, hogy mutatók helyett hivatkozásokal vár a squared és a cubed helyére. A pammé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 CI
cl
mutat6k mindarnl képesek, amire
hfvatl.lozások?
Válasz: A hivatkozásokat könnyebb használni és megérteni. A címfeloldás rejtenen tör-
ténik, nincs szükség isméte lt címfeloldó operáto rokra . Kérdés: Aná,., hasz/lálj/.m k akkor /IIuUllókat, ha egyszerl1bbek CI lIiv(l lkoz!üok?
Válasz: A hivatkoz{!sokat nem lehet nu llázni, sem pedig újra felhasználni. A mutatók rugalmasabbak, de valamivel nehezebb a használatuk.
Gyakorlatok Most, hogy megismerkedfÜnk a hivatkozások használatával , válaszoljunk meg néhány kérdést és végezzünk el néhány feladatO[ Uldásunk e l lenőrzésére!
Kvfz 1. Mi az a hivatkozás? 2. Milyen operátorral hozunk létre egy hivatkozást? 3. Mi egy hiv"koz's mem6"ocime' 4. Mi a függvények alapértelmezett paraméterátadási módszere a C++-ban? Milyen módon lehet ennek korlátait átlépni?
Feladatok 1. Egyesítse a passbyptr. cpp és a passbyref. cpp (11.5 és 11 .6 Lista) programjaiban használt módszereket úgy, hogy az egyik é rtékel mutató, a másikat hivatkozá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 helyeu 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 fejlé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 inidalizálni kell a deklaráláskor. Nem lehet nu ll hivatkozásu l,k, csak le nulIázott mutat6nk. 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 ellenté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 dolgozik 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 obje ktum címe kerül átadásra. Másrészt hivatkozások átadásával, hiszen ezzel az eredeti változó névvá llozatál kapja meg a filggvél1y.
12.
ÓRA
A hivatkozásokról és mutatókról - mélyebben Ebben az 6rában a következőkrőllesz sz6: • • • •
llogyan tehetjük hatékonyabbá programjainkat cím szerinti paramétecitadással Mikor érdemes hivatkozásokat használni, és mikor mutatókat Hogyan Icgyiln k úrrá a mcmóriagondokon a mutatók ha.~ználata közben Hogyan kerüljük el a hivatkozásokka l kapcsolatos csapdákat
Hatékonyabb a cfm szerinti paraméterátadás Az érték szerinti paramétcrát:tdáskor másolat készül az objeklumr61. Ha a függvény visszatérési értéke egy objektum , akkor megint egy újabb másolat keletkezik. Nagyobb mércrú, felhasználó általlétrehozou objekrumok esetén ezeknek a másolások a száITÚtásigényc már igen számottevő is lehel. A szükségesnél több memóriát használ a program, és végs6 soron sokkal !assabban fut.
214 1111. rész • Memóriak.,.lé. Egy, a felhasználó által definiált objektum mérete a veremben az egyes tagváltozók méretének összege. Ezen tagvállozók akár nagy objektumok is lehetnek. Egy efféle gigantikus struktúra átadása (azaz a verembe másolása) igen sokba kerülhet memória és futási idő vonatkozásában. Másféle költségek is fellépnek ilyenkor. Az oszt{llyok pélclányosításakor készül róluk t'b'Y ideiglenes másolat; ilyenkor a fordít6progmm meghív egy speciális konSlnlktOI1: a másoló konstruktort. A 13. órán, a Ixmyolulwbb függvényeknél megtanul juk, hogy hO!''Yan működnek ezek a másoló konstnlktorok, és hogy hogyan készíthetünk magunknak ilyeneket; most azonban elég annyit tudnunk, hogy egy objektum ideiglenes másolatának a verembe másolásakor meghív6GetAge()I std : :cout « ' years old \n' ; II theCat->SetAge(8); const! return thecat;
Making a cat ... Simple Cat Constructor ... Fris ky i s 1 years o l d Fris ky i s 5 years o l d Calling Fun ct i onTwo ... Function Two . Returni n g . Fris ky i s now 5 years old Frisky is 5 years old Simple Cat Destructor . .
12. 6ra' A hivatkozásokr61 és mutatókr61 - mély.bb.n 1219
A SirnpleCat számára két hozzáfér6 függvényt deklaráltunk: a GetAge () -el a 11. sorban, amely konstans függvény , és a SetAge () -et a 12. sorban, amely nem az. Van egy saját tagváltozója is, a 15. sorban deklarált egész itsAge. A konstruktor, a másoló konstn Jktor és a destrukwf úgy lett megírva, hogy kiírja a saját nyomje\z6 üzenetél. A másoló konstruktor azonban sohasem kerül meghívásn:I; prog-
ramunkban ugyanis csak dm szerinti paraméterátadás tönlmik, és ehhez nincs szükség másolat készítésére. A 4Q. sorban létrejön egy macska; az alapértelmezeu életkorát ki is nyomtat ja a 41-42. sor. A 44 . sorban a SetAge () segítségével átá!1íljuk a korát (it~Age) 5-re, és az en..>(lmf:nyt megjelenítjük a 45-46. sorban. A FunctionOne ( ) -l nem haszn:íljuk ebben a programban, csak a FunctionTwo () -t, amelyet egy kicsit átírtunkj a 34. és 35. sorban láthatjuk visszatérési értékét és átadand6 paraméter6t: mindkett6 egy konstans objektumra utal6 konstans mutató.
Minthogy az átadandó pamméter és a visszatérési érték is cím szerint kerUl ,ítad:'isl"""d , nem készülnek m:'isolatok, és nem hív6tlik meg a másoló konstmktor. A FunctionTwO () -beli mutató azonban konstans, így nC:!m lehet bel6le meghívni a nemkonsta ns SetAge () -et. Ha az ezt megkísérl6 61. sort kivesszük a megjegyzésb61, nem fordul Ic a program. Érdemes megIIgyeini, hogy a main () f6programban létrehozott Frisky objektum nem konst:ms, így például átáJlítható a kora, meghívhat6 rá a Setll.ge () . Ennek a nemkonstans Frisky objektumnak a eimét kapja meg a FunctionTwo (), és mivel ez úgy lett dC:!klarálva, hogy konstansra hivatkozó ll1utat6ra számít, az objektumot konstansként fogja kezelni!
Hivatkozások, mint a mutatók kényelmes altematfvái A 12.2 Lista megoldja a felesleges másolás (és ezzel a másoló konstmktor és destruktor indokolatlan meghívásá nak) problémáját. Konstans objektumra mutató konstans mutat6t használ, így nem kell tartanunk att61, hogya függvény jogosulatlanul megválloztatja a paraméterként átadott objektumo{ka)t. A módszer azonban még mindig nem tú l elegá ns, mivel mégiscsak mutatókat kell átadnunk a függvénynek. Mivel azt is tudjuk, hogy az átadandó paraméterek nem lesznek null értékűek , könnyebb lenne az élet, ha mutatók helyett hivatkozásoka t adhatnánk át. A 12.3 Lista ennek szellemében korrigálja a 12.2 Listát.
220 1111. rész· Mem6riakezelés
o:
II 12.3 Lista 1: II Objektum hivat ko zásának az átadása 2: ftinclude 3, 4: class SimpleCat 5: { 6 : public : 7: SjmpleCat() ; 8: Simp leCa t (SimpleCat&) ; 9: -simp1eCat() ;
10 : int GetAge () co n st void SetAgc( int age)
ll: 12 :
retur n itsAge : } { i tsAge • age; )
13 : 14 :
15 : 16 :
private : int itsAge:
);
17 : 18 :
simpleCat : :Simp1eCat()
19 :
(
7.0 : 21 : 22:
std : : cout « itsAge = 1 ;
'Simple Cat Constructor ... \1'1 ';
23: 24 :
SimpleCat: :Simplecat(Simp1eCat&)
25 : 26 : 27 : 28 : 29 : 30 :
(
31 :
std: : cout«
"Simple Cat Copy Constructor ... \n";
Simp1eCat : : -Simplecat () { std : : cout « 'Simple Cat Destructoc .. . \n' ;
32 : 33 : 34 : 35 : 36 :
37 : 38 : 39 : 40: 41 : 42 : 43 : 44: 45: 46 :
const SimpleCat & Func tion'l'wo (const SimpleCat & theCat) ; int main()
( std :: cout« 'Making a cat ... \ n '; Simpl eCa t Fr i s ky; std : : cout « "Frisky is ' « Frisky . CetAge() « ' years 01d\n' ; int age", 5; Frjsky.SetAgc{age) ; std : : cout « 'Frisky is ' « Fri sky . GetAge() « ' years 01d\n' ;
47 :
48: 49 : 50 :
std : : cout « 'Ca11ing FunctionTwo ... \n" ; FunctionTwo(Frisky) ; std : : cout « "Frisky is " « Frisky.Geti\ge()
12. 6ra • A hi_omokról és mutat6król- mé~ebb,n 1221 • years old\n" ;
«
51: 52 :
return O;
53 : 54 : 55 : 56 : 57 : 5B : 59 : 60 : 61 : 62 :
1/ functio nTwo.
const SimpleCilt
konstansra utal6 hivatkozást kap és ad vissza FunctionTwo (const SimpleCat (. theCat l
&
(
std :: cout«
' Punction Two. Returning ... \n' ;
std : : cou t «
'Frisky is now ' « « • years old \n "; /1 thecat . SetAge(B); const! return theCat;
theCa t . GetAgc()
63 :
Making él ca t . .. Simple eat constructor ...
Frisky lS 1 yoars o ld Frisky is 5 years old Calling FunctionTwo FunctionTwo . Returning ... Frisk.y is now 5 years old
frisky is
~
years old
simpl e eat Destructor ...
A kimenet megegyezik a 12.2 Listáéval. Az egyetlen emJílt!sre mélt6 külónbség az,
hogy most a FunctionTwo () függvény konsta ns objektumra utaló hiv,ltkoz{lsl vár és ad vissza. Ezen II példán is láthatj\lk, hogy hivlltkoz.'isokkal könnyebb dolgozni, mint mutat6kkal ; a ko nstansok által elérhct6 nyereség, hatékonyság és hizlonság azonban itt is megval6su1.
Mikor használjunk hivatkozásokat, és mikor mutatókat? A C++ programozók általában sokkal szívesebben használnak hivatkozásokat, mint mutat6kat, mivel ezek átláthat6bbak és hasznáJhat6bbak. A hivatkozásokhoz azonban nem lehet új objektumot hozzárcndeJni. Ha a program futása közben változtatni kell II célobjektumon, kénytelenek vagyunk mutatót használni. A hivatkozás nem lehet null értékű; így, ha van esély arra , hogy acélobjektum nul1áz6dik, akkor sem tudunk hivatkozást használni, csak mutatót. Ha a dinamikus memóriából szeretnénk területet foglalni, akkor is mUlatókra van szükség, ahogy arról az előző órákon már szó volt.
222 1111. rész' Memóriekezelés
Ne adjunk vissza hivatkozást!
megszűnt
objektumra mutató
Ahogya C++ programozók megtanulják a eim szerimi par.lméterátadásl, vérszemet kapnak, és id6nként hajlamosak túlkompenzálni ifjúkori lévedéseikel. Ne lévesszük szem eid!, hogya hivatkozás csupán egy névvá llOZSetAge (2 *i +1) ; 26: Family[i) = pCat ; 27 :
)
28 : 29 : for (1 = O ; i < 500; i+ + ) 30 : std : : cout « "Cat ff' « i +l « " : 31 , « Family[ i]->cctAge() « s td, : end l ; 32 : 33 : for (i = O; i < 500 ; i ++ )
34 : {
15. 6ra • Tömbök 35 : delet e Family [i l ; NULL ; 36 : Family[i ] 37 : ) ~
]8 :
39 , return 0 , 40,
menet Cat fl, Cat c.t
l
'2 : 53 '3 :
Cat M499 : 997 Cat ~500 : 999
A CAT osztályt a 3-15. sorokban deklaráljuk. Ez a form .. amClgy mindenben megegyezik
a 15.3. Listában bemutatott:!!. Ugyanakkor ez alkalommal a 19. sorban talá lható tömb neve m!lf Family, és a dekl:lrációja szerint 500 darab CAT típlJSÚ objektumot dmz6 mulató! képes tá rolni. A nyitó ciklusban C22-27. sarok) 500 új CAT objektumot hozunk létre, amelyek a dinamikus 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 tömbt:le mbe n.
műveletével
az objektu mot
címző
mulatót elhelyezzük a
megfele l ő
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ő objektu m dmét a tömbból (Family lil), majd ezen kereszCi.iJ meghívjuk a Ge tAge () tagfü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 heIyel. 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áltozó 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 objektumot, a Family tömb elemeit pedig null értékre 5l1íljuk
Dinamikus memóriában tárolt tömbök deklarálása Arra is
lehetőségünk
van, hogy a teljes tömböt a dinamikus memóriában [ároljuk, ne csak a bCl1ne elhelyezett objektumokat. Ehhez a new operátorL kell használnunk de úgy, hogy aZI kombinMjuk a tömbindex megadásával. Ennek a műveletn ek az eredménye egy mulató lesz, amely :17.1 ól memóriateriilelet címzi, ahova ti [őmb került. Lássunk egy példát: CAT *Family = new CAT15001;
Ez a deklaráció azt mondja, hogy Family egy muLatÓ, amely egy 500 darab Cl\T típusü objektumot t'iro16 tömböt dmez. Másként fogalmazva Family nem egyéb, mint a Family[OJ elem dmI:!.
,I
Ennek megoldásnak az egyik nagy előnye az, hogy ettől kezdve Inumtóaritmetikát is használhatunk a tömbelemek dmzésére. Lássunk erre is egy példát: C1\T "Family = new ClIT(5001 ; CAT *pCaL = Family; II pCat a Family[OI e l emre mutat pCat->Setllge(lO ); II F«mily[O ] érLékét lD-re á ll it juk pCat++; II advance to Family [l ] pCat->SetAge(20); /I Pamily(ll értékp.t 20-ra állit juk A femi els6 művelet létrehoz egy 500 CAT típusú demet tároló tömböt a dinamikus memóriáb:m és egy mutatÓl, amely ennek a tömnek az cls6 elemét címzi. A harmadik sorban 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 ezután inkrementáljuk, melynek hal.ís{ira immár a következő CAT objektumot címzi. EZl ismét a már látott módon használjuk, vagyis megint meghívjuk az aktuálisan címzett objektum Set1\ge () tagfü~'Vényét, de immár a 20 értéket adjuk neki bemenelkénl.
Tömb mutatója és mutatók tömbje Vi:t.Sgflljuk meg a következ6 három deklarációt: 1 : Cat FamilyOne[500l 2: CAT • FamilyTwo[SOO]; 3: CIIT • Family'l'hree = new CAT[500] ; FarnilyOne egy 500 CAT objektumot tároló tömb. FamilyTwo 500 olyan mutatót lárol, amelyek CA'r típusú objt!ktumokat címeznek. FamilyThree maga egyetlen mulató egy olyan tömbre, amely 500 CAT objektumol tárol.
15.6ra • Tömbök 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 egymá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ímz6 mutató.
Mutatók és tömbnevek C++-ban a tömb neve ncm egyéb, mint egy konstans mutató, amely,) tömb els6 elemé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:utalmaz az a cím, amit a &Family 101 mllvelettel is megkaphatn5nk. Kicsit egyszenlbben : a tömb nevét leírva ml::~kapjuk a tömb c1s6 elemének címét. A C++-b:m teljesen elfogadott, ha egy tömbnevet állandó nllltatókénr használunk , vagy épp fordítv:l. A Family + 4 forma tehát ebben a nyelvben ugyanazt jelent i, mint a Family r41, ugyanúgy hivatkozhatunk vele bárhol a tömb ötödik demére. Ha egy mulalÓ értékéhez hozz:'iadunk egy számoL, inkrememáljuk, v:tgy dekrementáljuk aZl, az ehhez szükséges Imlvelete a fordít6progra m végzi el. EZl azért fon tos hangsúlyozni, mert a Family + 4 forma természetesen nem egy olyan dmet jelent, ami 4 bájtnyira van a Family tömb e1ejét61. Ha például minden, a tömbben tárolL objekmm 4 bájt hosszlIságú, a kkor a Family + 4 cím 16 bájtnyirn lesz a tömb kezdetét61. Ha viszont a Family tömb CAT típusú elemeket tárol, és minde n CA'!' 20 bájtnyi tárhelyet igényel, :lkkor Family ~ 4 jelentése egy a tömb elejét6180 bájt távolságra nmtaeó CÍm lesz. A 1S.5. Lista e~y o lY'1n k6clor mutat be, amelyben a dinamikus me móriában hozunk lélre egy tömböt.
15.5. Lista - Tömb létrehozása 8 new operátorral (oewarrav.cpp)
o:
II 15.5 Lista - TOmb a dinamikus memóriában 1 : Hinclude 2, 3 : cla.ss CAT 4: {
5 : public :
1279
280 l iV. rész · Halad6 eszközök 6: CA'!'() { itsAge = 1; itsWeight=5; } 1/ alapértelmezett konstruktor 7: ~CAT() ; /1 destruktor S : int GetAge() const ( retllrn itsAge ; ) 9 : int GetWeight() const { return itsWeight; 10 : void SetAge(int age) 11 : 12 : private : 13 : int itsAg-e :
{ itsAge '" age ;
}
14 : int itsWeight ; 15 : J ; 16 : 17 : CAT : : ~CAT() 18 : ( 19 : /1 std " eout«
"Destructor called!\n' ;
20 : ) 21: 22 23 24 25
: int main() : : Cl\T * Fl.lmily .. new CAT [ 500j ; : int i;
26 : CAT • pCat ; 27 : for (i '" O; i < 500 ; iH) 28 : {
29 : pCat = new CAT ; 30 : pCat->SatAqe(2 * ]
+1};
31 : Fumily[ll ,. ' pCat ;
32 : delete pCat; 33 : l 34 : 35 : for (i .. O; i < 500; iH) 36 : std :: cout « 'Cat ." « 1+1 «
': •
37 : «Family[ij.GetAge() « s t d :: endl ;
38 : 39 : deletc [I Family; 40 : 41: return O; 42 :
Kimenet Cat U: 1
Cot 1/2 : 3 Cot B: 5 Cat "499 : 997 COL 8500: 999
A 24. sorban deklaráljuk a F'amily tömböt, amely 500 CAT típusú objektumot képes tárolni. A new CAT[SOOI utasítás hatására ebben az esetben az egész tömb a dinamikus memóriába kerül.
Apropó
Egy mGszaki részle!...
Technikai értelemben a 24. sorban tulajdonképpen egy a dinamikus memóriában le· vő névtelen tömböt deklarálunk. A Family mufafóba csupán a tömb első elemé· nek d me ke rűl. Ennek ellenére általiIban azonosnak teki ntj űk ezt a mutatót magával a tömbbel, ami azért nem helytelen, mert maga a C++ fordító is ~így gondolkodik". Amikor létrehozunk egy tömböt -legyen az akár a vermen, akár a dinamikus memóriában - annak a neve tulajdonképpen nem más, mint egy mutató, amely az elsó elemét clmzi. A tömbbe kerül6 CAT objektumokat szintén a dinamikus memóriában hozzuk létre (29. sor). Figyel jük meg ugy,makkor, hogy I:!bbl:!n az I:!setben nem az új objektumok mUlat6i kerülnek a tömbbe, hanem m:Lguk az objl:!ktumok. Ez a tömb tehát nem mUlalótömb, amelynek ekmci Cl\T típusú objektumokat címeznek, hanem CA'I' objektumok tömbje. Áltah'iban a [ l operátor( helyeltesíthetjük a * operátorra l Gndirekdó) is. A Family [3 J tehát cbben a helyzeten ugyanazt jelenti, mint a * (Family + 3) forma.
Dinamikus memóriában létrehozott tömbök törlése A Family név tehát nem egyéb, mit egy mutató, amely egy a dinamikus memóri!ib:u, talál ható, CAT objektumokat tarta lmazó tömb els6 elemét címzi. Amikor a 15.5. Lista 31. sorában használjuk a pCat mlllatót, akkor az garantáltan egy II dinamikus memóriában létrehozou CAT objektl.lmra mOIat, ami most bekerül a megfelelő tömbelembe. ( Eddig ugye semmi meglepö nincs 3 dologban, elvégre pont arról van szó, hogyan kell a dinamikus memórifib:m objektumtömböket kezelni .) Csakhogy a pCat mutatót ti ciklus következ6 iterációjában is feihasználjlIk. Nem jár ez azzal a veszéllyel hogy az el6zó CIIT objeknllnra már nem mutat semmi, és soha többé nem fildjuk felszabadítani az {t1t:da lefoglalt memóriát? Ez valóban nagy gond lehetne, de valójában nem kell t61e tartanunk. A fordítóprogram elég okos ahhoz, hogyemlékezzen az össze dinamikusan kezelt objektumra, így biztosak lehetünk benne, hogy az objektumtömb valamennyi eleme törölhető, és programunk vissL1kapja az általuk lefogla lt memóriát. Hogy megbizonyosoclhasSlmk err61, módosítsuk egy kicsit a 155. Listában bemutatott kódot. Először is változtassuk meg a tömb méretét 500-r61 IQ-rc a 24., 27. és 35. sorokban. Aztán vegyük ki a megjegyzésjelct a 19. sorban láthat6 cout utasítás elől. Amikor a program futása a 39. sorhoz érkezik, a tömb már nem létezik, a rendszer meghívta az abban tárolt valamennyi CAT ohjektum deslruktorál. Amikor a dinamikus memóriában létrehozunk. egy objektumot a new operátorral, azt a delet e utasítással lehet onnan törölni, amit az elem neve követ. Ehhez teljesen ha-
282 l iV. rész • Halad6 eszközök son!óan ha a new [l utasítással hozunk létre egy objekmmtömböt, akkor azt a delete [J utasítással lehet törölni. Az üres :z.'lr6jelpár jelzi a fordítónak aZl, hogy in egy egész tömb törlésé ről van szó. Ha véletlenül kifdejtjük a zárójeleket, akkor a mcrvelet csak az objektumtömb első elemét fogja törölni. EZl szintén beláthatjuk ~ kísér1elileg~ is, ha a 15.5. Lista 39. sorábó] eltávolítjuk a megje~,'yzésjeleL Ha ezzel párhuzamosan átszerkesztjük a 21. sort is úgy, hogya destruktor a képernyőn jelezze, ha lefut, akkor láthat6, hogy csak egy ilyen jelzés jelenik meg, vagyis csak cb'Y CAT objektum tÖrl6dÖtt. Sz6val gr..ltulálunk! Épp most sikerültl étrchozni cgy mcmór;akczclési hibát (memory leak).
Helyes
Helytelen
Ne felejtsük e l, hogy egy n elemű tö mb elemei O-lól n-J-ig sz5moz6dnak. Tö mbö k indexelését csa k olyan mutatókon keresztül végezzük, amelyek valóban az adott típusú tömbre Ilmtatnak.
Ne próbáljunk egy tömb vége után írni, vagy onnan olvasni. Ne keve rjük össze a tömböt dmző muta tót a mlllat6 tÖmbbe l.
Karaktertömbök A kar.lktcrl:'inc ne m egyéb, mint karakterek sorozata. Eddig C.'i upán névvcl nem rcndelkező 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 ', ' W' , ' o ', ' r' ,' l ' , ' d ', ' \0 ' } ;
'l' ,
'l ',
'o ' ,
'
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++ biztosít egy sokkal kézenfekvőbb lehetőséget a karakterláncok megadás."ira : char Greeting[) "
"Hello World" ;
15, 6ra • Tömbök 1283
Ezzel a szintaxissal kapcsolatban két dologra szeretnénk fölhívni a figyelmet: • Az előző módszt:rlől eltérően itt nem egyszeres, hanem keltős idézőjeleket kell használni, a vessz6k és a kapcsos zár6jelek pedig nem kellenek. • Nem kell külön kiírnunk a záró null karaktert, az II fordítóprogram automatikusan beilleszti a karakterlánc végére. A "Hello World n karaktedánc 11 bctűb61 áll , tehát 12 bájt hosszúságú. A Hello 5 b{ljtot tesz ki, II sz6köz egyet, a World újabb ÖlÖl, a záró null kardkter pedig egy bájlOS.
Deklarálhatunk inicia1i7..áci6 nélkül is kar:.lktertömböket. Ezeket szokás puffereknek is nevezni. Mini ffiindtm lömbnél, természetesen ill is ügyelnünk kell arra , hogy nem a karjuk több karaktert elhelyezni egy ilyen lárban, mint amekkora a mérete. Az inici:llizáció nélkü ! dck!arálL karaktertömbük használatára mU lat példát a 15.6. Lista.
15.6, Usta - Egy karaktertömb feltöltése (arrayfilled.cpp) O; /1 15 . 6 . Lista KaraktertOmbOk pufferkánt való használata l ; linclude
"
3; 4, 5; 6: 7: 8: 9: 10 :
i n t main () { char buffer[801 ; std : : cout « "Enter the string ; ' : std : : cin » buffer : std : : cout « " Here ' s the buffer , • « return O; }
buffer «
std : : endl;
Kimenet Enter the string : Hello Wo r l d Here ' s t he buffer : Hello
Az S. sorban létrehozunk egy puffert, amely 80 karAkte rt képes tárolni . Ez azt jelenti,
hogy ebben a tömbben egy legfeljebb 79 bettls szövegel helyezhetünk el, hiszen kell egy bájt a lezár6 null karaktcrnek is. A 6. sorban bekérunk a felhasznál6tól cgy szövegel, amit a 7. sorban el is helyezünk imént kialakított pufferben. A cin automatikusan elhelyezi a karakterlánc végét jelz6 null karakte r, ezzel tehát nem kell tör6dnOnk.
37..
A 15.6. Listában bemutatott kóddal két probléma is van. El6ször is nem tudjuk, hogy mit fog gépelni a felhasználó. Ha 79 karakternél többet ad meg, akkor a puffer túlcsor-
284 1IV. rész' Haladó aszközök dul, hiszen a c in nem érzékeli a végél. A másik probléma, hogy ha a felhasználó szóközt is gépel, akkor a cin azt fogja a bemenet végének tekinteni , és a maradékot be sem írja a pufferbe.
Ezeknek a problémáknak a megoldásra kicsit át kell alakítanunk a programot: a cin egy speciális metódusát, a get () -et kell használnunk. A c i n . get () három paramétcn vár bemt!nelké nl: •
A kitöltcnd6 puffer nevét
• •
A beolvasni kívánt karakterek legnagyobb számát Azt a jck:t, amit a bemenet végének kell tekinteni
Az alapértelmezett "bemenet vége jel az újsor. A módosított kód a 15.7. Listában látható. 15.1. Lista - Karaktertömb feltöltése (második változat) larrayfilled2.cpp) o : 111 5 .7. L i!l t ól A ci n . get (} met6dus ha szná léltv. l : #i nc l udc 2. 3 : int ma in() 4: 5: 6: 7: 8: 9:
I char buffer[80] : std : : cout « " Enter the st r ing : "; std :: cin . get!buffer, 79) : II get up to 79 or newlinú /ltd : : cout « "Here ' s the buffer : • « buffer « std : : endl ; return O;
10 :
menet Enter the sLri ng : Hello World Here ' s the buffer : Hello Wo r l d
A ci n osztá ly ge t () rnetódusát a 7. sorban hívjuk meg. Ennek az első argu rnenmma az a puffer, amit az 5. sorban deklarálrnnk. A második bemenő paraméter a bekérhet6 karakterek maximális száma . Esetünkben ez 79, mivel a pufferben így még é ppen elfér a záró null karakter is. A bemenet végét jelző karakte r megadására itt most nincs szükségünk, mivel az alapértelmezett újsor erre a célra éppen megfelel. A cin-ről és annak mindenféle különlegességér61 majd a 21. órában sz6lunk részletesebben.
Az strcpyO és az stmcpyO függvények A C++ örökölte a C nyelvtől a karakterláncok kezelésére szolgáló függvényeket. A számos lehetséges mO"velet közül iti csak kettőt szeretnénk kiemelni, amelyek karakterlán-
lS.6racok másolásra szolgálnak. Az c&'Yik az strcp () fijg&'Vény, a másik az s t rncpy ( ) . Az strcpy () működése viszonylag egyszenT: veszi a megadott kamkLerláncot, és a teljes tartaImát átmásolja a szintén megadott pufferbe. Használatát a 15.8. Lsta szemlélteti.
15.8. Lista - Az strcpyll függvény használ... (..ingstrepy.epp)
o:
1/ 15 . 8 . Lista - Az s trcpy() fo.ggvény használata l : #include 2 : #inc l ude
)
.
4: int main ()
5: { 6. chnr Stringll] = "No man is an island"; 7: char String2lBOJ ;
,.
9 . strcpy (String2 , Stri ngl) ; 10: « ll : s td : :cout « · string1 :
12: std : : cout « l): return O; 14:
"String2 :
«
Stringl « Stri.ng2 «
std :: endl ; std: : endl ;
JGmanot Stringl : No man iy an island String2 : No man is a n island
A 2. sorban beemeljOk a STRING . H fejlécállományt, amely tartalmazza az strcpy () függvény prototípusát. Az strcpy () bemenetként két karakte rtö mböt vár, melyek közü l az első il másolás c;(:lja, és a második a forrlIs. Ügyeljünk amI is, hogy ha a forrns
több adatot tartalmaz, mint nmennyi a célpufferben elfér, akkor az strcpy () en túlírja azt.
egyszerű
Ennek a lchel,>éges hibának a kiküszöbölésére a szabványos könyvtár tartalmaz egy strncpy () nevű változatot is, amelynek a másolni kívánt kamkte rck maximális számlIt is mcg lehet adni. A fü ggvé ny az első null karakterig másol, vagy nmíg el nem é ri ezt az e l ő re rögzíLeu számot. Az strncpy () függvé ny használa tM szemlélteti a 15.9. Lista .
15.9. Lista - Az strncpyO függvény ha""lata (usingstrncpy.epp)
o: II 15 . 9 . Lista Az strncpy() fo.ggvény használata 1 : öinc1ude 2 : *lnclude ) .
4 : int main() 5: {
6 : const int MaxLength : 80; 7 : char Stringl [] : "No man is an island";
I
286 IV. rész • H~ad6 eszkÖ2Ök 8 , char String2[MaxLength+l1; 9.
10 : strncpy(String2,Stringl,MaxLength); ll: 12 : 13 : 14 :
String2[strlen(Stringll] std : : cout «'Stringl : s t d :: cout« 'String2 : return O;
' \0'; II add a null to the end String! « std : :endl; «String2« std : : endl;
Speak(l; 70: } 71 : 72 : void RefFunction (Mammal & rMammal) 7]: { 74 : rMammal . Speak (); 75 : )
Kimenet (l)dog (2) cat (O)Quit : 1 Woof Woof MilIMlal Speak! (l) dog (2lcat (O)Quit : 2 Meow! Meow! Hammal Speak! (ll dog (2)cat (O)Quit : O
322 1V. rész· Öröklődés és többalakúság
A 3-23. sorban az em lős, kutya és macska oszlályok leeb'Yszerusíleu változatait deklaráljuk. Három függvényt is deklarálunk, PtrFunc t ion (). Re fFun ct i on (), és Va lue Func tion (l nt'!ven. Ezek egy eml6s mutatót, e ml6s hivatkozást illetve eb'Y eml ős objektumot várnak par-améterkénl. Mindhárom függvény ugyanntteszi: meghívja a Speak () metódusl. A fel hasznflló választhat kutya és macska közötti választásának megfelel6en készül el egy mulató a megfelelő típus!".! a 38-52. sorban. A kimenet első sora szerint a felhasználó a kutyát v{L!aszlja. A kutya objektum a dinamikus mem6riatertlleten készül el ti 44 . sorban. Ez a kutya azlán mut;ltóként, hivatkozásként és érték szerint keiii i átadásra Speak () függvény kelill 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 (err61 tanúskodik a paraméter-szignatúrája), így a fordítóprogram lccsonkítja a kutya objektumOt llZ emlős . részére". Ily m6don Purr() ; else cout«
'Húha,
6í! :
delete Zoo [ i] ; cout « "\ n";
63 : 64 :
65 : 66 : 67:
rcturn O;
68 :
IGmenet (I)Kutya (2)Hacs ka : l
Hammal (eml6s) konstruktor. Dog
(kutya)
konstru ktor . . .
ez nem
macska!\n';
334 1V. rész • Öröij6dés és többalakúság (llKu t y a
(2) Mac!lka : 2
Hammal (em16s1 kons truktor .. . Cat (macska) konstru ktor .. . (l)Kutya (2)Macska : 1
Mammal (emlos) konstruktor ... Dog (kutya) konstru ktor ...
Vaú ! Húha, ez nem macska ! Hammal (cm16s) destruktor ..
Mijáú rrr r r rrrrrr Hammal (em16s1 deHtru ktor ... Vaú ! Húha ,
Mamma l
EI.
~Z
n em maCSkll !
(em16s) des t ru kt o r .
S
A 38.·48. sorokb:Ln :l felhasználó választása szerint adunk hozzá Cat vagy Dog objekrumokat 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 tagfüggvények többaJ:.kúként viselkedve különböz6 eredményekkeltérnek vissza: A kutyá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 Ezután a Rectangle osztályból tovább származtathatjuk a Square (négyzet) osztályt, nunt a négyszögek egyik speciális képvisel6jél. Mindegyik sZiÍ rmaztatOlt osztály felüldefmiálja a Draw () (rajzo!), és a getArea () (teruletszámítás) metódusokat és még másokat. A 18.3. Lista a Shape osztály lecsupaszíton megval6sítását mutatja a szánnaztaton Cir c1e és Rectangle osztályokkal.
18. óra • A
kifinomult használata 335
18.3. Ut.. - SIkldom autítyok (.h.pecl....cpp) O, 1, 2,
II 18 . 3 . Lista - Sikidom osztályok lincludc
3:
class Shape
4: 5, 6:
( public : Shapc() (l
7: 8: 9: 10 :
virtual virtual virtual virtual
11 :
);
12 : 13 :
class Circle
-Shape(){) long GetArea() { return -1 ; } II hiba long GetPcrim{) { return -1; } void Draw() {)
public Shape
14 :
15 : 16 : 17 : 18 :
19 :
20 : 21 :
22 :
public : Circlc (int radius) : i tsR!!Idius {radiusl (l -Ci rel e () {}
long GetArea{) ( return 3 • itsRadius long GetPerim() void Draw ( ) ; privata: int i tsRadiu$ ;
23 :
{ return 9 * itsRadius;
itaRadius ; }
int itsCircumference ;
24 :
};
25 : 26 : 27 : 28 : 29 :
void Circ!e: :Draw() ( std: : cout « "KOr rajzolását végz6 utasítások helye! \n";
30 : 31:
32 :
class Rectangle
33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42: 43 : 44 : 45 : 46 : 47 : 4B : 49 : 50 ,
(
public Shape
public : Rcctangle(int len, int width) : itsLcngth (len), itswidth(widt h) () virtual -Rec tangle{) {} virtual long Ge tArea () ( return itsLength * i tsWidth; ) virtual long GetPerim() (return 2 *i tsLength + 2 *i tsWidth; v irt ual int GetLength{) ( return itsLength; virtual int Gctwidth() { return itsWidth; } virtual void Draw ( ) ; private : int itsWidth ; int itsLength ; ); void Rectangle : : Draw() for (int i
='
O; iEat () ; pAnimal->Reproduce() ; pAnimal->Move() ; pAnimal->Sleep() ; delete pAnimal ; std : : cout « "\n"; return O;
140 :
menet (l) Ku tya (2) L6 (3)Hal (O)KiIópós: 1 AnimaI konst r ukto r . Mamma! konstruktor . Dog konstruktor ... Vaú ! ...
A kutyák étkezési szokásai ... A kutyák szaporodása . A kutya futni szokot t . . . A kutyák a lvás i szokásai . Dog destru k tor ... Mamma! destru ktor . . . AnimaI destruk tor . . . (1)Oog (2)Horse (3)Bird (O)Quit: O
349
A 6-20. sorok közön adtuk meg az Animal osztály, mint elvont adattipus meghatározásál. Tartalmaz egy i tsAge () (kora) nem űres virtuális függvényt , valamint öt másik ürese!: Sleep (l, Eat (), Reproduce (), Move (), és Speak (). A Hammalosztály az Animal-b61 származik, a 28-36. sarok között található a meghatármása. Nem hoz létre új tulajdonságokat, de felüldefiniálja a ReprOduce () függvényt, általános formában leirva a sznporodásl az összes eml6sre vonatkozóan. A Fish osztálynak (elül kell clefinb'ílnia :l Reproduce () tagfüggvényt, mivel az közvetlenül az Animal OSZtá lyból szá rmazik, és nem tudja felhasználni a Mamma! osztály szaporodására vonatkozó !llliveleteket (ami egyébként így helyes). A Mammal-b61SZ!lrma7.Latoll osztályoknak nem keJl felü ldefini5lni II Reproduce () függvényt, de meglehetik, ha afra nn szükség (mint a Dog osztá ly esetében a 95. és 96. sorban). A Fish, a Horse és Dog osztályok mind felüldefiniálják az összes maradék tagfüggvényt, amelynek következtében ezek az oszt,1]yok példányosíthat6k. A program tönsében egy Anim!lltípusú ml..nat6t használunk a különböző szá rmaztatott objektumok e lé résé hez. Amikor a virtuális ragfüggvényckcl meghívjuk, a futásid6beni hozzárendeléseknek megfelel6en mindig a kívánt osztályok tagfüggvényei futnak le. Az AnimaI és M!lrnrMl osztálYOk példányosítására teu kísérlet fordítási hibához vezetne, mivel mindkett6 elvont adattípus.
Mely trpusok elvontak? Az egyik programban az Animal elvont oszttily, a másikban nem az. Mi hat5rozza mcg azt, hogy mikor kell egy oS7.tályt elvontként meghatároznunk? A kérdésre adott válasz nem a val6 világ valamely lényeges tényezője 2. 3: *pFunc) delete ptr;
81 : 82 : 83:
return O;
(J ;
(J)horse:
";
20. 6ra •
menet (O)Qu i t (1) Dog (2)CaL (1)Speak (2)Move : 1 Woof! (O)Quit (1)oog (2)Cat (1)Speak (2)Move : 1
(3}Horse : 1
(3)Hor sc :
2
(3)!-!orse:
3
(3)Horse :
o
Meow! (O)Quit ( 1) Oog (2)Cat ( 1 )Speak (2)Movc : 2
Galloping (O)Quit
(1)Oog
(2)Cat
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étrejön 11 mum6riában az AnimaI-nak megfelel6 ~J[:l!oszt{lly, és címe bukerül ti ptr nmtmóba a 49-63. sorban. Ezután eldöntheti a felhasz.náló, hogy melyik metódlIst szerelné meghívni. A döntésének megfele l ő metódus címe bekerül ·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-megadással, vagy esetleg egy statikus tagfüS&",ényen keresztüL A statikus adatok típusa az osztályhoz kÖl6dik, és a szigorítou elérés, illetve :I Z erds típusoSs{lg miatt b iztonságoslibban használh:Hók, mint a globálisak. Kérdés: ANér/ "aszt/álnúnk slaukus lagfüggvényel-.:cI, lIa lehet glo/:Jálisakal is !taszllál"i? Válasz: A statiku.s lagv:i lloz6k egy osztály hatókörébe tm1.07;nak, és Csak az adott osz-
t(lly objektum{m keresztül érl1et6ek el, vagy kifejezett és teljes osz!álynévvel, mi nt példú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 la7.ítja az egységlx:zárást. A cél :lZ, hogy az osztályok megvalósításának a részletei lehet6leg 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 világával. Yíllaszoljunk meg néhány kérdést és végezzünk cl nC:hány fel::tdatot, hogy el1l1élyíL itsLen) return itsString[itsLen - 1 ]; else return itsString[offset) ; ASSERT(Invariants(); )
AnimaI : : Anima1 (int age , const String& name) : 199 : itsAge( age),
200: itsNallle(name) 201 : {
21 , 6ra • kl 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 : 218 : 219 : 220 , 221 :
std :: cout«' is ' I std :: cou t «sparky . GetAge() «
AGE:
5
• years old . ' ;
spnrky . SetAge (8) ;
std: : cout « std :: cout « 222 : std :: cout « 223 , return Oi 224 :
' \n " « sparky .GetName( ) . GetString() ' is' , sparky .GctAgc() « • years old ."
(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 .
i
431
432 1VI. rész • Különlegességek
Apropó
A fordftáskor figyelmeztető üzeneteket kaphatunk
Ennek a kódnak a fordítása közben is felb ukkanhatnak ugyanazok a figyel meztető üzenetek, a melyekről a 21.4. Lista kapcsán már volt szó. Az. ok természetesen ugyanaz.
Elemzés Az assert () makró k6dját a 8-18 sorok tartalmazzák. Ez ebben az esetben úgy van kiahtkí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 szinten is hatást.dan , miközben ezeken aszinteken EVAL és assert () már ml1ködnek. A PRINT makrót az InvarÍcl. Ha egy Ozenerobjektum elkészült, a konstruktor munkája véget ér. A szerkesztő met6clusnak a ko nstruktorból való meghívása c5 theOtherObject.myValue) kleLarger; kIsSame ;
52 :
53 : II Egy másik osztály, amit szintén a listában kívánunk tárolni II Ebben a láncolt listában is a korábban említett két met6dusra II lesz szakség : 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 : { std : : cout « myAge « " éves macska tOrlése\n" ; 66 : 67 : 68 : i n t Compare (const Cat &) ; 69 : void Show() 70 : 71: std , : cou t « "Ez a macska': std : : cout « myAge « • éves\n": 72 : 7] : 74 : private: int myAgű ; 75: 76 : } ; 77: 78 , 54 : 55 : 56 : 57 :
1 23, óra • 79 : II A Compare met6dust arra használjuk , hogy egy adott objektum 80 : II listában elfoglalt helyét megállapits uk . 81 : B2 : B3 : B4: BS : 86 : 87 : 88 : 89 : 90:
int Cat : :Compare (const Cat
&
theotherCat )
if (myAge < theO therCat . my Age) return k IsSmaller ; i f ImyAge > theO therCat .myAge) return kIsLarger; else return kIsSame;
91: 92 : 93 :
94 : 95 : 96 : 97 : 98 : 99 : 100: 101: 102: 103: 104: 105: 106 : 107 : lOS : 109 : 110 : lll :
112: ll3:
II A liűta táro16objektumának absztrakt adattipusa II Minden származtatott tipusnak falűl kell defint~lnia
Sz Insert
.. és Show met6dusokat tcmplate class Node { public : Node () () virtual -Node I) I) virtual Node ~ InsertIT· theobject)zO; virtual void Showl) = O; private : };
ternp1ate class Interna lNode : public Node public : InternalNode(T * theObjcct , Node • next); -Interna1Nodel){ deleta n~Next; delete myObject ; virtual Node • InsertiT · theobject) ; virtual void Show ( ) (
114 : myObject - >Show(): 115 : myNex t->Showl); 116 : II delegálás 117 : private : US : T • rnyObject ; II Maga az objektum 119 : Node * myNex t ; II a lista kOvet kez6 t áro16e l emére muta t 120 : ) ; 121 : 122 : II A konstru ktor csak inicializál 123 : tcrnplate 124 : InternaINode:: InternalNode IT * theObject, Node * nex t) : 125 : rnyObjectltheübject),myNext(next) 126: ( 127 : ) 128 : 129 : II A lsita velej e 130 : II Amikor a l i s t ába új objektumot tes zü n k, 131 : II átadju k a táro16elemnek , amely ki ta l álja
470 11n, rész • Kiilönlegesség.k 132 : /1 a helyét. és beszúrj a a listába. 133 : templat c 134 : Node ~ InternalNode : : Insert(T * theObject) 135 :
136 : 137:
/1 Az új szerzet kisebb vagy nagyobb nálam? i nt rcsult = myOhject->Colnpare(*thcObjec t) ;
138 :
139 : 140 : 141 :
14 2 : 143: 144 : 145:
switch(result) { 1/ 11. tradició szerint, ha egyen16ek vagyunk, övé az els6bbség. case kisSame : / I {gy ez az ág átvezet arra az esetre, .. amikor nagyobb nálam case kisLarger: 1/ Az új objektum elém kerül ( IntcrnalNode • ObjectNode =
146 :
new InternalNode(thcObject , t his):
147 :
return Objcc t Nodc l
1.48 :
149 : II Nagyobb nálam , tehát adju k át a kOvetkez6 táro16elcmnck 150 : 151 : case kisSmaller : 152 : myNext = rnyNext->Insert(thOObjcct) ; 153 : return this; 154: 155, return this; 156, 157: 158 , II A Tail tárolóelern csak őrszem szC!repet játszik 159 , template 160: class Tai1Node : public Node 161 : ( 162 : public : 163: TailNoda(){} 164 : virtuel -TailNode(){} 165 , virtual Node • Insert (T .. theObject) ; 166 : virtual void Show() { } 167 : private: 168 : }: 169 : 170 : II Ha az Object típusú objektum hozzám kerül, mindenképpen elém 171: /1 lesz beszúrva, hiszen én vagyok a zár6elem , .. aki IllOgOt t már semmi sincs. 172 : templat e 173 : Node • TailNode :,Insert( T .. LheObject} 174 :
175 : 176 : 177: 178 :
Interna1Node • ObjectNode = new InternalNode(theObject, this); return ObjectNode;
179 : 180 , II A kezdd táro16elemben nincs objektum,
181 : 1/ csupán a lista 1ege ls6 el emére mutat 182 : templat e 183 : class HeadNode : publ ic Node
23. óra • Sablonok 471 184 : 185 : 186 : 187 : 188 , 189 : 190 : 191 : 192 : 193 : 194 , 195: 196: 197 , 198: 199: 200 : 201 , 202 : 203 : 204 , 205 : 206 : 207 , 208 : 209 , 210: 211 : 212 : 213 : 214 , 215 : 216 , 217 : 218 : 219 , 220 : 221 : 222 : 223 , 224 : 225 : 226 , 227 : 228 : 229 : 230 : 231 : 232 : 233 : 234 : 235 : 236 : 237 :
public: HeadNode(); virtual ~HeadNode() { delete myNext; } virtual Node * Insert(T * theObject) ; virtual void Show{) ( myNext~>Show() ; ) private: Node ~ myNcxt; }; / / Mihely 16trejön ti kezd6elem, / / az nyomban maga mögé teszi zár6elemct is template HeadNode: : HeadNodc() { !lIyN!:lxt : new TailNode ;
/1 A k ezd6elem el6tt nincs semmi, :így az objektumot II rögtön továbbadhatjuk a k övet kez6 táro16e l emnek . template Node * HeadNode :: Insert (T • theObject) ( myNext : myNext - >Insert(theObject) ; return this;
II Minden munkát delegálok, de enyém a dics6ség template class LinkedList
{ public : LinkedList(); -LinkedList() { deiete myHead ; } void Insert(T ~ theObject) ; void Showl\l1() ( myHead->Show() ; private : HeadNode * myHead ; }; II Születésemkor létrehozom a kezd6eleme t . a mely maga mögé II h e lyezi a zár6elemet . II Az üres lista tehát egy kezd6e lcmb6l áll . amelyet azonnal a /1 zár6elem követ , köztük pedig ninc s semmi . template LinkedList: :LinkcdList() ( myHead = new HeadNode ;
1/ oelegálj , delegálj, delegálj template void LinkedList :, Insert(T * pObject) {
472 1VI. rész • KilI6nl'llosségek 238 : 239 : 240:
myHead->Insert(pObject ) ;
241 : 1/ Tesztprogram 242:
243: 244: 245 : 246: 247 :
int main ()
• pCat; Data * pData; int val ;
C~t
Li nkedList
ListOfCats ;
248 : 2 4 9: 250: 251:
LinkedList ListOfOata:
252 253 254 255 256
for (;;)
: : : : :
1/ a felhaszná16t61
bekérűnk
pár értéket
1/ és elhelye2:zük azokat II l i stában { std :: cout« "Adj meg' egy értéket std :: cin »va l; i f (ival) break: pest ~ ncw Cat(val); pOata: new Data (val) ;
257 : 258: 259:
260: 261: 262: 263 :
(O vége):
";
ListOfCats.lnsert(pCat); ListOfOata.lnsert(pData):
264 : II végigmegyOnk a listán és kiírjuk az e l emek értékét . 265: std: : cout « ' \n ' ;
266: ListOfCats.ShowAll(); 267: std: :cout «
"\n",
268: ListOfData.ShowAll(); 269 : std :: cout « "\n ****,,***." •• \n\n"; 270: return O; II A listák kikerülnek a hatókOrb61 , és megsemmisülnek. 271:
Adj Adj Adj Adj Adj
meq meg egy meg egy meg egy meg egy Adj meg egy E, a macsk.!! a macska E, a macska a macska E, a macska
"'
"'
"""
értéket é'rtéket értéket értéket értéket értéket 2 éves 5 éves 7 éves 9 éves 13 éves
(o (o {o {o (o (o
vége) vége) vége} vége} vége) vége)
: : : : : :
5 13
2 9 7
O
Data objektum törlése, melynek é rt éke: 13 Dato objektum t ö r lése, melynek é rtéke : 9 Data objektum törlése, melynek értéke: 7
23. óra • Sablonok 1473 Data objektum törlése, melynek értéke: 5 Data objektum törl ése, melynek értéke : 2 13 éves macska t órlés e 9 éves macska t Or lése 7 éves macska törlése 5 éves macska t ö dés e 3 éves macska tOrlé se
Mindenekelőtt,
vegyük észre, mennyire hason lít a 23.1 Lista a 19. órán bemutatOllhoz.
A legnagyobb változás, hogy minden osztály- és metódusdcklaráci6 el6tt a következ6 preAx áll: Lempl ate c 1ass
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 Node
Eszerint :1 Node osztály önmagábrtn nem létezik, de példányosítani fogjuk a Cats és a Data oszlállyaL A paramélCrczeshez használt típust T jelöli. Hasonlóail, az Interna lNode helyett most InternalNode áll (T típus InternalNode - ja). Továbbá, InternalNode nem egy Data objeklumra es egy másik Node-ra mutat, h.mem egy T típusú objektumra és egy Node objektumm. Ez lámató a 118. és a 11 9. sorban. Nezzük meg alaposabban a 133-156. sorban definiált Insert mel6duSl. A logika ugyanaz, de ahol korábban konkrét típust (Data) használrunk, Olt most T szerepel. Tehát, a 134. sorban a paraméter mulató egy T típusú objekrumra. Később, a példányosít{\skor, a fordító T helyére behelyettesíti II megfelel6 típust (Data és Cats). A lényeg, hogy az Internal Node az adattípustó függetlenül elvégzi a feladatát. Tudja, hogyan hasonlílSa össze az objektumokat. Egyáltalán nem érdekli, hogy a Cats objektumok pontosan úgy hasonlítják össze egymást, mint a Data objekrumok. Valójában, a Cats osztályt újraírhatjuk úgy, hogy a kort nem tárolja, hanem a születési id6ból szükség szerint kiszámítja. Az InternalNode-ól ez egy cseppet sem érdekli.
474 1VI. rész • Külön~g.sség.k
Sablontípus használata A sablontípus úgy használh:tt6, ahogy a többi típus. Átadható függvénynek paraméte rk~n\, de lehet vis.~zaté rési érték is, mindkét esetben énékkel vagy referenciávaL Ezt mutatja be a 23.2 Lista. Hasonlítsuk össze a 23.1 Listával!
23.21J1111- Pa_1Isto ~......... (16aCOIt- _ _ 6YoI1
f--.
l
1 : II 23 . 1 példa
2 : II 3 : /1 Paramétere2ctt lista bemutatása 4: / I
5 : II 6 : II 7, / I 8, / I 9: II 10, II 11: II 12, II 13 : /1 14 : II 15 : II 16 : II
Paraméterezctt lista objektum-orientált megva16s1tása. A List az absztrakt Node táro16elemnek delcgálja a munkát.
Háromf61c táro16elemet használunk: kczd6t. zár6t és kOzba!s6t. Csak ez utóbbiban tárolunk hasznos objektumot. A Data osztályt azért hozzuk létre , hogy példányait a listában tároljuk.
II 18 : II **** ************** ********* **.**** ****** -******
17 :
19 : 20 : 'inc1ude 21 : 22 ; enum { kIsSma1ler. kIsLarger , kIsSame} ; 23 : 24 : II A listában tárolható Data osztály 25 : II Minden olyan osztálynak, melyet ebben a listában szeretnénk .. tárolni , rendel kezni e kell a kOvetkez6 két metódussal , 26 : II Show : Kiírja az objektum értékét. 27 : II Compare: OsszehasonH tja két objektum értéké t, és meghatározza _ a relatív poziciót . 28 : class Data 29 :
30 , 31 , 32 : 33 : 3 4: 35 :
public : Data{int val),myValue{val){) -Data () ( std : : cout « 'Data objektum tOrlése, melynek értéke: '; std: : cout « myVa 1ue « '\n ' ;
36 :
37 : 38 ,
int Compare(const Dat a & ) ; void Show() ( std : : cout « myValue «
std : : endl ; )
-
- --
-
-
-
-
23. óra • Sablonok 1475 - - - -- -- --=-=---==""'-''----------' ..
39 : prlvate : 40 : int rnyValue ; 41 :
J;
42 :
43 : II A Compare met ódust arra hasz ná ljuk, hogy egy adott objektum 44 : II listában el f ogl al t helyét megá l l apit suk . 45 : i nt Da t a : : Compa re (const Data & t h e OtherObj ect ) 46 : 47 :
48 : 49: 50 : 51 : 52 : 53 :
{
i f (myVa l ue < theOthcrObject.rnyValuc)
return kIsSmaller ; if (myValue > theOtherObject . myValue) retu r n kIsLarger ; else return kIsSame; l
54 :
55 : 1/ Egy másik osztá l y , ami L szint én a listában kívánunk tárolni 56 : II Ebben él láncol t lisLában i s a korábba n említett ké t met6dusra 57 : II l esz
szű k ség :
58 : /1 Show: Kiírja az objektum értékét . 59 : /1 Compare : Összeha sonlítja két objektum értékét, ... és relatív poziciót ad vissza . 60 : 61 : class Cat 62 : ( 63: public : 64 : Cat(int age) , myAge(ag-e) {} 65 : -Cat() 66 : ( std : : cout « !nyl\ge « 67 :
r.~l ~:;~:
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
146 : 147 :
case kIsLarger : /1 Az új obj ek tum e lém ke rul ( InternalNode * ObjectNode : new InternalNode (theübj ect, this) ; return ObjectNode;
148 : 149 :
/1 Nagyobb nálam. tehát adjuk át a következ6 táro16elemnek
143 :
144 : 145 :
150 :
case kIsSmal1er:
151 :
myNext = myNex t->Insert( t heübject) ; rcturn this ;
152 : 153 : 154 : 155 : 156 : 157 :
rcturn thic ;
158 , 159 : /1 A Tail táro l óe l em csa k úrszem szerepet játszik 160 : temp] ate
161:
class TuilNode : public Node
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 174 , Node ~ Tai1Node :: Insert(T • thCObject) 175 : ( 176 : Interna1Node • ObjectNode ~ 177 : new Interna 1Node(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 18 4 : c l ass HeadNod e : p ub l ic No de 185 : 186 : p ublic : 1 67 : Head Node{) ; 168 : virtual -HeadNode() { d e lete myNext; } 169 : virtual Node .. I nsert {T * t heübject) ; 190 : virtual void Show () ( myNe xt- >Show () ; } 191 : private : 192 : Node * myNe x t ; 193 : }; 194 ,
1477
I
478 VI. rész • KlIlönl.g.sség.k 195 : 1/ Mihelyt létrejön il kezd6 elelll, az nyomban 196 : 1/ maga mögé teszi zár6elemet is
197 : template 198 : HeadNode : : HeadNode (J 199:
(
200 :
myNext '" new TailNode;
201 : 202 : 203 : II A kezd6elem e16tt nincs semmi, igy az objektumot rOgtOn 204 : II továbbadhatjuk a következő táro16clemnek. 205 : template
206 : Node • HeadNode :: Insert(T " theObject) 207 , { 208 :
myNcxt ., myNcxt->Inscrt (thcObject) ;
209 : 210 :
return this ;
211 : 212 : II Minden munkát delegálok, de enyém a
213 : 214 : 215 :
template elauG LinkedLi ot
216 :
public :
217 : 218 :
LinkedList() ;
219 :
void Insert (T " theObject) ;
220: 221 : 222 : 223 : 224 :
void ShowAll() ( myHead->Show() ; private:
~LinkedList()
HeadNode
~
diCsőség
( delete myHead; )
myHead;
);
225: II SzOletésemkor létrehozom a kezd6elemet,
am~ly
226: II helyezi a zár6elemt. 227: II Az Ores lista tehát egy kezd6elemb6l áll . 228 : II záróelem kOvet. kOztOk pedig nincs semmi.
maga mOgé
am~lyct
229: templat~ 230 : LinkedList : : LinkedList() 231 : ( 232 : myHead =: new HeadNode ; 233 : 234 :
235 : /1 Oalogál j . delegálj , delegál j 236 : 237 : 238 : 239 : 240: 241 : 242 : 243 : 244 :
tcmplatc void LinkedList : : Insert( T * pObject) ( myHoad->Inser t (p Object) ;
void rnyFunction(Lin kedList& ListOfCats); void myOtherFunc tion (LinkedList< Data>& ListOfData);
245 : II Tesztprogram 246: 247 : 248 :
int main{) LinkedList
ListOf Cats ;
azonnal a
23. óra • Sablonok 479 249 : 250 :
Lin~edList
251 :
myFunction(Lis t OfCats): myOtherFunction(ListOfData);
252 :
ListOfData ;
253: 254 : 255:
256 : 257 :
256 : 259 : 260 :
II VégignézzOk a listát, és kiírjuk az elemek értékét. std : : cout « "\n" ; ListOfCats . ShowAll(); std : :cout« "\n" ; !>lsLOfData . ShowAll () : std::cout « "\ n *~**~**** .. *. \n\n"; return O; I I A listák kikerOlnek a hat6kOrb6l, és megsel1lllisülnek.
261 : 262 : 263 : void myFunction(LinkedList& ListOfCats) 264 : 265 : Cat • pCa t; 266 : int val; 267 : 268: 1/ A felhasznál6 adjon meg néhány értéket, 269 : II amit II listába teszünk 270: for l;:) 271: 272 :
273 : 274 : 275 : 276 : 211 : 218 : 219: 280: 281: 282 : 283 : 28-t : 285: 286: 287: 288: 289: 290 : 291 :
292 : 293: 294: 295 : 296: 297 : 298 : 299 :
{
std : :cout « " \nHány éves a macskád? (O vége): std: : cin » val; i f (Ivel) break: pCnt .. new Cat(val); ListOfCats . lnsert{pCat);
void myOtherFunction (LinkedT"ist& ListOfData) { Data .. pData ; int val; I I A felhasznál6 adjon meg néhány értéket, /1 amit II listába teszünk f or ( :; ) { std : : cout « 'Adj meg egy értéket (O vége) : ' . std : : cin » val; i f (!val) break ; pData = new Data{val); ListOfData . lnscrt(pData);
•
I
480 VI. ,ész' Külőnlegességek
Hány éves a macskád? Hány éves a macskád? Hány éves macskád? Hány éves a macskád? Hány éves a macskád?
,
(o (o (o (o (o
vége) v ége) vége) vége)
: 12 : 2 : 14 : 6
váge) : O
meg egy értéket (o vége) : 3 meg egy értékat (o vége) : 9 meg egy értéket 10 vége) : 1 meg egy értéket (o vége) : 5 meg egy értéket (o vége) : O
Adj Adj Adj Adj Adj
E,
a macska 2 évec E, a macska 6 éves
,
Ez macska 12 éves E, a macska 14 é ves LOrlése , melynek értéke : 9 Dat a ob jektum törlése , melynek é r té ke : 5
Da\.a objekt..um
Data objektum tOrlésc , melynek értéke : 3 objektum tOrlése , melynek értéke : 1 14 éves macska tOrlése 12 ével:! macska tOrlése Dl;ltu
6 éves macIJka tOrlése 2 éves macsk", tOrlésc
Ez a példa majdnem ugyanaz, mint az e16z6, azt leszámítva, hogyaLinkedList objeknunokat refcrendával adjuk át az azokal feldolgozó függvényeknek. Ez rendkívül fontos tulajdo nság. Miut:ín a listákat péld,ínyosítotluk, teljesen definiált típusú objckrumokként kezelhet6k: függvény paramétereként és visszmérési 6nékeként.
A szabványos sablon könyvtár A szabványos sablonkönyvtán (Standard Template Library - STL) a C+ + szabvány definiálja. Minden fordító , amelyről azt állílják, hogy szabványos, tartalmazza az STL-l . Az STL sablonnal megvalósílott tárol60sztályok, például tömbök, lisUík, sarok, vermek stb, továbbá számos közös algoritmus, például rcndez(:s, keresés stb. gytijteménye. Az STL célja az, hogy ezeknél a gyakran e l őforduló feladatoknál ne kelljen újra feltalálnunk a spanyolviaszt. Az STI.-t tesztelik és javítják, jó a teljesítménye, és nem utolsósorban ingyenes! Ennél is fontosabb, hogy az STI újrahasznosítható. Mihelyt megtanulluk és megértettük a használatát, minden programunkban alkalmazhatjuk, ahelyett, hogy újra és újra magunk megirnánk.
23.6ra • Sablonol1481 Mindig emlékezzünk rá, hogy a termelékenység és a az újrahasznosítás!
könnyű
karbantarthatóság kulcsa
Kérdések és válaszok Kerdés: Mié11 használjunk sablonl, amikor makróval is mego/dhatunk egyjeladatot? Válasz: A sablon!"... típuscllcn6r..:és vonatkozik, és integrált részét képezi a nyelvnek. Kérdés: Mi ti lu1 lő1lbség a paraméterezltetó jüggwllysabloll és a szokvtillyosjligsvény panl1néterei között? Válasz: Szokványos függvé ny (nem függvénysablon) előre megadott típusú paramétereken végez /Jniveleteket, A függvénysablon ellenben azl is lehet6vé leszi, hogya pammétereket paraméterezhessü k. Kérdés: M/I...'Or hasz náljunk sablol/t, és mikor öröklődést? Válasz: Használjunk s.1.blont akkor, ha a művel etek halmaza azonos, csak az oszclly másmá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, milyen 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 oldali 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 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 (vcgyü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 írnunk 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 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 dinamikusan hozta létre, a törlésük is a lista feladata. Ezt ot 23:1 Lista 110. soriiban láthatjuk .
24.
ÓRA
Kivételek, hibakezelés és néhány tanács Ebben az órában a következőkrőllesz szó: • Mik azok 1:1 kivételek • Hogyan használjuk a kivételeket, és használatuk milyen kérdéseket vel fe l • Hogyan í~llnk hibátlan kódot • Merre tovább
Programhibák. tévesztések. kódrnegromlás Könyvünk példaprogramjai illuszlrációs céllal születtek. Szándékosan kerültük a hibaforrások taglalását , hogy ne vonja el figyelmünker az éppen tárgyalt témáktóL Az éles programoknak azonban figyelniük kell a hibalehet6ségekrcj sőt, a valódi é l etből veU programkódok legnagyobb részét a hibákra való felkészülés és a hibakezelés teszi ki.
484 1VI. rész • Különlegességek Azt mondják, hogy ha a városok is úgy épülnének, mint a szoftverek, akkor az első arr.:!. járó harkály romba dömené a civilizációl. Mindenesetre tény, hogy szinte minden kereskedelmi program, köztük a legnagyobb gyártók termékei is tartalmaznak hibákat. Komoly programhibákat. Attól még, hogy ezt tudjuk, ez nincs így rendben. Bárkinek, aki komolyan programozásra adja a fejét, elsődleges fontossággal afnt kell törekednie, ho&'Y robusztus, hib;1 nélküli programokat írjon. Állítom, hogya szoftveripar legnagyobb és kiemelked6 problémáját a hibás, inswbil k6dok jelentik. Az mindenesetre tény, hogya program megírásának legkölcségesebb rés7.e a tesztelés, hibakeresés és a hibák kijavítása. Számos olyan apró hiba van, ami gondot okozhat egy progr.lIn futásakor. Az első a rossz logika: a program azt csinálja, amit a progr.HTloz6 szeretne; csakhogy (5 nem gondolta végig dég alaposan II mcgval6sítotl algoritmust. A második II szintaktika: rossz kifejezésm6dot, függvényt vagy struktúrát használtunk. Ez a két leggYllkoribb, és a legtöbb programozó ezek megt:ll~lására törekszik. Sokkal mvaszabbak azok :1 hibák, medyek csak akkor ütköznek ki, amikor a felhasználó valami váratlan dolgot múvel. Ezek a piciny, logikai tapos6aknák csendesen és zavartabnul meglapulhatnak. Amikor már úgy tűnik, minden rendben van, egyszer csak hirtelen - BUMM! - valaki rossz helyre lép, és progr.uTlunk megfeneklik. A kutatások és a gyakorlati tapasztalat egyaránt azt mutatjá k, hogya fejlesztés minél kés6bbi stádiumában kerül el6 egy hiba, annál drágább a kijavítása. A lego1csóbbak azok a problémák és hibák, amelyeket sikerül még a progmm megírásakor elkerülni. Csak alig drágábbak azok, melyeket a fordít6program kidob. A C++ szabvány afra tö· rekszik, hogy .1 fordít6progmmokb61 a lehet6 legtöbb hibajelcntést és fi gyelmeztetést kipréselje (még fordítási időben). A IcfordílOu programoknak azok a hibái sokkal olcsóbbak, amelyek rögtön az e l ső futtatáskor kideriiinek (amelyek mindig összeomlást okoznak), mim amelyek csak nag)' ritkán okoznak problémát. A logikai és szintaktikai hibáknál nagyobb gondot jelentenek az esetleges hibák; amikor például a programunk remekül múködik, ha számol ad meg a felhasznál 6, dc összeomlik, ha belűt ír be, Más progmrnok akkor fagynak le, ha kevésnek bizonyul a mem6ria, vagy ha kint felejtettilk a hajlékonyJemezl, esetleg ha a modem éppen bontja a vonalaI. A programozók az effajta törékenység ellen úgy küzdenek, hogy próbálják programjukat ~golyóállóvá ~ tenni. Golyóá1l6 az a progmm, amely minden futásidóben elképzelhet6 esetre fel van készítve, kezdve a bizarr felhasználói bemeneuől egészen a memóriahiányig. Ez a fajta programozás hasonlít a "defenzív vezelésre~, amely abból áll, hogy nem bízunk a másik sofőr (esetünkben felhasznál6 vagy rendszer) udvariasság:i-
_ __
_
_ _ _ _ _ _ _ _ _ _"'24"-"",6ra,,-0-"Kiv ..é...'."""I....~,.h""ib..,.k"eze=lé... s .., és"n..,éh..,é...nyL'...an...' ... csCJ
I,,48"''s--__--"""
ban, hanem azt feltételezzük, hogy bármilyen meglepá esemény is megtönénhet. Ha azonban elórelál6ak vagyunk és felkészülünk az effajta dolgokra, akkor elkerülhető az összeütközés. Fontos különbséget tenni, a progrdmhibák közön. Vannak ol}"dnok, amelyek abból adódnak, hogy a programozó szintaktikai hibát vételt. Vannak logikai hibák, amelyek onnan erednek, hogy a programozó félreértette a feladatot vagy a megoldás módját. Végül vannak olyan kivételek, melyek szokatlan (ám nem megjósolhatatlan) események miatt lépnek föl, mint például az erőforrások (memória vagy merevlemezhc\y) hiánya.
Váratlan események kezelése A programozók hatékony fordít6progr.lmoka t haszn:ílnak, és külö n böző biztosítékokka l rakják tele a kódot, amely Icbct6vé teszi a programozási hibák felfedezését. Újra és újra áuekintik a tervet, 6s kimcrít6 tesztelést folytaUlak, hogy megtaláljlik a logikai hibákat A kivételek azonb:m más jelleguek. A kivételes körülményeket nem lehet teljesen kiküszöbölni, csak felk(!szülni lehet clJuk, Felhasználóink számítógépein id6nként belelik amemória; ilyer,kor az il kérdi!s, hogy mit csináljon a program. Az alábbi lehet6ségek közül lehet váJaszt:mi: • Hagyjuk összeomlani a programot • Jelezzük a helyzetet il relhasználónak, és elegánsan lépünk ki • Jelezzük a helyzetet a relhasználónak, és lehetővé tesszük s7Jlmára, hogy megkísérclje a helyreállításI éS a folytatást • A progrnm maga javítja a problémáI, és tovább fut a felhasználó megzavarása nélkül Nem muszáj, S61 nem is kívánatos, hogy minden programunk automatikusan (!5 csendben maga küszö\)öljön ki minden váratlan helyzetet, az azonba n nyilvánvaló, hogy többet kell tennünk, mint hogy hagyjuk csak (Igy összeomlani a programot. A CH kivételkezelés típusbiztos és szervesen beépülő módszert biztosít arra, hogy megbirkózzunk a program fu tása közben fell épő ritka, de megjósolható eseményekkel.
Kivételek A C++ nyelvben a kivétel egy olyan objektum, amely a kód egyik részér61 a másikra adódik ál: a hibát kiváltó reszr61 a lubát kezelő részre. Amikor bekövetkezik a kivételes esemény, akkor azt mondjuk, hogy a program .továbbdobon egy kivételtH; a kivétel kezelését pedig .a kivétel elkapásának mondjuk. H
486 1VI. rész· Különlegességek A kivétel típusa dönti el, hogy melyik kódrészlet fogja kezelni a problémát; a továbbdobott objektum tartalma (ha van), visszajelzést nyújthat a fdh