Professional Documents
Culture Documents
j elemek a C++-ban
Alternatvk a #define direktva helyett
Cm szerint nyilvntartott tpus, vagy referencia tpus vltozk
Inicializlt fggvnyparamterek
C ++ kommentek
Az OOP alapjai
Egysgbezrs
rkls
Tbbrtsg (polimorfizmus)
Fggvnymezk definilsa
Fggvnymezk aktivizlsa
A this nev, implicit mutat
Konstruktorok s destruktorok
Konstruktorok definilsa
Destruktorok definilsa
Mezhozzfrs
Mezhozzfrsi szintek
Mezhozzfrs s rkls
Virtulis fggvnyek
Ksi sszerendels
Virtulis fggvnyek deklarlsa
Dinamikus objektumok
Dinamikus objektumok ltrehozsa
Dinamikus objektumok megszntetse
Tovbbi flexibilits a C++-ban
Rokonok s bartok
Operator overloading
C++ I/O knyvtrak
deklarci azt jelenti, hogy r olyan vltoz, amely egy egsz tpus vltozra vonatkoz
referencit tartalmazhat. gy is felfoghatjuk a dolgot, hogy egy ilyen fajta vltoz egy olyan
konstans pointer-kifejezs, amelyre vonatkozlag automatikusan vgrehajtdik egy
indirekci-mvelet, amikor az adott referencia tpus vltozra hivatkozunk. Ez hrom dolgot
von maga utn. Az egyik, hogy a fenti plda szerinti r vltoz minden olyan helyen llhat,
ahol egy int tpus vltoz is llhat, azaz egsz tpus kifejezsekben akr balrtk, akr
jobbrtk lehet. A msodik, hogy a referencia tpus vltozkkal semmilyen mvelet nem
vgezhet, hiszen minden hivatkozs alkalmval minden egyebet megelz az implicit
indirekci mvelet (dereference operation, lsd a 1.5.2 alatt az egyoperandus * opertorrl
lertakat). Ezzel ll szoros sszefggsben a harmadik fontos dolog, hogy nincs rtelme egy
cm szerint nyilvntartott tpus vltozt inicializls nlkl definilni. Teht csak az
albbihoz hasonl defincinak van rtelme:
int ii = 0;
int& rr = ii;
Ekkor az rr++; utasts szintaktikailag ugyan helyes, de nem az rr vltoz inkrementldik,
hanem az az int tpus trterletfoglal trolsi egysg, amelyiknek a cmt rr tartalmazza.
Ez a fenti pldban ppensggel az ii vltoz. Ez az rtelmezs trivilis akkor, amikor az
inicializl kifejezs egy balrtk, de nem ktelez, hogy az inicializtor balrtk legyen, st,
az sem ktelez, hogy a referncia tpus alaptpusba tartozzon. Ilyen esetekben
a)
elszr tpuskonverzi hajtdik vgre, ha az szksges,
b)
aztn a tpuskonverzi eredmnye egy ideiglenes vltozba kerl,
c)
s vgl ennek az ideiglenes vltoznak a cme kerl felhasznlsra az adott referencia
tpus vltoz inicializlshoz.
double& dr = 1;
A hasznlat
double* drp;
double temp;
temp = (double)1;
drp = &temp;
sorn a (*drp) kifejezs
egyenrtk a dr-rel.
Ekkor az
alfa = 0L; f1(alfa);
#include <stdio.h>
// **********************************************************
int any_function(int par_by_value, //Ertek szerinti int
int* use_for_arrays,//Ertek szerinti pointer
int& par_by_address)//Cim szerinti int
// **********************************************************
{ int
work;
work = par_by_value;
par_by_value *= 3;
*use_for_arrays = par_by_address * work;
par_by_address = work * work;
return par_by_value;
}
// **********************************************************
main()
// **********************************************************
{
int x = 2, y[ ] = { 1, 2 }, z = 10, w = 0;
w = any_function(z,y,x);
printf("%d %d %d %d\n",x,y[0],z,w);
}
Az any_function fggvny harmadik paramtert deklarltuk referencia tpusnak,
tpusnv (int) utn ll & tpusmdost opertor utal. Tekintsk t a fenti program
erre a
mkdst.
A main-ben any_function meghvsa eltt az x, y[0], z s w vltozk rtke rendre 2, 1, 10
s 0, a w-nek fggvnyhvssal trtn rtkads eredmnyekppen (mellkhatsknt) pedig
x, y[0], z, valamint w a 100, 20, 10 s 30 rtkeket veszik fel; a szabvnyos kimeneten ezek a
szmok fognak sorra megjelenni. Lthat, hogy az rtk szerint tadott paramter (z) nem
vltozott meg, y[0] j rtket kapott, hiszen a re mutat pointeren keresztl indirekt mdon
cmezve belertunk, s a cm szerint tadott skalr, x is j rtkkel br a fggvnyhvs utn.
Lthat az is, hogy par_by_value s par_by_address hasznlata szintaktikailag azonos.
Az any_function deklarcijakor a par_by_address parmternl hasznlt megolds
hasonlt ahhoz, amit a Pascal nyelv alkalmaz. Pascal-ban is alaprtelmezs szerint rtk
szerint addnak t a paramterek; ott a VAR kulcsszval jelezhetjk a cm szerinti
paramtertadst, ugyanakkor a FUNCTION, vagy PROCEDURE trzsben az rtk, vagy cm
szerint tadott paramterek hasznlatban nincs szintaktikai klnbsg.
A referencia tpust fggvnyek visszatrsi tpusaknt is nagyon jl fel lehet hasznlni.
Gondoljunk csak a 1.12-es pontban a balrtkek kapcsn deklarlt char *get_buff_pos(int
i) fggvnyre. Ennek a fggvnynek balrtkknt val alkalmazsa gy nzett ki:
*get_buff_pos(1) = 'b';
Ha fggvnynket char & tpus visszatrsi rtkkel deklarljuk, akkor a referencia tpus
tulajdonsgai kvetkeztben a get_buff_pos(1) kifejezs minden tovbbi nlkl rvnyes
balrtk kifejezs lesz, teht a fenti rtkad utastst gy rhatjuk:
get_buff_pos(1) = 'b';
Vegyk szre, hogy a referencia tpus visszatrsi rtk miatt a char& get_buff_pos(int
i) fggvny egy rtkad utasts mindkt oldaln llhat. A referencia tpusrl itt
elmondottak elssorban a fogalom megrtetst szolgljk. A referencia tpus legeslegfontosabb szerepet azonban a felhasznl ltal definilt tpusokra vonatkoz opertorfggvnyeknek definilsnl jtssza - ezek visszatrsi rtke ugyanis az adott tpusra
vonatkoz referencia-tpus.
Vegyk szre, hogy a referencia tpust kpz & opertor termszetes kiegsztse a
hagyomnyos C tpusmdost opertorainak: mg a * indirekci opertornak, a [ ] indexel
opertornak s a ( ) fggvnyaktivizl opertornak volt prja a tpusmdost opertorok
kztt, addig az 'address of' opertornak (egyoperandus &) nem volt.
Inicializlt fggvnyparamterek
Vltozk esetben mr a hagyomnyos C-ben megszoktuk, hogy a definci sorn kezdeti
rtkeket is megadhatunk. A C++ filozfia a kezdeti rtkadst kiemelt fontossggal kezeli
(lsd pldul a konstruktorokat 2.7-nl), gy szinte termszetes, hogy ezt a lehetsget a
fggvnyparamterekre is kiterjeszti.
A C nyelv (s a C++ is) megengedi, hogy egy fggvnyt kevesebb aktulis paramterrel
aktivizljunk, mint ahny paramtert a formlis paramterlistban deklarltunk. Ez sokszor
kellemes, jl kihasznlhat tulajdonsg, sokszor azonban kellemetlen, nehezen szrevehet
mellkhatsokat eredmnyez hibaforrs is lehet. Gondoljunk csak a jl ismert printf
fggvnyre, melynek mkdse a C ezen tulajdonsgn alapul: Ha a formtum-specifikl
sztringben tbb kiirand adatot hatrozunk meg, mint ahnyat aztn tnylegesen felsorolunk a
printf aktulis paramterlistjban, akkor mindig valami "szemt" kerl standard outputra.
Ehhez hasonlan hatrozatlan paramterrtkekkel dolgozhat akrmilyen ms fggvny is, ha
vletlenl hinyos aktulis paramterlistval hvtuk meg. Az ilyen - esetleg fatlis hibt okoz
- szitucik elkerlst szolglja az a lehetsg, hogy egy fggvny deklarcijakor a
formlis paramterlistn az egyes paramterekhez alaprtelmezs-szer (default) kezdeti
rtkeket rendelhetnk. Tekintsnk erre egy pldt:
Ha a fenti deklarci szerinti fggvnyt arra hasznljuk, hogy egy hromszg harmadik
oldalnak hosszt kiszmtsuk a koszinusz-ttel segtsgvel (gy, hogy a kt ismert oldal
ltal kzbezrt szget fokokban kell megadnunk), akkor a gamma paramter kihagysval a
Pithagorasz-ttelre egyszersdhet a problma. Teht a triangle(3,4,135)
fggvnyhvskor az aktulis paramterek rtke rendre 3, 4 s 135, a triangle(3,4)
hvskor 3, 4 s 90, triangle(3) hatsra 3, 0, 90 lesz, s vgl a triangle()
fggvnyhvs esetben teljesen az alaprtelmezs szerinti 0, 0, 90 szmhrmas lesz
rvnyben.
C ++ kommentek
Figyeljk meg a 2.1.2-es rsz pldaprogramjban, hogy a C++-ban kt slash (/) karakterrel
egysoros kommentrokat rhatunk. Ez igaz minden C++ rendszerre. A BORLAND C++
esetben ezt a kommentezsi stlust alkalmazhatjuk akkor is, ha csak egyszer C fordtknt
akarjuk hasznlni a rendszert, mindazonltal megjegyzend, hogy az egysoros kommentrok
ilyen jellse ltalban nem portbilis (mert a rgebbi C fordtk nem fogadjk el //-t.
Termszetesen a /* .. */ pros s a // alkalmazsa vegyesen is lehetsges.
Az OOP alapjai
Az objektum-orientlt programozs (rviden OOP) a termszetes gondolkodst, cselekvst
kzelt programozsi md, amely a programozsi nyelvek tervezsnek termszetes
fejldse kvetkeztben alakult ki. Az gy ltrejtt nyelv sokkal struktrltabb, sokkal
modulrisabb s absztraktabb, mint egy hagyomnyos nyelv. Egy OOP nyelvet hrom fontos
dolog jellemez. Ezek a kvetkezk:
Egysgbezrs
A C++ egyik fontos tulajdonsga, hogy lehetsgnk van az adataink s az ket manipull
programkd sszeforrasztsra, egy egysgbe zrsra, egy osztlyba foglalsra. (Ez az n.
encapsulation.) Pldul tegyk fel, hogy definltunk egy karakterkszletet (font-ot) ler
adatstruktrt (pldul egy fejrszt s az egyes karakterek tulajdonsgait ler tmbt), amely
kell informcit tartalmaz ahhoz, hogy a karaktereket kirajzolhassuk a kpernyre, s
vannak fggvnyeink, amelyekkel a karaktereinket megjelenthetjk, tsznezhetjk s
mozgathatjuk a kpen.
A hagyomnyos C-ben az a szoksos megolds, hogy az adatstruktrinkat s a hozzjuk
tartoz fggvnyeket egy nll, kln fordthat forrsmodulban helyezzk el. Ez a
megolds mr elg elegns, de az adatok s az ket manipull fggvnyek kztt mg nincs
explicit sszerendeltsg, tovbb ms programozk egy msik modulbl direkt mdon is
hozzfrhetnek az adatainkhoz, anlkl, hogy az adatok kezelsre szolgl fggvnyeinket
hasznlnk. Ilyen esetekben az alap-adatstruktra megvltozsa fatlis hibt okozhat egy
nagyobb project-ben.
A C++-ban specilis trolsi egysgfajtk, az osztlyok szolglnak arra, hogy adatokat s a
hozzjuk rendelt fggvnyeket egy egysgknt, egy objektumknt kezelhessnk. Az osztlyok
alaptpusa a C++-ban a class, amely hasonlt a hagyomnyos C-bl ismert struct-ra s
union-ra. A C++-ban a struct s union is egy bizonyos fajta osztlyok. A union-ra a
hagyomnyos rtelemben tovbbra is szksg van, a struct-ot a kulcsszt pedig a C++-ban
alapveten a hagyomnyos C-vel val kompatibilits biztostsa vgett tartottk meg.
Teht a fenti kulcsszavak szolglnak arra, hogy osztlyokat definilhassunk. A C++-ban egy
osztly tpus trolsi egysg ( class, vagy struct) fggvnyeket (n. fggvnymezket angolul member functions) kombinl adatokkal (adatmezkkel - angolul data members), s az
gy ltrejtt kombincit elrejtjk, elzrjuk a klvilg ell. Ezt rtjk az egysgbezrs alatt.
Egy class-deklarci hasonl a jl ismert struktradeklarcihoz:
class font {
...
};
font tpussal
font times[10];
font *f_ptr;
Egy msik vltozs a hagyomnyos C-hez kpest az, hogy egy struct vagy class
tpuscmke (fenti pldnkban a font) kulcssz elhagysval nmagban is tpusrtk, teht
kzvetlenl is hasznlhat vltozk definilsra, deklarlsra.
rkls
Mi jut esznkbe az rkls szrl? Taln az, hogy a gyermek milyen tulajdonsgokat rklt
szleitl, vagy gondolhatunk egy csaldfra is. Ugyanilyen csaldfa pthet fel osztlyokkal
is. A C++-ban deklarlhatk szrmaztatott osztlyok (derived classes), amelyek rklik az
stpusok, az alaposztlyok (base classes) adat- s fggvnymezit. Ez lehetv teszi egy
hierarchikus osztlystruktra ltrehozst.
Nzznk meg erre egy egyszer pldt. Ha grafikus programot ksztnk, szksgnk lehet
arra, hogy kezeljk a grafikus kpernyn egy pont (pixel) helynek x s y koordintit.
Hozzunk ezrt ltre egy location (hely) nev struktrt:
struct location {
int x;
int y;
};
az x, y
Ezek utn
definilhatnnk:
struct point {
struct location position;
colortype
color;
};
protected:
int x;
int y;
}
class point : location
{
colortype color;
};
Ez a deklarci sokkal jobban kifejezi azt, hogy miben klnbzik a point tpus a
location tpustl. (A protected kulcssz jelentst ksbb trgyaljuk majd.) A klnbsg
teljesen nyilvnvalan abban van, hogy a point egy hely (location), amelyhez valamilyen
sznt is rendeltnk.
A kpernyn a pont helyzete a point defincijban explicit mdon nem szerepel, mert azt a
location tpustl rklte. Mivel a point tpus a location leszrmazottja, annak
minden mezjt rkli, de ugyanakkor a sznt ler mezvel bvtettk. Egy szrmaztatott
tpus defincis blokkjban csak az j, vagy megvltozott tulajdonsgoknak megfelel mezk
szerepelnek. Termszetesen a point tpusbl jabb osztlyokat szrmaztathatunk gy, hogy
tovbb bvtjk tetszleges tpus mezkkel. Az gy ltrehozott tpusok termszetesen (az j
tulajdonsgaik mellett) hordozni fogjk a point tpus minden meglv tulajdonsgt, s
hasznlhatk lesznek minden olyan esetben, amikor point is hasznlhat.
Azt az eljrst, amely ltal az egyik tpus rkli a msik tpus tulajdonsgait, rklsnek
hvjk. Az rkl a leszrmazott tpus ( descendant type). Az stpus (ancestor type) az,
amelytl a leszrmazott tpus rkl.
Fenti pldnkban a location az stpus, a point pedig a leszrmazott tpus. Ksbb ltni
fogjuk, hogy ez az eljrs korltlanul folytathat. Definilhatjuk a point leszrmazott tpust,
majd ennek a point leszrmazott tpusnak a leszrmazottjt. A programtervezs legnagyobb
rsze az OOP-ben a megfelel osztly-hierarchinak, azaz az osztlyok csaldfjnak a
ltrehozst jelenti.
A C++-ban egy szrmaztatott tpus nemcsak egy stpustl rklhet (egy csaldfn pl.
minden egyednek van apja is s anyja is). Azt, amikor egy szrmaztatott tpus tbb stpusbl
kerlt levezetsre, tbbszrs rklsnek (multiple inheritance) nevezzk. A tbbszrs
rkls csak az AT&T 2.0-s verzij C++ fordtjval kompatibilis C++ rendszerekben
lehetsges. Tbbszrs rkls esetn az stpusokat egymstl vesszvel elvlasztva kell
felsorolnunk. Ha teht father s mother egy-egy ltez osztly (pldul class-knt definilt
tpus), akkor segtsgkkel a child tpust a kvetkezkppen deklarlhatjuk:
class child : father, mother
{
// Itt jonnek a 'child'-ra jellemzo
// ujabb tulajdonsagok mezoi.
};
rkli a father s a mother minden tulajdonsgt (azaz
A child teht
minden adat- s
fggvnymezjt), s egyben jabb tulajdonsgokkal (mezkkel) rendelkezhet.
A mezhozzfrs kapcsn a 2.8-as szakaszban ltni fogjuk, hogy egy szrmaztatott tpusban
az stpusoktl rklt fggvnymezk hozzfrsi szintjei mdosthatk az stpus-listban
a tpusazonostk eltt elhelyezett megfelel kulcsszavakkal ( private, protected).
Mivel a szrmaztatott tpusokkal mindent olyan mvelet elvgezhet, ami a definicija sorn
felhasznlt stpusokhoz felhasznlhat, ezrt egy szrmaztatott tpus mindig fellrl
kompatibilis az stpusaival.
sszefoglalva teht a fent lertakat: Hagyomnyos C-ben egy struktra nem tud rklni, a
C++ azonban tmogatja az rklst. A nyelvnek ez a kiterjesztse egy j trolsi egysgfajtban nyilvnul meg, amely hasonl a C-ben megszokott trterletfoglal trolsi
egysgekhez, a struktrkhoz s a -unionokhoz, de azoknl sokkal hatkonyabb. A trolsi
egysgek ezen j fajtja az osztly. Hatkonysga abban ll, hogy mg a hagyomnyos C-ben
hierarchikusan egymsbagyazott struktrarendszerben csak adatmezk "rkldnek", addig a
C++ osztlyok esetben egy objektum egyb tulajdonsgai - nevezetesen az, hogy az
adatmezkkel mit s hogyan lehet csinlni - is (azaz a fggvnymezk is) rkldnek.
Megjegyzend, hogy a hagyomnos C egymsba skatulyzott struktri esetben nincs sz
igazi rklsrl, s a skatulyzs rvn definilt jabb struktrk, s a definilshoz
felhasznlt struktrk kztt semmifle tpuskompatibilts nincs.
Tbbrtsg (polimorfizmus)
A tbbrtsg (vagy sokalaksg, sokoldalsg) a C++-ban azt jelenti, hogy egy adott
stpusbl szrmaztatott tovbbi tpusok termszetesen rklik az stpus minden mezjt, gy
a fggvnymezket is. De az evolci sorn a tulajdonsgok egyre mdosulnak, azaz pldul
egy rkltt fggvnymez nevben ugyan nem vltozik egy leszrmazottban, de esetleg mr
egy kicsit (vagy ppen nagyon) mskpp viselkedik. Ezt a C++-ban a legflexibilisebb mdon
az n. virtulis fggvnyek (virtual functions) teszik lehetv. A virtulis fggvnyek
biztostjk, hogy egy adott osztly-hierarchiban (szrmazsi fn) egy adott fggvny
klnbz verzii ltezhessenek gy, hogy csak a ksz program futsa sorn derl, hogy ezek
kzl ppen melyiket kell vgrehajtsra meghvni. Ezt a mechanizmust, azaz a hv s a
hvott fggvny futsi id alatt trtn sszerendelst ksi sszerendelsnek (late binding)
nevezzk. Errl majd a 2.9-es rszben olvashatunk rszletesebben.
A tbbrtsgnek a fordtsi idben megvalsthat formja az n. overloading. (A szerintnk
is alkalmas magyar kifejezs helyett az eredeti angol kifejezst hasznljuk knyvnkben. gy
gondoljuk, hogy a szszerinti fordts, a tlterhels nem fejezi ki e fogalom lnyegt. A
overloading helyett esetleg hasznlhatnnk az tdefinils kifejezst, de mint ksbb ltni
fogjuk, ez sem precz megfogalmazs.) Nos, lssuk, mirl is van sz:
A hagyomnyos C-ben egy adott nvvel csak egy fggvnyt definilhatunk. Pldul ha
deklarljuk az
int
cube(int number);
fggvnyt, akkor kiszmthatjuk egy egsz szm kbt, de elkpzelhet, hogy egy long, vagy
egy double szm kbre van szksgnk. Termszetesen rhatnnk mg kt tovbbi
fggvnyt erre a clra, de a cube azonostt mr nem hasznlhatjuk:
long
lcube(long
lnumber);
double dcube(double dnumber);
A C++-ban mgis lehetsg van arra, hogy ugyanolyan azonostval lssuk el mind a hrom
fggvnyt. Ezt hvjk angolul overloading-nak. Ez teht azt jelenti, hogy lehetsgnk van
arra, hogy egy azonos nvvel tbb klnbz fggvnynk legyen, melyek termszetesen
klnbz adattpusokat dolgoznak fel:
int
cube(int
inumber);
long
cube(long
lnumber);
double cube(double dnumber);
Fggvnymezk definilsa
Ebben az alfejezetben az rkls fogalmnl hasznlt grafikus pldt folytatjuk.
Az elzekben lthattuk, hogy egy osztlynak nemcsak adatmezi, hanem fggvnymezi
(function members) is lehetnek. Egy fggvnymez nem ms, mint egy, a class defincin
bell deklarlt fggvny. (A fggvnymezket ms OOP nyelvekben, pldul a Smalltalkban, vagy a Turbo Pascal-ban metdusoknak hvjk).
Most deklarljunk egy get_x azonostj fggvnymezt a point osztly szmra. Ezt
ktflekppen tehetjk meg:
a)
vagy az osztly-defincin bell definljuk a fggvnyt (n. implicit inline
fggvnyknt),
b)
vagy az osztlydefincin bell csak deklarljuk, s azutn valahol ksbb definiljuk.
A kt mdszer klnbz szintaxissal rendelkezik. Lssunk egy pldt az els lehetsgre:
class point
{ int
x;
int
y;
colortype color;
int
get_x(void) { return x; };
....
// implicit inline definicio
};
valamint tudja azt is, hogy mi az rvnyessgi tartomnya. Ez teht azt jelenti, hogy a
pont::get_x fggvny (a hagyomnyos globlis vltozkon kvl) csak a point osztlyhoz
tartoz objektumok mezihez frhet hozz. Termszetesen ltezhet tbb, ms osztlyhoz
tartoz get_x fggvny is. (Erre a lehetsgre - br nem explicit mdon - utaltunk a
tbbrtsg trgyalsnl.) Az osztly-deklarcin belli fggvny-definici esetben
termszetesen nem volt szksg az rvnyessgi tartomnyt definil point:: eltagra,
hiszen a get_x fggvny hovatartozsa abban az esetben teljesen egyrtelm volt.
A hovatartozs definilsn tlmenen a point::-nak ms szerepe is van. Hatsa kiterjed az
t kvet fggvnydefincira oly mdon, hogy a get_x fggvnyen bell az x azonostj
vltozra val hivatkozs a point struktra x azonostj adatmezjre vonatkozik, valamint
a get_x fggvny a point osztly hatskrbe kerl (lsd a 2.6.2-es rszt).
Fggetlenl attl, hogy a fggvnymezt milyen mdon definiljuk, a lnyeg az, hogy van
egy get_x nev fggvnynk, ami szorosan hozz van kapcsolva a point nvvel elltott
osztlyhoz. Mivel a get_x-et mezknt definiltuk a point szmra, a point minden
adatmezjhez hozzfrhet. Ebben az egyszer pldnkban get_x egyszeren csak x rtkt
szolgltatja.
Elkpzelhet, hogy egy szrmaztatott tpusban loklisan deklarlunk egy fggvnymezt,
melynek azonostja s paramterlistja is megegyezik egy stpusban definilt
fggvnymez azonostjval s paramterlistjval, ugyanakkor a definci sorn fel
szeretnnk hasznlni az stpusbeli vltozatot. Mivel a ksbbi definciban lv
fggvnymez a sajt hatskrben elfedi a korbbi defincit, a szrmaztatott tpusban csak
az rvnyessgi tartomnyt definl opertor segtsgvel hasznlhatjuk az stpusban
definilt, "elfedett" fggvnymezt. Erre vonatkozan (az eddigi pldnkat egy kicsit
flretve) tekintsk az albbi deklarcikat:
class rectangle { int
...
void
};
class triangle { int
...
a, b;
// Negyszog adatai
show(void); // Kirajzolja
a, b, c;// Haromszog adatai
Fggvnymezk aktivizlsa
A fggvnymezk egy adott tpus adathalmazon vgrehajtand mveleteket jelentenek.
Amikor teht a get_x fggvnyt meghvjuk, tudatnunk kellene vele azt is, hogy most ppen
melyik definilt point tpus objektum-pldny (object instance) x koordintira van
szksgnk, ppen ezrt valahogy ezt az informcit is kzlnnk kell a fordtprogrammal.
A megolds a hagyomnyos C struktra-mezkhz val hozzfrs szintaktikjnak
kiterjesztse. Ha origin egy point tpus objektum-pldny, akkor az origin.x
hivatkozshoz hasonlan az
origin.get_x( )
origin objektum-pldny get_x
kifejezs az
fggvnymezje ltal szolgltatott rtket
jelenti. A hagyomnyos C struktra adatmez-hozzfrshez hasonlan itt is a '.' (pont)
karakter jtssza az osztly mezkivlaszt opertornak (class component selector) szerept.
Az ltalnos szintaxis a kvetkez:
objektumnv.fggvnymez-nv( argumentumlista)
Az elbbiekhez hasonlan, ha p_ptr egy point* tpus mutat, akkor a
p_ptr->get_x( )
kifejezs a p_ptr ltal megcmzett point tpus objektum get_x fggvnymezje ltal
szolgltatott rtket adja.
int m;
public:
....
int read_m(void) { return m; }
};
ha aa s bb cc
deklarci esetn,
tpusak, akkor az aa.read_m( ), illetve a bb.read_m( )
fggvnyhvsok esetn rendre az aa.m, illetve bb.m rtkeket kapjuk. A fggvnyhvs sorn
gy derl ki, hogy melyik objektum adatmezit kell hasznlni, hogy minden fggvnymez
szmra implicit mdon deklarlsra kerl egy this nev pointer. Ha teht a read_m
fggvny egy cc tpus osztly fggvnymezje, akkor a read_m-en bell this egy cc*
tpus pointer, ilyen mdon az els read_m hivatkozsnl this az aa vltozra, mg a
msodik hivatkozs alkalmval a bb-re mutat, azaz az els esetben this == &aa, a msodik
esetben pedig this == &bb.
A this mutat explicit mdon is megjelenhet a fggvnymezk defincija sorn:
class cc {
int m;
public:
int read_m(void) { return this->m; }
};
Konstruktorok definilsa
Most tekintsk t elszr a konstruktorok definilst a megszokott point tpusunk esetben;
bvtsk ki az eddigi deklarcit egy konstruktorral.
class point
{
int
x;
int
y;
colortype color;
int
get_x(void) { return x; }
int
get_y(void) { return y; }
colortype
get_color(void);
void
show(void);
void
hide(void);
point(int newx, int newy);
// konstruktor deklaracio
};
point::point(int newx = 0, int newy = 0)
{
// konstruktor definicio
x = newx;
y = newy;
color = black;
}
Innen tudja a fordtprogram, hogy az adott fggvnymez egy konstruktor. Figyeljk meg
azt is, hogy mint minden fggvnynek, a konstruktornak is lehetnek paramterei, a
konstruktor trzse pedig egy szablyos fggvnytrzs. Ez azt jelenti, hogy egy konstruktor
hvhat ms fggvnyeket s minden, a hatskrbe tartoz adathoz hozzfrhet. Az egyetlen,
de lnyeges klnbsg, hogy egy konstruktornak nem lehet tpusa, mg void sem! (gy
rtket sem adhat vissza.) A fenti konstruktor-deklarcit figyelembe vve az albbi mdon
deklarlhatunk egy point tpus objektumot:
point a_point(100,200);
definci
alaprtelmezs szerinti 0 marad.
Destruktorok definilsa
Ahogy konstruktorokat definilhatunk, ugyangy definlhatunk sajt magunk destruktorokat
is.
A statikus objektumok szmra a C++ futtat rendszer a main meghvsa eltt foglal
trterletet s a main befejezse utn felszabadtja azt. Az auto objektumok esetben a
trterletfelszabadts akkor trtnik meg, amikor az adott vltoz rvnyt veszti (teht az
objektum definicit tartalmaz blokk vgn). Az ltalunk definilt destruktorokat akkor
aktivizlja a C++ futtat rendszer, amikor egy objektumot meg kell szntetni. A destruktor
definci hasonlt a konstruktor defincira. Ha a point tpus objektumoknak
point::point a konstruktora, akkor point::~ point lesz a destruktoruk. A formai
klnbsget csak a ~ (tilde) karakter jelenti (ami a komplementls opertora).
class point
{
int
x;
int
y;
colortype color;
int
get_x(void) { return x; }
int
get_y(void) { return y; }
colortype get_color(void);
void
show(void);
void
hide(void);
point(int newx, int newy); // konstrktor
~point(void);
// destruktor
};
A fenti plda els rnzsre felesleges, hiszen elg lenne a C++ futtat rendszer
alaprtelmezs szerinti destruktorra hagyatkozni. Igen m, de egy megsznsre tlt pontot a
kpernyrl is el kell tntetni. Ez azt jelenti teht, hogy egy olyan destruktor fggvnyt kell
definilnunk a point tpus szmra, amelyik szksg esetn (pldul ha a hatterszin ==
get_color() igaz) a hide fggvny meghvsval az adott pontot le is trli a kpernyrl.
Mezhozzfrs
Ahogy a 2.1-es szakaszban az egysgbezrs kapcsn mr utaltunk r, egy osztly egyes
mezihez val hozzfrst mi magunk is kzben tarthatjuk a C++ ltal nyjtott alaprtelmezs
fellbrlsval.
A C++-ban a struct tpus osztlyokban csak rszben rvnyesl az egysgbezrs.
Nevezetesen az egysg megvan, ugyanis az adatmezk s a fggvnymezk egybe vannak
forrasztva, de nincsenek elzrva a klvilgtl. Ennek oka az, hogy az alaprtelmezs szerint
egy struct minden mezje publikus, a klvilgbl szabadon hozzfrhet. A j C++
programtervezs szem eltt tartja az adat- vagy mg inkbb az informcirejtst, azaz egy
osztly egyes mezit privtnak, vagy vdettnek deklarlja, s ezzel egyidejleg jl definilt,
krlhatrolt "hivatalos" kls hozzfrst biztost hozzjuk. ltalnos elvknt tekinthetjk
azt, hogy az adatmezk privtak vagy vdettek s a csak bels mveleteket vgrehajt
fggvnymezk szintn privtak, mg a klvilggal val kapcsolattartst publikus
fggvnymezk biztostjk. Ezrt javasoljuk a class hasznlatt a struct helyett, hiszen a
class alaprtelmezs szerinti mezhozzfrsi szintjei pontosan a fenti kivnalmaknak
felenek meg.
A C++-ban hrom, a cimkkhez formailag hasonl mdon hasznlhat kulcssz ll
rendelkezsnkre, hogy a mezhozzfrst mi magunk llthassuk be egy osztly-definci
sorn. A mezhozzfrs-szablyozs ltalnos szintaktikja a kvetkez:
hozzfrsi md : mezlista
ahol a hozzfrsi md a public, private, vagy protected kulcsszavak egyike lehet.
Pldul ha point-ot struct-knt deklarljuk, clszer az albbi mezhozzfrst belltani:
struct point {
// Ez most
private:
int
int
colortype
public:
int
colortype
point(int
};
Egy mezhozzfrst mdost kulcssz hatsa egy kvetkez hasonl kulcsszig, vagy az t
tartalmaz blokkzr kapcsos zrjelig tart.
Mezhozzfrsi szintek
Mint emltettk, hrom klnbz szinten rhetjk el egy osztly egyes mezit. Most
tekintsk t rszletesen, mit is jelent e hrom szint.
A private-knt deklarlt mezket csak az ugyanabban az osztlyban deklarlt
fggvnymezk rhetik el. A protected mezket az ugyanabban az osztlyban, s az adott
osztlybl szrmaztatott tovbbi osztlyokban definilt fggvnymezk rhetik el. A public
mezk korltozs nlkl elrhetk mindenhonnan, ahonnan az adott osztly is elrhet.
A struct-tal definilt osztlyok mezi alaprtelmezs szerint mind publikusak (public
elrsek), gy ahhoz, hogy az igazi egysgbezrst megvalsthassuk, a klvilg ell
elzrand mezket mi magunk kell, hogy a private kulcsszval privtnak deklarljuk (a
private rszt esetleg kvet jabb nyilvnos rszeket megint a public kulcsszval kell
megnyitnunk a klvilg szmra). Minthogy egy j C++ programoz mindent privtnak tekint
s csak azt mondja meg kln, hogy mely mezk publikusak, osztlyok definilsra
elnysebb a class-t hasznlni a struct helyett, ugyanis egy class minden mezje
alaprtelmezs szerint private. (Az egyetlen klnbsg a struct s a class kztt teht a
mezhozzfrs alaprtelmezsben van.) A point tpust class-knt definilva gy clszer a
mezhozzfrst a kvetkezk szerint szablyozni:
class point { // Alapertelmezes szerint privat:
int
x;
int
y;
colortype color;
public:
// Publikus:
int
get_x(void) { return x; }
int
get_y(void) { return
y; }
colortype get_color(void);
void
show(void);
void
hide(void);
point(int newx, int newy);
~point(void);
};
Mezhozzfrs s rkls
A mezhozzfrs s az rkls egymssal szoros kapcsolatban llnak. Ennek ttekintshez
a point tpusnak a location-bl levezetett vltozatt hasznljuk gy, hogy mr a
mezhozzfrst is szablyozzuk.
enum colortype { black, red, blue, green, yellow, white };
class location {
protected:// A csak vedett mezoket a szar// maztatott tipusok is elerhetik
int
x;
int
y;
public:
// Publikus fuggvenyek a kulvilagnak
int
get_x(void);
int
get_y(void);
location(int ix, int iy); // Konstruktor
~location(void);
//
Destruktor
};
class point : public location
{ // A publikus szarmaztatas azt jelenti, hogy
// 'location' mezoinek hozzaferesi szintjei
// valtozatlanul oroklodnek.
protected: // A leszarmazottak is lathatjak
colortype color;
public:
colortype get_color(void);
void
show(void);
void
hide(void);
point(int ix, iy);
~point(void);
Destruktor
// Konstruktor
//
};
D : hozzfrs-mdost B { ... };
struct
D : hozzfrs-mdost B { ... };
illetve
ahol D a szrmaztatott (derived) osztly tpusazonostja, B pedig az alap osztly (base class)
tpusazonostja. A hozzfrs-mdost vagy public, vagy private lehet; hasznlata
opcionlis. Mint mr emltettk, class esetn a hozzfrs-mdost alaprtelmezs szerint
private, struct
Ksi sszerendels
Az elz rszekben egy sszetett grafikus programrendszer lehetsges alaptpusainak pldjn
mutattuk be az OOP egyes jellemzit. A point tpus a location-bl lett szrmaztatva,
ugyangy point-bl ltrehozhatunk egy vonalakat ler osztlyt, amelyet pldul line-nak
nevezhetnk, s mondjuk line-bl szrmaztathatunk mindenfle poligont. A poligonokra kt
vzlatos pldt mutattunk (triangle, rectangle), ezekbl felptve pedig mr egy
"valsgos" objektumok lersra val tpus, a house vzlatos lerst adtuk. (Ezek a pldk
termszetesen nem egy mkd program ltrehozsra, hanem sokkal inkbb egy-egy OOP
fogalom megvilgtsra voltak kihegyezve.) Az eddigi pldk tbbsgnek ugyanakkor volt
egy kzs vonsa, nevezetesen az, hogy deklarltunk bennk egy-egy show azonostj
fggvnymezt azzal a cllal, hogy az az adott tpus objektumot a kpernyn megjelentse.
Az alapvet klnbsg az ltalunk elkpzelt objektumtpusok kztt az, hogy milyen mdon
kell ket a kpernyre kirajzolni. Az egsznek van egy nagy htrnya: akrhnyszor egy
jabb alakzatot ler osztlyt definilunk, a hozztartoz show fggvnyt mindannyiszor jra
kell rnunk. Ennek az az oka, hogy a C++ alapveten hromflekppen tudja az azonos nvvel
elltott fggvnyeket egymstl megklnbztetni:
};
class point : public location
{
protected:
colortype
public:
colortype
void
void
void
point(int
color;
get_color(void);
show(void);
hide(void);
move(int dx, int dy);
ix,int iy);
~point(void);
};
// A point:: fuggvenyek definicioja:
...
void point::move(int dx, int dy)
{
// dx, dy offsetekkel athelyezi
if (color) hide( ); // Ez itt a point::hide
x += dx;
y += dy;
if (color) show( ); // Ez itt a point::show
}
class circle : public point
{
protected:
int
radius;
// A sugara
public: // Konstruktorhoz kezdeti x,y,r
circle(int ix,int iy,int ir);
colortype get_color(void);
void
show(void);
void
hide(void);
void
move(int dx, int dy);
void
zoom(int factor);
};
// A circle:: fuggvenyek definicioja:
...
void circle::move(int dx, int dy)
{
// dx, dy offsetekkel athelyezi
if (color) hide( ); // Ez itt a circle::hide
x += dx;
y += dy;
if (color) show( ); // Ez itt a circle::show
}
...
Figyeljk meg, hogy a kt move fggvny mennyire egyforma! A visszatrsi
tpusuk void s
a paramter-szignatrjuk is azonos, st, mg a fggvnytrzsek is azonosnak tnnek. Ht
akkor mi alapjn tudja a fordt, hogy mikor melyik fggvnyrl van sz? Ez esetben - ahogy
azt a norml fggvnymezk esetben lttuk - a move fggvnyeink abban klnbznek, hogy
ms-ms osztlyhoz tartoznak ( point::move, illetve circle::move). Flvetdik a krds:
Ha a kt move fggvny trzse is egyforma, akkor mirt kell bellk 2 pldny, mirt ne
rklhetn circle ezt a fggvnyt point-tl? Nos azrt, mert csak felletes rnzsre
egyforma a kt fggvnytrzs, ugyanis ms-ms hide, illetve show fggvnyeket hvnak ezeknek csak a neve s a szignatrja azonos. Ha circle a point-tl rkln a move
fggvnyt, akkor az nem a megfelel hide, illetve show fggvnyeket hvn: nevezetesen a
krre vonatkoz fggvnyek helyett a pontra vonatkozkat aktivizln. Ez azrt van gy, mert
a korai kts kvetkeztben a point::hide, illetve a point::show lenne a point tpus move
fggvnyhez hozzrendelve, s az rkls ltal a circle tpus move fggvnye is ezeket
fggvnyeket hvn. Ezt a problmt gy oldhatjuk meg, ha a show, illetve a hide
fggvnyeket virtulis fgvnyekknt deklarljuk. Ekkor nyugodtan rhatunk egy kzs move
fggvnyt a point s a circle szmra (pontosabban fogalmazva a circle tpus rklhetn
a point tpus move fggvnyt), s majd futsi idben fog kiderlni, hogy melyik show,
illetve hide fggvnyt kell meghvni.
Dinamikus objektumok
Eddigi pldinkban csak statikus objektumokkal volt dolgunk (amelyek szmra a
fordtprogram foglal le trterletet a fordts sorn), jllehet a hagyomnyos C-ben mr
megszokhattuk, hogy vltozink egy rsze szmra mi magunk foglalunk le trterletet, s a
vltozkat megszntetve felszabadtjuk a memrit, ha az adott vltozkra tbb mr nincs
szksgnk.
Termszetesen a C++-ban sem kell lemondanunk a dinamikus trkezelsrl az objektumok
kapcsn, akr hasznlhatjuk is a hagyomnyos C-ben megismert trkezel fggvnyeket,
pldul a malloc-ot is. Ezt mgsem javasoljuk objektumok esetben, mert a C++ a
hagyomnyos trkezel fggvnyeknl jval hatkonyabb mdszereket knl. Pldul
biztostja, hogy a dinamikusan kezelt objektumok esetben is aktivizldjanak a megfelel
konstruktorok s destruktorok.
ltrehozott j "kr" kzppontjnak koordinti rendre 100 s 200, mg a sugr rtke 50 lesz.
A new opertorral tmbk szmra is foglalhatunk helyet. A new-nak ez az alternatv
szintaxisa a kvetkez:
new
tpus [mret]
A kifejezs rtke az adott tpus szmra lefoglalt adott mret tmbre mutat pointer lesz.
A new-val trtn memria-foglalskor fellp hibkat a _new_handler pointer
(elredefinilt globlis vltoz) ltal mutatott fggvny kezeli le. Alaprtelmezsben
sikertelen trterlet-foglalsi kisrlet esetn new visszatrsi rtke NULL, s programunk
futsa gy folytatdhat, mintha mi sem trtnt volna, ugyanis a _new_handler-en keresztl
aktivizlt hibakezel nem csinl semmit. Ugyanakkor lehetsgnk van arra, hogy a
_new_handler-t egy sajt hibakezelre lltsuk a set_new_handler( ) fggvnyhvs
segtsgvel. Ezt a kvetkez plda szemllteti:
#include <iostream.h>
#include <stdlib.h>
// ***********************************************************
void out_of_store(void)
// Sajat hibakezelo-fv. new-hoz
// ***********************************************************
{
cerr << "operator new failed: out of store\n";
exit(1);
// cerr-t lasd 3.11.3. alatt
}
// ***********************************************************
typedef void (*PF)();
// Fuggvenyre mutato tipus
extern PF set_new_handler(PF);// _new_handler-t allitja
struct pp { double x,y,z,w;
// Nagy struktura. Ennek prolong
a,b,c,d; };// balunk sok helyet foglalni
// ***********************************************************
main()
// _new_handler atallitasat szemlelteto program
// ***********************************************************
{
set_new_handler(&out_of_store);
pp *p = new pp[65535];
cout << "Meghivtuk new-t, p = " << long(p) << '\n';
}
// cout-ot is lasd 3.11.3 allatt
// ***********************************************************
A fenti program soha sem fog "normlisan" lefutni, hanem mindig az operator new failed: out
of store zenetet kapjuk a kpernyn s visszatrnk 1-es hibakddal az opercis
rendszerhez.
objektum [mret]
Rokonok s bartok
Az rkls kvetkeztben klnbz stpusokbl kiindulva teljes szrmazsi fkat, mskpp
kifejezve osztlyhierarchikat hozhatunk ltre. A tbbszrs rkls azt is lehetv teszi,
hogy egy jabb tpust tbb stpus leszrmazottjaknt hozzunk ltre. ly mdon egymssal
ronkonsgban ll tpusok deklarlhatk. Elkpzelhet azonban, hogy egy programon bell
egymstl teljesen fggetlen csaldfk jnnek ltre. Az egy tpuscsaldba (szrmazsi fba,
....
void yy_func1(void);
....
};
class zz {
....
void zz_func(void);
friend void yy::yy_func1(void);
....
};
};
....
void zz_func(void);
friend class yy;
....
Operator overloading
A C++-nak van egy specilis tulajdonsga, amellyel kevs ms nyelv rendelkezik.
Nevezetesen az, hogy a nyelv ltal definilt, ltez opertorokhoz jabb jelentst
rendelhetnk. Ez hasznos lehet abbl a clbl, hogy valamilyen osztlyknt definilt j
adatstruktrn is elvgeztethessk azt a mveletet, amit az illet opertortl megszoktunk az
elemi tpusok esetben. Ha pldul definilunk egy halmazok reprezentcijra szolgl
tpust, akkor nagyon kellemes, ha a + opertorral halmazok unijt, a * opertorral pedig
halmazok metszett kpezhetjk. Vagy ha a s b egy-egy mtrix tpus vltoz, akkor j
lenne, ha az a * b kifejezs szintn mtrix tpus eredmnyt, nevezetesen a kt mtrix
szorzatt szolgltatn.
A polimorfizmus trgyalsakor mr utaltunk arra, hogy egy adott mvelet vgrehajtst
jelent szimblumhoz tbbfle jelentst rendelhetnk olyan rtelemben, hogy azt a bizonyos
mveletet tbb, klnbz tpus adaton is vgre szeretnnk hajtani - ezt neveztk
overloading-nak. Gondoljunk bele, hogy az overloading nem egszen C++ tulajdonsg,
hiszen a hagyomnyos C-beli opertorok is kpesek klnbz adattpusokon is ugyanazt a
mveletet vgrehajtani. Erre egy igen kzenfekv plda az rtkads opertora (amelyik mind
klnbz sorszmozott tpus adatokon, mind pedig lebegpontos szmokon rtelmezett
mvelet), vagy gondoljunk az egyes aritmetikai opertorokra, amelyek a hagyomnyos C
klnbz elemi tpusain nagyjbl egyformn mkdnek.
Az opertor-overloading lehetsgt a C++ kiterjeszti gy, hogy az egyes opertorokhoz a
felhasznl is megadhat jabb rtelmezst egy n. opertor fggvny segtsgvel. Ez gy
lehetsges, hogy az operator kulcsszt kveten rjuk azt az opertor-szimblumot,
amelyikhez az jabb jelentst szeretnnk rendelni. gy pldul az operator+ annak a
fggvnynek lesz az azonostja, amelyikkel a + opertorhoz rendelnk egy jabb jelentst.
Nzznk erre egy pldt!
Ezek a folyamok tetszleges eszkzre vagy file-ba tirnythatk, mg C-ben csak az stdin s
az stdout irnythat t. Az iostream knyvtr elemei hierarchikusan plnek egymsra. A
knyvtr elemei az alap-primitvtl a legspecializltabb elemig a kvetkezk:
streambuf a memriabuffereket s az azokat kezel eljrsokat tartalmazza,
ios a folyamok llapotvltozit s a hibkat kezeli,
istream egy streambuf-bl szrmaz formtumozott s ktetlen formtum
karakterfolyam konverzijt vgzi,
ostream egy streambuf-ba kldend formtumozott vagy ktetlen formtum
karakterfolyam konverzijt vgzi,
iostream istream-et s ostream-et kombinlja annak rdekben, hogy ktirny
folyamokat lehessen kezelni,
istream_withassign a cin folyam szmra definil konstruktorokat s rtkad
opertorokat,
ostream_withassign a cout, cerr s a clog folyamok szmra definil
konstruktorokat s rtkad opertorokat.
Az istream osztly az overloading segtsgvel egy j jelentst ad a >> opertor szmra a
standard elemi tpusok esetre. Ha az x vltoz tpusa standard elemi tpus, akkor a cin>>x
kifejezs mellkhatsaknt az x tpusnak (char, int, long, float, double) megfelel
konverzis input rutin az x vltoznak megfelel memriacmre teszi a standard bemenetrl
rkez adatot. A kifejezs rtkeknt cin-t kapjuk vissza.
Az elbbiekhez hasonl mdon, az ostream osztlyban tallhat a << opertor j
rtelmezse. A cout<<x; utasts hatsra az x vltoz tpusnak megfelel output rutin a
szabvnyos kimeneti llomnyra kldi az x rtknek megfelel karaktersorozatot s
visszaadja cout-ot. (Itt x tpusa szintn valamelyik standard elemi tpus lehet.)
A << s >> opertor-fggvnyek a megfelel folyam osztlyra vonatkoz referencia tpus
visszatrsi rtket adnak, gy tbb ilyen opertor lncba fzhet:
int
i = 0, x = 243;
double d = 0;
cout << "The value of x is " << x << '\n';
cin >> i >> d; // Egy int-et, utana SPACE-t,
// majd egy double-t var.