52
Pohl László C++ gyak. és labor jegyzet BME 2006

Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

Pohl László

C++ gyak. és labor jegyzet

BME 2006

Page 2: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

2

Tartalom Tartalom ........................................................................................................................................... 2 Bevezetés.......................................................................................................................................... 4 1. UNIX gyorstalpaló [labor] ........................................................................................................... 5

1.1. Bejelentkezés......................................................................................................................... 5 1.2. stty......................................................................................................................................... 5

erase, kill, intr, echo ................................................................................................................. 6 raw és cooked........................................................................................................................... 6 fontos........................................................................................................................................ 6

1.3 Alapvető parancsok................................................................................................................ 6 2. Integrált fejlesztőkörnyezet [labor] .............................................................................................. 8 3. C ismétlés [gyakorlat] ................................................................................................................ 10

3.1. Feladat ................................................................................................................................. 10 3.2. Feladat ................................................................................................................................. 10 3.3. Feladat ................................................................................................................................. 10 3.4. feladat .................................................................................................................................. 11 3.5. feladat .................................................................................................................................. 11

4. Const, referencia, string [labor] ................................................................................................. 14 4.1. Const ................................................................................................................................... 14 4.2. Referencia ........................................................................................................................... 15 4.3. String................................................................................................................................... 16

5. Vándorlás C-ről C++-ra [gyakorlat] .......................................................................................... 19 5.1. C++ kiírás............................................................................................................................ 19 5.2. Mutatók, tömbök, referenciák ............................................................................................. 19

5.2.1. Változók ....................................................................................................................... 19 5.2.2. Swap............................................................................................................................. 20

5.3. Függvények ......................................................................................................................... 20 5.4. cin, cout, cerr....................................................................................................................... 21 5.5. Kivétel ................................................................................................................................. 21 5.6. Stack, névtér, kivétel ........................................................................................................... 22

6. Osztályok és operátorok [gyakorlat] .......................................................................................... 24 6.1. Osztály struct-ból ................................................................................................................ 24

6.1.1. Osztály ......................................................................................................................... 24 6.1.2. Objektumok és szorzás................................................................................................. 24 6.1.3. Szorzás valóssal ........................................................................................................... 26 6.1.4. Abszolút érték .............................................................................................................. 26 6.1.5. Házi feladat .................................................................................................................. 27

6.2. Osztály class-ból ................................................................................................................. 27 6.2.1. Osztály ......................................................................................................................... 27 6.2.2. Konstruktor .................................................................................................................. 28 6.2.3. Egyenlőségjel operátor................................................................................................. 30 6.2.4. Kiírás és beolvasás ....................................................................................................... 31 6.2.5. Nem inline tagfüggvény............................................................................................... 31 6.2.6. Konstruktorok összevonása.......................................................................................... 32 6.2.7. Házi feladat .................................................................................................................. 32

7. String osztály [labor].................................................................................................................. 33 Feladatok .................................................................................................................................... 33 string_class_h.cpp ...................................................................................................................... 33

Page 3: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

3

string2.cpp.................................................................................................................................. 37 8. Dinamikus adattagokat tartalmazó osztályok [gyakorlat].......................................................... 40

8.1. 10 komplex szám tárolására alkalmas tömb........................................................................ 40 8.2. Diákok adatai példa............................................................................................................. 41 8.3. Gyakorló feladatok:............................................................................................................. 44

9. Öröklés ....................................................................................................................................... 45 9.1 Ablakok................................................................................................................................ 45 9.2. IOSTREAM ........................................................................................................................ 48

F Hivatkozások .............................................................................................................................. 52

Page 4: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

4

Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni, melyek a C++ programozás tanulása során felmerülnek, és a tárgy tematikának részei. A C++ nyelvet, hasonlóan a beszélt nyelvekhez, nem lehet elsajátítani önálló tanulás nélkül, ezért mindenképpen oldjunk meg gyakorló feladatokat óráról órára, egyedül, otthon! A példaprogramokat nem kell begépelni. A jegyzet elektronikus formában a http://www.eet.bme.hu/~pohl/ oldalon megtalálható, a pdf-ből ki lehet másolni a szöveget, ha az Adobe Reader felső eszköztárán a Select Text-et választjuk, de a példaprogramok zip-elve is letölthetők ugyanonnan, így még a formázás is megmarad. A jegyzettel kapcsolatos kritikákat, hibalistákat a [email protected] címre várom.

Page 5: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

5

1. UNIX gyorstalpaló [labor] A félév során végig C++-szal fogunk foglalkozni, de bevezetésképp nagyon röviden

megismerkedünk a UNIX legfontosabb parancsaival. Irodalom: [1,2,3].

1.1. Bejelentkezés

Az Ural2 szerveren futó UNIX használatához témaszámra van szükség, mely a megfelelő

operátori fülkékben átvehető. A számítógépen indítsuk el a Windowst, innen a putty.exe terminál programmal tudunk

bejelentkezni az Ural2-re. Az Ural2 eléréséhez használjuk az SSH módot (1.1. ábra)!

1.1. ábra: A putty terminál konfigurációs képernyője

Ha helyesen töltöttük ki a konfigurációs adatokat, és az Open gombra kattintunk, egy

szöveges terminál ablak jelenik meg. Itt először meg kell adni azonosítónkat és jelszavunkat. Ha először jelentkezünk be, a rendszer új jelszót kér. A jelszó legalább 6 karakter legyen, és tartalmazzon legalább egy számjegyet és két betűt az első hat karakterében.

Ha sikeresen bejelentkeztünk, kapunk néhány tájékoztató üzenetet, és megjelenik a prompt. Ide írhatjuk a parancsokat. Figyeljünk arra, hogy a UNIX érzékeny a kis- és nagybetűk megkülönböztetésére!

1.2. stty

Azaz set teleltype. Írjuk be, hogy stty -a Ennek hatására megjelennek a konzol beállításai. Pl.: speed 9600 baud; line = 1; intr = ^C; quit = ^\; erase = DEL; kill = ^U; eof = ^D; eol <undef>; swtch = ^Z lnext = ^V; werase = ^W; rprnt = ^R; flush = ^O; stop = ^S; start = ^Q -parenb -parodd cs8 -cstopb hupcl cread clocal -loblk -tostop -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc

Page 6: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

6

ixon -ixany -ixoff isig icanon -xcase echo echoe echok -echonl -noflsh opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3

Ezek közül csak néhánnyal foglalkozunk.

erase, kill, intr, echo Alapértelmezés szerint általában az stty erase DEL szerepel, vagyis a DEL billentyűvel

törölhetünk. Ha a # billentyűvel szeretnénk törölni, írjuk ezt: stty erase # ! Ha ki akarjuk törölni a parancssorba eddig begépelt összes szöveget, a kill funkciót

használjuk. A fenti esetben ez ^U, azaz Ctrl+U. Ha ki akarunk lépni egy futó programból, az intr funkciót használjuk, ami általában Ctrl+C. stty echo, stty –echo: az elsőt beállítva a leütött billentlyűk megjelennek a

képernyőn, a második esetben (--szal) nem, ilyenkor tehát vakon kell gépelnünk.

raw és cooked stty raw, vagy stty -cooked esetén nem dolgozza fel a rendszer a vezérlő

karaktereket (pl. a kill, erase stb.-t sem), stty –raw vagy stty cooked esetén feldolgozza. További részletetek: http://www.szabilinux.hu/ufi/13_1.html#egy ill. man parancs.

fontos A bash raw módot kapcsol, ezért valójában a kill és az erase nem az, amit beállítunk. Viszont

ha egy programot elindítunk (pl. cat), már az stty-nal beállított értékek vannak érvényben.

1.3 Alapvető parancsok

További parancsokért, illetve a parancsok további beállításaiért lásd [1,2,3]. man

A UNIX elektronikus kézikönyvében (manual) kereshetünk vele. ls

Kilistázza a mappa tartalmát. Néhány kapcsolója: -a: minden állományt listáz -l: részletes adatokat közöl az állományokról -r: fordított sorrendben listáz -t: időrendben listáz A kapcsolókat össze is vonhatjuk. Pl.: ls –alrt.

cat Összefűzi a paraméterként kapott fájlokat, és kiírja azokat a standard kimenetre. Ha nem

írunk paramétert, vagy --t írunk, a standard bemenetről olvas EOF-ig. (Az EOF-ot is beállíthatjuk a stty-nal).

Fájl kiírása a képernyőre: cat fajlnev Két fájl összefűzése: cat fajlnev1 fajlnev2 > fajlnev3 Az eredmény a fajlnev3-ba kerül.

Page 7: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

7

echo A paraméterként kapott szöveget megjeleníti a standard outputon.

cd Change directory: mappaváltás. cd .. : egy mappával feljebb cd / : a gyökérbe cd mappanev : a mappanev nevű mappába

pwd Print working directory: kiírja a munkamappa (az aktuálisan használt mappa) nevét.

passwd Új jelszót adhatunk meg.

rm, rmdir Fájl ill. mappa eltávolítására. rm * : kitörli az aktuális mappából az összes fájlt. (Vigyázat, nincs kuka, ahonnan

visszaállítsuk!) mkdir

Új mappa létrehozása. mv

Fájl mozgatása vagy átnevezése. more

Fájl kiírása a standard outputra. cp

Fájl másolása. -r vagy –R: rekurzív másolás, azaz ha a forrás mappa, annak tartalmát a célmappába másolja

chmod Fájl vagy mappa védelmi kódjának módosítása. Csak a tulajdonos, vagy rendszergazda

változtathatja. chmod g-rwx fájlnév : megtiltja az írást, olvasást és végrehajtást a csoporttársaknak chmod 0764 fájlnév : a tulajdonos írhat, olvashat, végrehajthat, a csoporttagok

olvashatnak és írhatnak, a többi felhasználó csak olvashat. Feladat: http://www.iit.bme.hu/~szebi/unixlab/vedelem.html

ln Link vagy szimbolikus link létrehozása. ln forrasfajl ujnev : létrehozza az ujnev nevű linket forrásfájlra. A kettőnek

fizikailag azonos disken kell lennie. ln –s forrasfajl ujnev : létrehozza az ujnev nevű szimbolikus linket forrásfájlra. A

kettőnek nem kell fizikailag azonos disken lennie. df

Display free disk space du

Disk usage. Feladatok: http://www.iit.bme.hu/~szebi/unixlab/feladat1.html A haladók megpróbálhatják első szorgalmi házijukat elérni. Fájlok feltöltésére jól használható a WinSCP nevű program, melyben commanderhez

hasonlóan tudunk fájlokat másolni.

Page 8: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

8

2. Integrált fejlesztőkörnyezet [labor] A laboron tetszőleges korszerű C++ fordító használható (a borland C++ nem ilyen). A

jegyzetben a Visual C++ .NET 2003-mal találkozhatunk majd. Ebben kicsit másképpen kell létrehozni a Console Application projektjét.

1. lépés:

2. lépés:

3. lépés:

Page 9: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

9

4. lépés:

5. lépés:

Page 10: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

10

3. C ismétlés [gyakorlat]

3.1. Feladat

Készítse el a „Hello Világ!” programot szabványos C nyelven, pontosvessző használata nélkül!

3.2. Feladat

Készítsen szabványos C programot, mely a paraméterként kapott szöveges fájlt olyan formában írja a képernyőre, hogy csak a magánhangzók, és a soremelések maradnak a helyükön, az összes többi helyett szóköz jelenjen meg!

Egy megoldás:

//******************************************************* #include <stdio.h> #include <stdlib.h> //******************************************************* //******************************************************* void error(const char * s){ //******************************************************* printf("%s",s); exit(-1); } //******************************************************* void main(int argc,char ** argv){ //******************************************************* int c; FILE * fp; if(argc<2)error("Tul keves parameter, inditaskor adja meg a hivando fajl nevet!"); if((fp=fopen(argv[1],"rt"))==NULL)error("Nem tudom megnyitni a fajlt"); while((c=getc(fp))!=EOF){ switch(toupper(c)){ case 'A': case 'E': case 'I': case 'O': case 'U': case '\n':fprintf(stdout,"%c",c);break; default: fputc(' ',stdout); } } }

A programban nem használtuk a feof() függvényt, mert utólag működik, ami azt jelenti,

hogy akkor jelzi, hogy a fájl végére értünk, ha már egy művelet sikertelen volt. Emiatt jobban járunk, ha a művelet sikertelenségére figyelünk.

Beolvasásnál használhattuk volna a fscanf() függvényt is, ebben az esetben nem azt nézzük, hogy a visszatérési érték egyenlő-e az EOF konstanssal, mert sohasem lesz egyenlő, hanem azt, hogy a visszatérési érték megegyezik-e a beolvasni kívánt változók számával.

3.3. Feladat

a) Írjon függvényt, mely kiszámítja két egész szám összegét és négyzetösszegét! b) Írjon programot, mely egészekből álló számpárokat olvas a standard inputról, meghívja a

fenti függvényt, kiírja az összeget és négyzetösszeget, egész addig, míg az egyik beolvasott szám 0 nem lesz. Nem használhat globális változókat!

Egy megoldás:

Page 11: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

11

//******************************************************* #include <stdio.h> #include <stdlib.h> //******************************************************* //******************************************************* void osszegzo(int a,int b,int * sum,int * nsum){ //******************************************************* *sum=a+b; *nsum=a*a+b*b; } //******************************************************* void error(const char * s){ //******************************************************* printf("%s",s); exit(-1); } //******************************************************* void main(){ //******************************************************* int a,b,s,ns; fflush(stdin); if(scanf("%d %d",&a,&b)!=2)error("Sikertelen beolvasas"); while(a!=0&&b!=0){ osszegzo(a,b,&s,&ns); printf("ossz=%d\tnegyzetossz=%d\n",s,ns); fflush(stdin); if(scanf("%d %d",&a,&b)!=2)error("Sikertelen beolvasas"); } }

Olvassuk el a C jegyzet 7.1.2. fejezetében a veremkezelésről szóló részt!

Mi történik, ha lemarad az & jel a függvényhívásnál?

3.4. feladat

Írjon függvényt, mely kiszámítja két, paraméterként kapott double típusú szám szorzatát! Írjon függvényt, mely kiszámítja két, paraméterként kapott, stringként megadott valós szám

szorzatát, és az eredményt egy paraméterként kapott karaktertömbben adja vissza! Egy megoldás:

//******************************************************* void mul(const char * a,const char * b,char * c){ //******************************************************* double d1=0,d2=0,d3; sscanf(a,"%lg",&d1); sscanf(b,"%lg",&d2); d3=d1*d2; sprintf(c,"%g",d3); }

3.5. feladat

Írjon programot, mely tartalmaz egy struktúra típust: //******************************************************* struct String{ //******************************************************* char * p; int len; };

Készítsen függvényt, mely • összefűz két ilyen Stringet • visszaadja a String valahányadik karakterét (azaz indexeli) • létrehoz egy ilyen típusú változót egy char * típusú stringből • létrehoz egy ilyen típusú változót egy karakterből

Page 12: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

12

• kiír egy ilyen Stringet • kitörli a paraméterként kapott Stringből a dinamikus tömböt

Készítsen main függvényt, melyben ezt kipróbálja! Nem használhat globális változókat! Egy megoldás:

//******************************************************* #include <stdio.h> #include <stdlib.h> #include <string.h> //******************************************************* //******************************************************* struct String{ //******************************************************* char * p; int len; }; //******************************************************* void error(const char * s){ //******************************************************* printf("%s",s); exit(-1); } //******************************************************* String osszefuz(const String a,const String b){ //******************************************************* String c; int n=a.len+b.len+1; c.len=n-1; c.p=(char *)malloc(n*sizeof(char));//a sizeof(char) definíció szerint 1 if(c.p==NULL)error("osszefuz -> Memoriafogalalas nem sikerult!"); for(int i=0;i<a.len;i++)c.p[i]=a.p[i]; for(int i=0;i<b.len;i++)c.p[i+a.len]=b.p[i]; c.p[c.len]=0; return c; } //******************************************************* char index(const String s,const int n){ //******************************************************* return (n>=0&&n<s.len)?s.p[n]:0; } //******************************************************* String letrehoz1(const char * s){ //******************************************************* String a; a.len=strlen(s); a.p=(char*)malloc(a.len+1);// most nem írjuk ide a sizeof-ot if(a.p==NULL)error("letrehoz1 -> Memoriafogalalas nem sikerult!"); strcpy(a.p,s); return a; } //******************************************************* String letrehoz2(const char c){ //******************************************************* String a; a.len=1; a.p=(char*)malloc(2);// most nem írjuk ide a sizeof-ot if(a.p==NULL)error("letrehoz2 -> Memoriafogalalas nem sikerult!"); a.p[0]=c; a.p[1]=0; return a; }

Page 13: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

13

//******************************************************* void kiir(const String s){ //******************************************************* printf("%s",s.p); } //******************************************************* void torol(String * s){ //******************************************************* free(s->p); s->p=NULL; s->len=0; } //******************************************************* void main(){ //******************************************************* String a=letrehoz1("Sikerult "); String b=letrehoz2('a'); String c=letrehoz1(" letrehozas"); String d=osszefuz(a,b); String e=osszefuz(d,c); kiir(e); torol(&a); torol(&b); torol(&c); torol(&d); torol(&e); }

Ebben a programban már használtunk néhány C++ elemet. C++-ban a struktúra (és az enumé

is) neve típusnak számít, tehát ezzel közvetlenül létre lehet hozni ilyen változót (pl. nem kellett odaírni a main()-ben, hogy struct String a).

A másik: C++-ban bárhol lehet definiálni változót, ahol egyébként utasítás állhat, tehát nem csak blokk elején. Pl.: for(int i=0;i<a.len;i++)c.p[i]=a.p[i];

A szabványos C++-ban a for fejében definiált i változó lokális a for ciklusra nézve, tehát azon kívül nem látszik, ezért szerepelhet a következő sorban egy ugyanolyan definíció. Figyelem! A Visual C++ 6-ban ez nem igaz, ott az így definiált i később is látszik. A későbbi Visual C++ fordítók már elfogadják a szabványos felépítést is, meg a korábbiból örököltet is, ami annyit jelent, hogy a szabványosan megírt program is lefordul, nem szól amiatt, hogy a változót újradefiniáltuk volna, viszont a változó a for cikluson kívül is elérhető marad, ami sajnos egy hibalehetőség.

Page 14: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

14

4. Const, referencia, string [labor]

4.1. Const

Töltsük le Szeberényi tanár úr oldaláról a http://www.iit.bme.hu/~szebi/proga2/const_h.cpp

fájlt!:

// // Állapítsa meg, hogy milyen hibák vannak az alábbi C++ állományban! // Magyarázza meg, hogy az egyes számozott programrészekben mi miért hiba! // Ha valameyik részben nicsn hiba, akkor azt is indokolja! // // 0. void ff(const int *ip) { *ip = 3; } int main(){ // 1. const int i; // 2. const int j = 10; j++; // 3. const int l = 10; int *p=&l; (*p)++; // 4. extern void f(int *i); const int iv = 100; f(&iv); // 5. const int ivv = 100; ff(&ivv); // 6. const int v[] = {1,2,3}; v[1]++; // 7. const int siz = 20; int t[siz]; // 8. char s1[] = "Hello konstans szoveg"; const char *pc = s1; pc[0] = 'A'; pc++; // 9. char s2[] = "Hello konstans pointer"; char* const cp = s2; cp[0] = 'B'; cp++; // 10. char s3[] = "Hello konstans szoveg konstans pointer"; const char* const cpc = s2; cpc[0] = 'C'; cpc++; // 11. enum Szinek { tok, zold, makk, piros }; Szinek adu;

Page 15: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

15

adu = 1; adu = Szinek(10); return(0); }

Találjuk ki, hogy mely sorok hibásak, és miért? Lehetőleg első körben ne használjuk a fordító

segítségét!

4.2. Referencia

Töltsük le Szeberényi tanár úr oldaláról a http://www.iit.bme.hu/~szebi/proga2/ref_h.cpp

fájlt!:

// // Állapítsa meg, hogy milyen hibák vannak az alábbi C++ állományban! // Magyarázza meg, hogy az egyes számozott programrészekben mi miért hiba! // Ha valameyik részben nicsn hiba, akkor azt is indokolja! // // 1. long glob; float fglob; // 2. long& fr(){ return glob; } // 3. int& fifi(){ int a; return a; } // 4. int *fp(){ int a; return &a; } // 5. long& fr(long x){ return x; } // 6. long& fr(long& a){ return a; } // 7. float& fr(long x){ return fglob; } // 8. int szg(int a = 1, float x){ return a * x; } // 9. int main(){ fr()++; fr() = 54; fr() = fr() + 4; return 0; }

Page 16: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

16

Találjuk ki, hogy mely sorok hibásak, és miért? Lehetőleg első körben ne használjuk a fordító segítségét!

4.3. String

Töltsük le Szeberényi tanár úr oldaláról a http://www.iit.bme.hu/~szebi/proga2/string_h.c

fájlt!:

// // Egészitse ki az alábbi C programot a megfelelő helyeken úgy // hogy az működőképes legyen ! // #include <stdio.h> // Kiiratáshoz #include <malloc.h> // Dinamikus memóriakezeléshez #include <string.h> // Stringműveletekhez // A string struktúránk. // A 'p'-ben vannak a karakterek (a lezáró nullával együtt), 'len' a hossza. // (A lezáró nulla léte vagy hiánya implementációs kérdés). struct String { char *p; // pointer az adatra int len; // hossz lezaró nulla nélkül }; // Stringet készít egy karakterből struct String createFromChar(char ch) { struct String temp; // Meghatározzuk a hosszát temp.len = 1; // Lefoglaljuk a helyet + a lezaro nulla helyet temp.p = (char*)malloc(temp.len+1); // if (temp.p == NULL) ... mi tegyunk ? Nagyvonaluan elhagyjuk a vizsgálatot // Betesszuk a karaktert temp.p[0] = ch; temp.p[1] = '\0'; return temp; } // Stringet készít egy karaktertömbből. struct String createFromCharArray(char p[]) { struct String temp; // Meghatározzuk a hosszát #error hossz // Helyet foglalunk #error foglal // Bemásoljuk #error masol return temp; } // Stringet készít egy Stringból struct String createFromString(struct String s) { struct String temp; #error ezt kell megirni return temp; } // Két stringet fűz össze, eredményét egy harmadikba írja. struct String concat(struct String a, struct String b) { struct String temp;

Page 17: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

17

#error ezt kell megirni return temp; } // A string egy megadott indexű elemével tér vissza. // Ha az index a [0..hossz-1] tartományon kívül van, '\0' karakterrel tér vissza. char index(struct String str, int index) { #error ezt kell megirni } // Hozzáad egy karaktert a stringhez void addChar(struct String *str, char ch) { // Növeljük a helyet: régi karakterek + új karakter + lezáró nulla str->p = (char*)realloc(str->p, str->len + 2); // if (temp.p == NULL) ... mi tegyunk ? Nagyvonaluan elhagyjuk a vizsgálatot // A megnövelt helyre beírjuk az új karaktert és a lezáró nullát. *(str->p + str->len) = ch; *(str->p + str->len + 1) = '\0'; // Megnöveljük a hosszt. str->len++; } // Kiírunk egy stringet void print(struct String str) { printf("%s", str.p); } // Kiírunk egy stringet (debug célokra) void print_dbg(struct String str) { printf("[%d], %s\n", str.len, str.p); } // Ha dinamikus memóriát használunk, gondoskodni kell a felszabadításról is. void dispose(struct String str) { free(str.p); } int main() { struct String a,b,c,d, e; a = createFromChar('x'); print_dbg(a); b = createFromCharArray("alma"); print_dbg(b); c = concat(a, b); print_dbg(c); d = concat(b, c); print_dbg(d); e = createFromString(d); print_dbg(e); printf("%c\n", index(d, 4)); if (index(d, 100) == '\0') printf("Hibas indexeles\n"); addChar(&d, 'a'); print_dbg(d); addChar(&d, 'b'); print_dbg(d); addChar(&d, 'c'); print_dbg(d); dispose(a); dispose(b); dispose(c); dispose(d); dispose(e); return 0; }

Egészítse ki ezt a C programot, hogy működőképes legyen! (a #errorok törlendőek). Készítse el a program C++ változatát!

• A struct név típus értékű. • Memóriafoglalás és felszabadítás operátorral (new, delete), realloc kerülendő! • Minden createFrom fv. helyett az overloaded create() függvény használandó.

Page 18: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

18

Próbáljuk ki a kivételkezelést a string1.cpp program felhasználásával! Ehhez használhatjuk a UNIX-ot is: g++ -DBAD -o string1 sring1.cpp bash ulimit -d 1000 ./string1 exit

Page 19: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

19

5. Vándorlás C-ről C++-ra [gyakorlat] A bemutatott példák részben [4]-ből származnak.

5.1. C++ kiírás

Írjunk programot, amely kiírja az ’a’..’z’ betűket és a ’0’..’9’ számjegyeket, valamint a

hozzájuk tartozó egész értékeket. Írjuk ki a hozzájuk tartozó egész értékeket hexadecimális alakban is!

#include <iostream> #include <iomanip> using namespace std; void main(){ for(char i='0';i<='9';i++) cout << i << '\t' << int(i) << endl; for(char i='a';i<='z';i++) cout << i << '\t' << int(i) << endl; cout.setf(std::ios_base::showbase); cout.fill(' '); for(char i='0';i<='9';i++) cout << i << setw(12) << dec << int(i) << setw(12) << hex << int(i) << endl; for(char i='a';i<='z';i++) cout << i << setw(12) << dec << int(i) << setw(12) << hex << int(i) << endl; }

Az első változat egyszerű, a cout-ra küldjük a kiírandó változókat. A második esetben azonban formázást is használunk. A cout.setf(std::ios_base::showbase); azt állítja be, hogy hexadecimális

vagy oktális esetben a kimeneten jelenjenek meg a számrendszerre utaló karakterek is a kiírt szám előtt, azaz 0x hexa esetben, és 0 oktális esetben.

A cout.fill(' '); azt mondja meg, hogy mivel legyenek kitöltve a mező üresen maradt karakterei.

A setw(12) az utána kiírt adat szélességét 12 mezőre állítja, az üres helyeket a fenti fill karakterrel (most szóközzel) tölti ki.

A dec ill. a hex azt írja elő, hogy az utána következő egész számok kiírása az adott számrendszerben történjen.

Az endl nem csak a soremelés karaktert írja ki, hanem üríti a kimeneti puffert, azaz az eddig kiírtak biztosan megjelennek a standard outputon.

5.2. Mutatók, tömbök, referenciák

5.2.1. Változók Hozzunk létre változókat, mindegyiknek adjunk kezdőértéket is!: a) Karakterre hivatkozó mutató b) 10 egészből álló tömb c) 10 egészből álló tömb referenciája d) Karakterláncokból álló tömbre hivatkozó mutató e) Karakterre hivatkozó mutatóra hivatkozó mutató f) konstans egész

Page 20: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

20

g) konstans egészre hivatkozó mutató h) egészre hivatkozó konstans mutató

void main(){ //a) char c='x'; char * p=&c; //b) int t[]={1,2,3,4,5,6,7,8,9,10}; //c) int (&rt)[10]=t; //d) char * st[5]; char * (*pst)[5]=&st; //e) char ** ppc=&p; //f) const int k=0; //g) const int * pk=&k; //h) int i=100; int * const cpi=&i;

}

5.2.2. Swap Írjunk egy swap nevű függvényt, amely két egészet cserél fel. Használjunk int* típust a

paraméterek típusaként. Írjunk egy másik swap-et is, melynek paraméterei int& típusúak.

#include <iostream> inline void swap(int * a,int * b){ int t=*a; *a=*b; *b=t; } inline void swap(int & a,int & b){ int t=a; a=b; b=t; } void main(){ int x=1; int y=3; swap(&x,&y); swap(x,y); }

5.3. Függvények

Deklaráljuk a következőket: a) Függvényt, melynek egy karakterre hivatkozó mutató, és egy egészre mutató referencia

paramétere van, és nem ad vissza értéket. b) Ilyen függvényre hivatkozó mutató. c) Függvény, melynek ilyen mutató paramétere van. d) Függvény, mely ilyen mutatót ad vissza. e) Írjuk meg azt a függvényt, amelynek egy ilyen mutatójú paramétere van, és visszatérési

értékként paraméterét adja vissza.

//a) typedef void fvt(char*,int&);

Page 21: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

21

//b) typedef fvt * pfvt; //c) void fv1(pfvt); //d) pfvt fv2(); //e) pfvt fv3(pfvt p){return p;} void main(){ }

5.4. cin, cout, cerr

Íjunk titkosító programot, ami a cin-ről olvas, és a kódolt karaktereket kiírja a cout-ra.

Használjuk a következő, egyszerű titkosító sémát: c karakter titkosított formája legyen c^key[i], ahol key egy karakterlánc, amely parancssori paraméterként adott. A program ciklikus módon használja a key-ben lévő karaktereket, amíg a teljes bemenetet el nem olvasta. Ha nincs megadva a key (vagy a paraméter null karakterlánc), a program ne végezzen titkosítást. Írja ki a titkosítatlan szöveget a cerr-re.

#include <iostream> using namespace std; void main(int argc,char ** argv){ char c; bool kodole=argc>1&&argv[1][0]!=0; char * key=kodole?argv[1]:0; int i=0; while(cin.get(c)){ if(c=='\n')break; if(kodole){ if(key[i]==0)i=0; cout<<char(c^key[i]); } else cout<<c; cerr.put(c); // << c is lehetett volna } }

5.5. Kivétel

Példaprogram, melyben ha a fv() függvényt true paraméterrel hívjuk, kivetel típusú

kivételt vált ki, ha false paraméterrel hívjuk. akkor bad_alloc-ot.

#include <iostream> using namespace std; struct kivetel{}; void fv(bool x){ int n=1024*1024*256; if(x)throw kivetel(); int * a=new int[n]; int * b=new int[n]; int * c=new int[n]; int * d=new int[n]; for(int i=0;i<1024;i++)a[i]=0; for(int i=0;i<1024;i++)b[i]=0; for(int i=0;i<1024;i++)c[i]=0; for(int i=0;i<1024;i++)d[i]=0;

Page 22: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

22

delete [] a; delete [] b; delete [] c; delete [] d; } void main(){ try{ fv(false); } catch(bad_alloc){ cerr<<"Bad alloc"<<endl; } catch(kivetel){ cerr<<"Kivetel"<<endl; } }

5.6. Stack, névtér, kivétel

Helyezze el a MyStack névtérbe a Stack struktúrát, valamint a verem kezeléséhez szükséges

függvényeket (push, pop, init, done). A verem karakterek tárolására legyen alkalmas. Készítse el a függvényeket, a hibák kezelését kivételekkel oldja meg!

#include <iostream> using namespace std; namespace MyStack{ struct Stack{ char * p; int size; int elemszam; }; struct Overflow{}; struct Underflow{}; Stack init(const unsigned n){ Stack s; s.elemszam=0; s.size=n; s.p=new char[n]; return s; } void done(Stack & s){ delete [] s.p; s.elemszam=s.size=0; } char pop(Stack & s){ if(s.elemszam==0)throw Underflow(); return s.p[--s.elemszam]; } void push(Stack & s,const char c){ if(s.elemszam>=s.size)throw Overflow(); s.p[s.elemszam++]=c; } } void main(){ MyStack::Stack s=MyStack::init(20); try{ for(char c='a';c<'z';c++){ MyStack::push(s,c); cout << c << "\tpushed" << endl; } } catch(MyStack::Overflow){

Page 23: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

23

cerr << "\nOverflow" << endl; } catch(MyStack::Underflow){ cerr << "\nUnderflow" << endl; } cout << endl; try{ while(1){ cout << MyStack::pop(s) << "\tpopped" << endl; } } catch(MyStack::Overflow){ cerr << "\nOverflow" << endl; } catch(MyStack::Underflow){ cerr << "\nUnderflow" << endl; } MyStack::done(s);

}

Page 24: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

24

6. Osztályok és operátorok [gyakorlat]

6.1. Osztály struct-ból

6.1.1. Osztály Készítsünk komplex számok tárolására alkalmas osztályt! struct complex_1{ double im,re; };

Íme.

6.1.2. Objektumok és szorzás Hozzunk létre három ilyen komplex szám objektumot, kettőnek adjunk kezdőértéket, és

tegyük a harmadikba a kettő szorzatát, majd írjuk ki a standard outra! void main(){ // complex_1 complex_1 a1,b1,c1; a1.re=3.3; a1.im=0.0; b1.re=-5.1; b1.im=0.3; c1.re=a1.re*b1.re-a1.im*b1.im; c1.im=a1.im*b1.re+a1.re*b1.im; cout << "a1=" << a1.re << ',' << a1.im << 'i' << endl; cout << "b1=" << b1.re << ',' << b1.im << 'i' << endl; cout << "c1=" << c1.re << ',' << c1.im << 'i' << endl;

Ebben semmi új nincs eddig. Látható, hogy elég hosszú kódot kellett írnunk. Első lépésben a

szorzást helyettesítsük függvényhívással. Ehhez készítsük el a mul függvényt! complex_1 mul(const complex_1 a,const complex_1 b){ complex_1 c; c.re=a.re*b.re-a.im*b.im; c.im=a.im*b.re+a.re*b.im; return c; }

Ha a paramétereket konstansként adjuk meg, akkor nem fordulhat elő, hogy véletlenül

valamit felülírunk, és elképzelhető, hogy a fordító hatékonyabb kódot hoz létre. A mul függvény paraméterként két darab komplex szám objektumot kap. Egy ilyen objektum

16 bájt méretű, mert két darab nyolcbájtos double számot tartalmaz. Lehet-e csökkenteni az adatforgalmat? Igen, ha nem érték szerint adjuk át a paramétert, hanem cím szerint, vagyis referenciával. Még gyorsabbá tehetjük a kódot, ha inline függvényt használunk. Ehhez nem kell mást tenni, mint a függvény neve elé írni, hogy inline.

inline complex_1 mulr(const complex_1 & a,const complex_1 & b){ complex_1 c; c.re=a.re*b.re-a.im*b.im;

Page 25: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

25

c.im=a.im*b.re+a.re*b.im; return c; }

Lehetne a visszatérési érték is referencia? Így nem, hiszen c lokális változó. A fenti két függvény használata: // szorzás függvénnyel complex_1 d1; d1=mul(a1,b1); cout << "d1=" << d1.re << ',' << d1.im << 'i' << endl; // szorzás referencia faraméter, inline függvény complex_1 e1; e1=mulr(a1,b1); cout << "e1=" << e1.re << ',' << e1.im << 'i' << endl;

De ez a szorzás még nem az igazi. Az igazi az volna, ha megtehetnénk, hogy így írjuk fel a

szorzást: e1=a1*b1;. Van erre lehetőség? Igen, a C++ nyelvben lehetséges az operátorok túlterhelése:

inline complex_1 operator * (const complex_1 a,const complex_1 b){ complex_1 c; c.re=a.re*b.re-a.im*b.im; c.im=a.im*b.re+a.re*b.im; return c; }

Mely operátorok terhelhetők túl? Majdnem az összes, beleértve pl. a new-t, és a delete-t is.

Egyszerűbb azokat felsorolni, amelyek nem terhelhetők túl: • :: hatókör operátor • . tagkiválasztás • .* tagkiválasztás a tagra hivatkozó mutatón keresztül • ?: • sizeof • typeid Csak a meglévő operátorok használhatók, újat nem hozhatunk létre (azaz pl. nem gyárthatunk

>>> operátort stb.). A 2-es pontban látni fogjuk, hogy ezt az operátort megvalósíthattuk volna tagfüggvényként is. Az operátor függvény paraméterei közül az első az operátor bal oldalán szereplő, a második a

jobb oldalán szereplő változót jelenti. A paraméterek legalább egyike saját típus kell legyen, hiszen az alaptípusokhoz tartozó operátorok már definiálva vannak (pl. két double szorzása).

// a * operátor túlterhelése complex_1 f1; f1=a1*b1; cout << "f1=" << f1.re << ',' << f1.im << 'i' << endl; f1=operator*(a1,b1); // így nincs értelme az operátort használni, de lehet

Page 26: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

26

6.1.3. Szorzás valóssal Mi a helyzet akkor, ha nem két komplex számot akarunk összeszorozni, hanem egy

komplexet egy valóssal? Akkor ezekhez a szorzásokhoz is el kell készíteni a megfelelő operátor függvényeket (vagy megfelelő típuskonverziós konstruktort kell írni, de erről később).

inline complex_1 operator * (const complex_1 & a,const double b){ complex_1 c; c.re=a.re*b; c.im=a.im*b; return c; } inline complex_1 operator * (const double a,const complex_1 & b){ complex_1 c; c.re=a*b.re; c.im=a*b.im; return c; } Figyeljük meg, hogy a double típusra nem használtuk a & referencia operátort. Miért? Mert

az alaptípusnál gyakran előfordul, hogy konstanssal adjuk meg, pl.: // valóssal szorzás complex_1 g1,h1; g1=2.8*b1; cout << "g1=" << g1.re << ',' << g1.im << 'i' << endl; h1=b1*2.8; cout << "h1=" << h1.re << ',' << h1.im << 'i' << endl; Ez saját osztályainknál nyilván nem fordul elő.

6.1.4. Abszolút érték Készítsünk abszolút értéket számító globális függvényt és tagfüggvényt! A globális függvény így néz ki: inline double abs(const complex_1 c){ return sqrt(c.re*c.re+c.im*c.im); }

Természetesen az inline elhagyható. A tagfüggvényként történő megvalósítás egy teljesen új elem az eddig tanultakhoz képest. A

tagfüggvény az osztályon belül kerül megvalósításra, ehhez tehát a következőképpen egészítjük ki a complex_1 osztályt:

struct complex_1{ double im,re; double abs()const{return sqrt(im*im+re*re);} };

A következő dolgokat vehetjük észre: • A függvény nem kap paramétert, mert a struktúra adattagjaival dolgozik.

Page 27: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

27

• A () után szerepel a const szó. Ezzel azt jelezzük, hogy a függvény nem bántja a struktúra adattagjait, csak olvassa őket. Akkor sincs probléma, ha ezt elfelejtenénk odaírni.

• Az im és re adattagok a függvény számára olyanok, mintha globális változók lennének, tehát nem valami.im ill. valami.re-ként hivatkozunk rájuk.

• A struktúrán belül írtuk az abs függvény kódját. Ezt külön is lehet, később majd látjuk, hogyan. Ha így, a struktúrán belül adjuk meg, a függvény inline lesz, anélkül, hogy ezt külön odaírnánk. (Ez nem baj, az inline-nak csak előnye van, hátránya nincs.)

Használat: // abs double i1=abs(b1); double j1=b1.abs(); cout << "i1=" << i1 << endl; cout << "j1=" << j1 << endl;

Mindkét megoldás jó, kinek melyik szimpatikus, azt használja. A tagfüggvényt ugyanúgy

objektumnév.tagfüggvény() módon érhetjük el, mint a tagváltozókat.

6.1.5. Házi feladat a) Készítsen +, -, / operátort az osztályhoz! b) Készítsen arg() globális és tagfüggvényt, mely a komplex vektor szögét adja vissza!

6.2. Osztály class-ból

6.2.1. Osztály Készítsünk komplex számok tárolására alkalmas osztályt! class complex_2{ double im,re; };

Ez eddig ugyanúgy néz ki, mint az előbb, de próbáljuk csak beírni, hogy complex_2 a; a.re=3.3;

! A fordító hibaüzenettel fog válaszolni, nem érhetjük el a re tagváltozót! class complex_1{ public: double im,re; }; Ha így módosítjuk, akkor az osztályunk és változóink éppen úgy viselkednek, mint a 6.1.

pontban. Akár át is írhatjuk az ott közölt programot erre.

Page 28: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

28

A public az utána következő változókat és tagfüggvényeket kívülről hozzáférhetővé, publikussá teszi. Ennek ellentéte a private, ami az utána következő változókat és tagfüggvényeket kívülről hozzáférhetetlenné teszi. struct esetén is alkalmazható:

struct complex_2{ private: double im,re; }; Egy osztályon belül tetszőleges számú public ill. private rész lehet. Mi a különbség a class és a struct között? Egyetlen különbség van, class esetén az

alapértelmezett állapot private, míg struct esetén public. Egyébként bármelyik használható.

Miért jó, ha private? A komplex osztályunk esetén ez annyira nem nyilvánvaló, de például gondoljuk el, hogy az osztályunk egy ablakot ír le a képernyőn.

Az adattagok legyenek mondjuk a bal alsó sarok koordinátái, valamint az ablak szélessége és magassága pixelben. Mi történik, ha kívülről módosítjuk a szélességet? Az ablak a képernyőn nem fog változni, de a szélességét tároló adatmező nem a valódi szélességet fogja tükrözni. Ha az ablakot kezelő függvények feltételezik, hogy a szélesség érték jó, akkor rosszul fognak működni.

Megoldás: nem engedjük meg, hogy kívülről belepiszkáljanak az adatainkba. Készítünk ehelyett egy tagfüggvényt, mondjuk SetWidth() néven, amivel átállíthatjuk a szélességet. Ez nem csak a változó értékét fogja módosítani, hanem ténylegesen át is méretezi az ablakot, és minden bizonnyal pont erre van szükségünk. Tehát az ablak objektumszerű lesz: azt mondjuk neki, hogy módosítsd a szélességedet, és ő módosítani fogja. Ez az ablak-objektum használója számára nagyon kényelmes, hiszen őt nem is érdekli, hogy belül hogyan is történik a szélesség megváltoztatása. Erről szól az objektumorientált programozás!

A továbbiakban igyekszünk ahhoz a szabályhoz tartani magunkat, hogy minden tagváltozót

priváttá teszünk. Ettől csak akkor térjünk el, ha ez biztosan nem okoz problémát, és jelentősen leegyszerűsíti a dolgunkat. Az osztályaink ezentúl általában class-ok lesznek.

6.2.2. Konstruktor Készítsünk konstruktort a komplex osztályhoz! Mi a konstruktor? Ez egy tagfüggvény, mely akkor fut le, amikor létrehozunk egy változót,

azaz objektumot az adott osztályból. Arra használjuk, hogy kezdőértéket adjunk az objektum tagváltozóinak.

Például: complex_2 b=3.0;

Ha nem készítünk konstruktort, akkor a fenti kód hibaüzenetet okoz. A konstruktor tehát a

következő lesz: class complex_2{ double im,re; public: complex_2(const double d){im=0.0;re=d;} };

Page 29: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

29

A konstruktor neve megegyezik az osztály nevével, és nincs visszatérési típusa. (Nem void, hanem egyáltalán nincs!) Ugyanezt másképp is hívhatjuk:

complex_2 b1(3.0);

A két hívási mód teljesen ekvivalens egymással, éppen úgy, ahogy a 6.1-ben a * operátor

kétféle meghívását láthattuk. Természetesen előfordulhat, hogy nem double számból akarunk komplexet konstruálni,

hanem másból. Lássunk négy ilyet: class complex_2{ double im,re; public: complex_2():im(0.0),re(0.0){} // alapértelmezett (default) konstruktor complex_2(const double d){im=0.0;re=d;} complex_2(const double Im,const double Re){im=Im;re=Re;} complex_2(const complex_2 & c):im(c.im),re(c.re){} // másoló (copy) konstruktor

Az alapértelmezett konstruktor akkor hívódik, ha nem adunk meg kezdőértéket az objektum

létrehozásakor, pl.: complex_2 a;

Ha egyáltalán nem írunk konstruktort (egyet sem) az osztályunkhoz, akkor kapunk hozzá

alapértelmezettet, ami nem csinál semmit. Ezért működött a 6.1-ben bemutatott osztály. DE ha készítettünk bármilyen konstruktort, akkor az alapértelmezett nem jön létre, tehát azt is meg kell írnunk, ha a fenti formában objektumot akarunk létrehozni.

Figyelem! Ha dinamikusan foglalunk le, mondjuk egy komplex számokból álló tömböt, és a

new operátort használjuk, akkor a new meghívja az alapértelmezett konstruktort, a malloc nem hív semmit, ezért ne használjunk malloc-ot és társait!

Mi ez a :im(0.0),re(0.0){}? Így hívhatjuk meg az osztály adattagjainak

konstruktorát. Az egyszerű típusoknál tulajdonképpen nem volna szükség erre a módszerre, hiszen itt megfelelne az egy darab double paraméterrel rendelkező konstruktornál látott módszer is, de előfordulhat, hogy a tagváltozónk egy objektum, melynek nem az alapértelmezett konstruktorát kívánjuk hívni (enélkül az alapértelmezett konstruktor hívódik meg, ezt nem is kell jelezni).

A harmadik konstruktor két darab double számból kreál egy komplexet. A negyedik pedig egy ugyanilyen típusú komplex számból. Ezt hívják másoló (copy)

konstruktornak. Figyelem! A másoló konstruktor paramétere kötelezően referencia! Mi ennek az oka?

Nagyon egyszerű: ha egy értéket adunk át függvénynek, akkor ott másoló konstruktor hívás

történik! Ha tehát nem írnánk oda a & jelet, akkor a konstruktor saját magát akarná hívni, ami saját magát akarná hívni, ami saját magát akarná hívni… Így viszont csak egy referencia, azaz pointer másolása történik.

A 6.1. fejezetben láttunk olyat, hogy egy függvényt objektum paraméterrel hívtunk, konkrétan az abs() függvényt. Itt nem történt másoló konstruktor hívás? Dehogynem! Ha nem

írunk mi magunk másoló konstruktort, akkor magától keletkezik egy olyan, ami bájtról bájtra átmásolja az objektumot az új objektum területére. Ez általában gyorsabb is, mintha mi magunk írnánk meg, viszont dinamikus adattagok esetén nem alkalmazható (látni fogjuk a string osztálynál).

Page 30: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

30

Lássunk példákat a konstruktorok használatára: void main(){ complex_2 a; complex_2 b=3.0; complex_2 b1(3.0); complex_2 c(2.2,-1.8); complex_2 d(c);

6.2.3. Egyenlőségjel operátor Adjunk = operátort az osztályunkhoz! complex_2 & operator=(const double d){re=d;im=0.0;return *this;} complex_2 & operator=(const complex_2 & c){re=c.re;im=c.im;return *this;}

Ezúttal az egyenlőségjel operátor, mint tagfüggvény szerepelt. Lehetett volna globális

függvényként is létrehozni, ahogyan a 6.1-ben a * operátort, ekkor azonban problémát jelentett volna, hogy a tagváltozók privátok. Erre is van megoldás, ezt látni fogjuk a << és >> operátornál.

Lehetőleg tagfüggvény operátorokat használjunk. Van, amikor ez nem megoldható, ugyanis a tagfüggvény operátor esetén az operátor bal oldalán álló érték maga az objektum, míg az operátor jobb oldalán álló objektum a paraméterként megadott. Ez azt jelenti, hogy pl., ha egy olyan = operátort szeretnénk létrehozni, ahol double=complex, akkor a double osztályt kéne kibővíteni ezzel a tagfüggvénnyel, pedig double osztály nincs is. Ehelyett muszáj globális operátort használni.

Használat: b1=c; d=8.2;

Végre nem kell d.re=8.2;d.im=0.0; módon szenvedni! Ne kreáljunk egyszerre ugyanolyan célra jó globális és tagfüggvény operátort is, mert akkor a

fenti hívás esetén a fordító nem tud dönteni közülük! További magyarázatra szorul az operátor visszatérési értéke és a return *this;. A C-

ben, és így a C++-ban is megengedett az a=b=c; típusú többes értékadás. Ez azt jelenti, hogy a=(b=c);, azaz az a a b=c visszatérési értékét kapja. Meg lehetett volna void visszatérési értékkel is oldani az = operátort, ekkor azonban a hármas értékadás hibaüzenetet adna.

A visszatérési érték nem kell, hogy referencia legyen, de ha nem az, akkor egy felesleges adatmozgatás is bekövetkezne, ugyanis akkor a másoló konstruktor létrehozna egy ideiglenes változót a b=c eredményének, és aztán ez kerülne a-ba.

Mi a this? Nos, pl. az egyik konstruktornál azt láthatjuk, hogy im=0.0;. A programban egy ilyen konstruktor függvény van, de rengeteg olyan változó (azaz objektum) lehet, amire ezt meghívjuk. Honnan tudja a konstruktor, hogy melyik adatokon kell dolgoznia? Onnan, hogy a programkódban nem láthatóan megkapja annak a struktúrának a memóriacímét, amelyiken dolgozik. Ez a memóriacím a this. A fordítóprogram minden olyan helyre, ahol egy tagfüggvényben az objektum változóját használjuk, odaszerkeszti, hogy this->, azaz valójában this->im=0.0; fordítódik le. (Ha akarjuk, mi is írhatjuk így, csak ez pluszmunka.) Előfordulhatnak helyzetek, amikor szükségünk van az objektumunk címére, a fenti is ilyen. Ekkor ugyanis a *this a b változót adja vissza, és mivel a visszatérési érték referencia, a b=c visszatérési értéke a b-re mutató referencia lesz, ez kerül tehát az a= jobb oldalára.

Page 31: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

31

6.2.4. Kiírás és beolvasás friend istream & operator>>(istream & s,complex_2 & c); friend ostream & operator<<(ostream & s,const complex_2 & c){ s << c.re << ',' << c.im << 'i';return s; } A standard könyvtár készítői a kiírásra ill. beolvasásra a biteltolás operátorok túlterhelését

választották. Ebből is látszik, hogy egy túlterhelt operátorral bármit megtehetünk, mégis célszerű ragaszkodni az eredeti funkciójához, különben magunk sem fogjuk érteni a programunkat.

Most csak a kiírást nézzük részletesebben. Ezt így használhatjuk: cout << "a=" << a << endl; a=b*c; cout << "a=" << a << endl;

Mint láthatjuk, ugyanúgy működik, mint az egyszerű típusok esetén. Egyszerű típusok esetén

azért működik, mert az ostream osztályban elkészítették az összes egyszerű típushoz a << operátor túlterhelt változatát.

Mivel a << operátor bal oldalán áll az ostream osztályú objektum (jelen esetben a cout), ha tagfüggvényként akarnánk megvalósítani, az ostream osztályba kellene beletenni, ez azonban lehetséges ugyan, de nagyon rossz megoldás volna, hiszen így beleírnánk egy rendszerkönyvtárba, ami abszolút hordozhatatlanná (vagy legalábbis igen nehezen hordozhatóvá) tenné a programunkat.

Ehelyett globális << operátort használunk. Mit keres akkor ez az operátor a complex_2 osztályon belül?

EZ A << OPERÁTOR NEM TAGFÜGGVÉNY! Annak ellenére nem az, hogy úgy tűnik ránézésre, mintha az lenne. Azért nem az, mert a neve előtt szerepel, hogy friend. Ha egy osztályon belül megadjuk egy másik osztály, vagy egy globális függvény deklarációját (ami, mint ebben az esetben is, lehet definíció is) friend-ként, akkor az adott osztály tagfüggvényei, ill. az adott függvény hozzáférhet az osztály privát változóihoz és tagfüggvényeihez.

Jelen esetben ezért érheti el a << operátor a c.im-et és a c.re-t. A kiírás úgy történik, hogy a kiírandó adatot elemi adatokra (int, double, char, char*) bontjuk,

és így, darabonként küldjük a kimenetre, ami azt jelenti, hogy darabonként hívjuk meg rá az ostream-ben definiált túlterhelt << operátor függvényeket.

A visszatérési érték ostream referencia, hiszen a kiírásokat egymás után lehet láncolni. Az érdekesség az, hogy a láncolás balról jobbra történik, nem jobbról balra, mint az = operátornál láttuk.

6.2.5. Nem inline tagfüggvény Eddig csak inline tagfüggvényeket láttunk, vagyis olyanokat, melyeket az osztályon belül

nem csak deklaráltunk, hanem definiáltunk is. Most a * operátort valósítsuk meg úgy, hogy ne legyen inline!

Ekkor az osztályban csak a függvény deklarációja szerepel: complex_2 operator*(complex_2 & c);

A definíciót külön adjuk meg: inline complex_2 complex_2::operator *(complex_2 & c){ return complex_2(re*c.re-im*c.im,re*c.im+im*c.re); }

Page 32: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

32

Itt láthatjuk, hogy a függvény nevét megelőzi az osztály neve, és a hatókör operátor (::). Ha azt akarjuk, hogy inline legyen, akkor ezt külön oda kell írni. Ha elhagyjuk az inline kulcsszót, akkor megkapjuk a nem inline tagfüggvényt. Ez akár külön cpp modulba is kerülhet.

Ahogy elhangzott, a friend -ként megadott függvény nem tagfüggvény, hanem globális,

ezért friend istream & operator>>(istream & s,complex_2 & c);

definíciója nem tartalmazza az osztály nevét és a hatókör operátort, mert nem tagfüggvény, hanem globális. (Remélem, világos!):

istream & operator>>(istream & s,complex_2 & c){ s >> c.re; cin.ignore(1); s >> c.im;cin.ignore(1); return s; }

A cin.ignore(1) egy karaktert átugrik, tehát 3.2e-5,-12.4i alakban várja a számokat.

6.2.6. Konstruktorok összevonása Használjunk alapértelmezett paraméter értékeket, ekkor leegyszerűsíthetjük a

konstruktorírást. Komplex osztályunknál négy helyett elég lesz kettő: class complex_3{ double im,re; public: complex_3(const double Im=0.0,const double Re=0.0):im(Im),re(Re){} complex_3(const complex_2 & c):im(c.im),re(c.re){} };

6.2.7. Házi feladat a) Készítsen további operátorokat és függvényeket a komplex osztályhoz (pl. +,-, előjel -,

előjel +, abszolút érték, stb.) b) Alakítsa át a komplex osztályt úgy, hogy abszolút értéket és szöget tároljon c) Készítsen három dimenziós vektor osztályt, hozzá +,-, és * operátort, ahol a * skaláris

szorzás legyen, tehát visszatérési értéke double.

Page 33: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

33

7. String osztály [labor] Ezalkalommal az óra anyaga teljes egészében a Szeberényi tanár úr által javasolt anyag.

Feladatok

A feladat egy saját dinamikus String osztály elkészítése. A megoldáshoz segítségül

felhasználható a string2.cpp forrás, amely a feladat osztály nélküli megvalósítása. A megoldás során adott a string_class_h.cpp forrás, amely a megoldás vázát képezi, de az osztály nélküli megoldás függvényei találhatók benne. Ezeket kell módosítani az osztály elkészítésekor.

A megoldást ütemezett. Ezt az ELKESZULT makró segíti, melynek növekvő értéke a

következő ütem elkészítését jelenti. Az ütemezés a következő: 1. konstruktor C stringből 2. konstruktor karakterből 3. másoló konstruktor 4. értékadó (=) operátor 5. összeadó (+) operátor, ami a feladatban két string összefűzését jelenti 6. az összeadó operátor definiálása karakter hozzáfűzéshez 7. a += operátor definiálása karakter hozzáadáshoz 8. index operátor definiálása 9. UPPERCASE kiírás lehetősége egy érték megadásával

string_class_h.cpp

/* * Dinamikusan nővekedni tudó String osztály. * Feladat: * A korábbi gyakorlatokon készített Sring struktúra megvalósítása osztállyal. * A feladat megoldásához előre elkészítettük a következőket: * 1. Megadtuk az osztály és tagfüggvényeit * 2. Definiáltuk kiiró operatort, amit majd át kell irnia, de * tesztelés megkezdhető átírás nélkül is. * 3. Definiáltuk a main függvényt * 4. Definiáltuk a ststikus tagokat (ez csak a 9. függvényhez kell) * * El kell készíteni a további tagfüggvényeket, melyeknek funcióban megfelő * párját segítségül meghagytuk a korábbi strutúrával megvalósított változatból. * (A feladat megoldását segíti, ha tanulmányozza a 4. előadás diáit!) * */ /* * Az alabbi define segit a programm fokozatos kiprobalasában. * Értékét definiálja annak megfelelően, hogy sorrendben hány * tagfüggvényel készült el. */ #define ELKESZULT 0 #include <iostream> using namespace std;

Page 34: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

34

/** * String osztály. * * String osztály, ami dinamikusan vaáltoztatja a méretét. * A p-ben vannak a karakterek (a lezáró nullával együtt), * len hosszban nincs bene a lezáró nulla. */ class String { char *p; // pointer az adatra int len; // hossz lezáró nulla nélkül #if ELKESZULT > 8 static bool ucase; // kiirasnál csupa nagybetűvel ír #endif public: String(const char *s = ""); String(const char ch); String(const String &s); String operator=(const String& s); String operator+(const String& s); String operator+(const char ch); String operator+=(const char ch); char& operator[](int i); friend ostream& operator<<(ostream& os, const String& s); #if ELKESZULT > 8 static bool SetUcase(bool b = true); #endif ~String(); }; #if ELKESZULT > 0 /// Stringet készít egy nullával lezárt karaktersorozatból. #error "ide jön az egyik konstruktor" String create(char *p) { String temp; // Meghatározzuk a hosszát temp.len = strlen(p); // Helyet foglalunk temp.p = new char[temp.len+1]; // Bemásoljuk strcpy(temp.p, p); return temp; } ///< Ha dinamikus memóriát használunk, gondoskodni kell a felszabadításról is. #error "ide jön a destruktor" void dispose(String str) { delete[] str.p; } #endif #if ELKESZULT > 1 ///< Stringet készít egy karakterből #error "ide jön egy masik konstruktor" String create(char ch) { String temp; // Meghatározzuk a hosszát temp.len = 1; // Lefoglalunk a helyet a hossznak + a lezaro nullának temp.p = new char[temp.len+1]; // Betesszük a karaktert temp.p[0] = ch; temp.p[1] = '\0'; return temp; } #endif #if ELKESZULT > 2 ///< Stringet készít egy Stringból #error "ide jön a másolo konstruktor" String create(String s) { String temp; temp.len = s.len; // Helyet foglalunk temp.p = new char[s.len+1]; // Bemásoljuk

Page 35: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

35

strcpy(temp.p, s.p); return temp; } #endif #if ELKESZULT > 3 ///< Egy stringet bemásol egy másikba (értékadás) #error "ide jön az ertékadó operátor" void ertekadas(String& s1, String& s2) { if (&s1 != &s2) { // felszabaditjuk a régit delete[] s1.p; // beállítjuk a hosszt s1.len = s2.len; // lefoglalja a memóriát az új stringnek s1.p = new char [s1.len+1]; // átmásol strcpy(s1.p, s2.p); } } #endif #if ELKESZULT > 4 ///< Két stringet fűz össze az eredménnyel tér vissza #error "ide jön az egyik + operátor" String concat(String a, String b) { String temp; // Meghatározza az új string hosszát temp.len = a.len + b.len; // lefoglalja a memóriát az új stringnek. temp.p = new char[temp.len+1]; // Az elejére bemásolja az első stringet strcpy(temp.p, a.p); // Bemásolja a második stringet. strcpy(temp.p+a.len, b.p); return temp; } #endif #if ELKESZULT > 5 ///< Egy stringet és egy karaktert fűz össze #error "ide jön a másik + operátor" String concat(String a, char ch) { String temp; // Meghatározza az új string hosszát temp.len = a.len + 1; // lefoglalja a memóriát az új stringnek. temp.p = new char[temp.len+1]; // Az elejére bemásolja az első stringet strcpy(temp.p, a.p); // Bemásolja a karakter és lezárja; temp.p[temp.len-1] = ch; temp.p[temp.len] = '\0'; return temp; } #endif #if ELKESZULT > 6 ///< Hozzáad egy karaktert a stringhez #error "ide jön a += operátor" void addChar(String& str, char ch) { // Növeljük a helyet: régi karakterek + új karakter + lezáró nulla char *p = new char[str.len+2]; // Átmasoljuk strcpy(p, str.p); // Felszabaditjuk a régit delete[] str.p; str.p = p; // A megnövelt helyre beírjuk az új karaktert és a lezáró nullát. str.p[str.len++] = ch; str.p[str.len] = '\0'; //Megnöveltük a hosszt. } #endif #if ELKESZULT > 7

Page 36: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

36

///< A string egy megadott indexű elemének referenciájával tér vissza /// Kivétel generálas helyett meg kell nyujtani a stringet #error "ide jön a [] operátor" char& index(String& str, int i) { if (i < 0) throw "Index hiba\n"; if (i >= str.len) { // Növeljük a helyet: char *p0 = new char[i+2]; // Átmasoljuk strcpy(p0, str.p); // Felszabaditjuk a régit delete[] str.p; str.p = p0; // A megnyujtott reszt szóközzel töltjük fel while (str.len <= i) str.p[str.len++] = ' '; str.p[str.len] = 0; } return str.p[i]; } #endif ///< Kiírunk egy stringet ostream& operator<<(ostream& os, const String& s) { #if ELKESZULT > 8 #error "Meg kell irni a nagybetűsítő részt" #else os << s.p; #endif return(os); } #if ELKESZULT > 8 ///< definiálni kell a statikus mezőket bool String::ucase = false; /// Bellítja az ucase módot /// String osztály statikus tagja bool String::SetUcase(bool b) { bool prev = ucase; ucase = b; return(prev); } #endif int main() { #if ELKESZULT > 0 String a; // default konstruktor cout << "Ez ures: " << a << endl; String b("hello"); // konstruktor char*-bol cout << "Ez egy hello: " << b << endl; #endif #if ELKESZULT > 1 String c('C'); // konstruktor char-bol cout << "Ez egy C: " << c << endl; #endif #if ELKESZULT > 2 String d = b; // masoló konstruktor cout << "Ez egy masolt hello: " << d << endl; #endif #if ELKESZULT > 3 a = b; cout << "Ez egy ertekadott hello: " << a << endl; a = a; cout << "Ez is: " << a << endl; #endif #if ELKESZULT > 4 cout << "Ez str+str osszeadas: " << a + b << endl; // + művelet #endif #if ELKESZULT > 5 cout << "Ez str+c osszeadas: " << a + 'A' << endl; // + művelet

Page 37: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

37

#endif #if ELKESZULT > 6 c += 'a'; cout << "Ez +=c osszeadas: " << c << endl; // += #endif #if ELKESZULT > 7 a[10] = '5'; cout << "Ez egy indexeles: " << a[10] << endl; cout << "Ez pedig zavaros, ha nem toltotte fel a nyujtasnal:" << a << endl; #endif #if ELKESZULT > 8 String::SetUcase(); cout << "Ez nagybetus: " << a << b << c << d << endl; #endif return 0; }

string2.cpp

/* * Dinamikusan nővekedni tudó String struktura * osztály használat nélkül * */ #include <iostream> using namespace std; // A string struktúránk. // A 'p'-ben vannak a karakterek (a lezáró nullával együtt), 'len' a hossza. // (A lezáró nulla léte vagy hiánya implementációs kérdés). struct String { char *p; // pointer az adatra int len; // hossz lezaró nulla nélkül }; // Stringet készít egy nullával lezárt karaktersorozatból. String create(char *p) { String temp; // Meghatározzuk a hosszát temp.len = strlen(p); // Helyet foglalunk temp.p = new char[temp.len+1]; // Bemásoljuk strcpy(temp.p, p); return temp; } // Ha dinamikus memóriát használunk, gondoskodni kell a felszabadításról is. void dispose(String str) { delete[] str.p; } // Stringet készít egy karakterből String create(char ch) { String temp; // Meghatározzuk a hosszát temp.len = 1; // Lefoglalunk a helyet a hossznak + a lezaro nullának temp.p = new char[temp.len+1]; // Betesszük a karaktert temp.p[0] = ch; temp.p[1] = '\0'; return temp; }

Page 38: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

38

// Stringet készít egy Stringból String create(String s) { String temp; temp.len = s.len; // Helyet foglalunk temp.p = new char[s.len+1]; // Bemásoljuk strcpy(temp.p, s.p); return temp; } // Egy stringet bemásol egy másikba (értékadás) void ertekadas(String& s1, String& s2) { if (&s1 != &s2) { // felszabaditjuk a régit delete[] s1.p; // beállítjuk a hosszt s1.len = s2.len; // lefoglalja a memóriát az új stringnek s1.p = new char [s1.len +1]; // átmásol strcpy(s1.p, s2.p); } } // Két stringet fűz össze, eredményét egy harmadikba írja. String concat(String a, String b) { String temp; // Meghatározza az új string hosszát temp.len = a.len + b.len; // lefoglalja a memóriát az új stringnek. temp.p = new char[temp.len+1]; // Az elejére bemásolja az első stringet strcpy(temp.p, a.p); // Bemásolja a második stringet. strcpy(temp.p+a.len, b.p); return temp; } // Egy stringet és egy karaktert fűz össze String concat(String a, char ch) { String temp; // Meghatározza az új string hosszát temp.len = a.len + 1; // lefoglalja a memóriát az új stringnek. temp.p = new char[temp.len+1]; // Az elejére bemásolja az első stringet strcpy(temp.p, a.p); // Bemásolja a karakter és lezárja; temp.p[temp.len-1] = ch; temp.p[temp.len] = '\0'; return temp; } // Hozzáad egy karaktert a stringhez void addChar(String& str, char ch) { // Növeljük a helyet: régi karakterek + új karakter + lezáró nulla char *p = new char[str.len + 2]; // Átmasoljuk strcpy(p, str.p); // Felszabaditjuk a régit delete[] str.p; str.p = p; // if (temp.p == NULL) ... mi tegyunk ? Nagyvonaluan elhagyjuk a vizsgálatot // A megnövelt helyre beírjuk az új karaktert és a lezáró nullát. str.p[str.len++] = ch; str.p[str.len] = '\0'; // Megnöveltük a hosszt. } // A string egy megadott indexű elemével tér vissza. // Ha az index a [0..hossz-1] tartományon kívül van, '\0' karakterrel tér vissza. char index(String& str, int i) { if (i < 0) throw "Index hiba\n"; if (i >= str.len) {

Page 39: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

39

char *p0 = new char[i+2]; strcpy(p0, str.p); delete[] str.p; str.p = p0; while (str.len <= i) str.p[str.len++] = ' '; str.p[str.len] = 0; } return str.p[i]; } // Kiírunk egy stringet void print(String str) { cout << str.p; } // Kiírunk egy stringet (debug célokra) void print_dbg(String str) { cout << '[' << str.len << "], " << str.p << endl; } int main() { String a,b,c,d, e, f; try { a = create('x'); print_dbg(a); b = create("alma"); print_dbg(b); c = concat(a, b); print_dbg(c); d = concat(b, c); print_dbg(d); e = create(d); print_dbg(e); cout << index(d, 4) << endl; addChar(d, 'a'); print_dbg(d); addChar(d, 'b'); print_dbg(d); ertekadas(d, b); print_dbg(d); #ifdef BAD for (;;) f = concat(d, d); #endif f = concat(d, '*'); print_dbg(f); index(d, 101); } catch (bad_alloc) { cerr << "Elfogyott a memoria!\n"; } catch (const char *p) { cerr << p << endl; } dispose(a); dispose(b); dispose(c); dispose(d); dispose(e); dispose(f); return 0; }

Page 40: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

40

8. Dinamikus adattagokat tartalmazó osztályok [gyakorlat]

8.1. 10 komplex szám tárolására alkalmas tömb

Először nézzünk egy nem dinamikus esetet! Feladat: Olvassunk be 10 komplex számot, majd írjuk ki azokat fordított sorrendben és írjuk

melléjük az abszolút értéküket is. #include <iostream> #include <cmath> using namespace std; class complex_2{ double im,re; public: complex_2():im(0.0),re(0.0){} // alapértelmezett (default) konstruktor complex_2(const double d){im=0.0;re=d;} complex_2(const double Im,const double Re){im=Im;re=Re;} complex_2(const complex_2 & c):im(c.im),re(c.re){} // másoló (copy) konstruktor complex_2 & operator=(const double d){re=d;im=0.0;return *this;} complex_2 & operator=(const complex_2 & c){re=c.re;im=c.im;return *this;} complex_2 operator*(complex_2 & c); friend double abs(const complex_2 c){return sqrt(c.im*c.im+c.re*c.re);} friend istream & operator>>(istream & s,complex_2 & c); friend ostream & operator<<(ostream & s,const complex_2 & c){ s << c.re << ',' << c.im << 'i';return s; } }; inline complex_2 complex_2::operator *(complex_2 & c){ return complex_2(re*c.re-im*c.im,re*c.im+im*c.re); } istream & operator>>(istream & s,complex_2 & c){ s >> c.re; cin.ignore(1); s >> c.im;cin.ignore(1); return s; } class IndexHatarTullepes{}; class KomplexTar{ complex_2 t[10]; public: // nincs szükség sem copy konstruktorra, sem = operátorra complex_2 & operator[](const int index){ if(index<0||index>9)throw IndexHatarTullepes(); return t[index]; } }; void main(){ cout << "Kerek 10 komplex szamot 10.2,-5.1i formaban!" << endl; KomplexTar t; for(int i=0;i<3;i++)cin >> t[i]; for(int i=2;i>=0;i--)cout << t[i] << '\t' << abs(t[i]) << endl; }

A programban felhasználtuk a 6. fejezetben bemutatott komplex osztályt, melyet

kiegészítettünk a globális abs() függvénnyel.

Page 41: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

41

A KomplexTar osztály nagyon egyszerű, egy adatmezője van, a tízelemű tömb, továbbá egy tagfüggvénye, az index operátor.

Az osztály adattagját nem szükséges inicializálni, ezért elmarad(nak) a konstruktor(ok). Másoló konstruktorra és egyenlőség operátorra sincs szükség, ezek maguktól keletkeznek. Mindkettő átmásolja a t[10] tömböt a forrásból a célba. A másoló konstruktor és az = operátor közti különbséget meglátjuk a dinamikus adattagot tartalmazó osztály ismertetésénél.

8.2. Diákok adatai példa

Diákok adatait kell beolvasni, és csak azokat kell kiírni, akiknek az átlaga az összátlag felett

van. * Funkcionális dekompozícióval: 1. Beolvasás (diákok listája) 2. Átlagszámítás (diákok listája) 3. Kiíratás (diákok listája) * OO dekompozícióval: 1. Diák (beolvas(), kíír(), átlag()) 2. Diáktár (beszúr_a_listába(), következő_listaelem(), összátlag()) Csak az utóbbi megoldását tárgyaljuk, az első megvalósítása házi feladat. Mivel a feladat semmiféle megkötést nem tartalmaz a megadott függvények funkcionalitását

illetően, ezek úgy fognak működni, ahogy a legcélszerűbbnek, és a legegyszerűbbnek tűnnek. A Diák osztály a következő lesz: //******************************************************* class Diak{ //******************************************************* char Nev[40]; double Atlag; public: Diak(){Nev[0]=0;Atlag=0;}//üres string void beolvas(); void kiir()const; double atlag()const{return Atlag;} }; //******************************************************* void Diak::beolvas(){ //******************************************************* cout << "Nev: "; cout.flush(); cin.sync(); cin.getline(Nev,40); cout << "Atlag: "; cout.flush(); cin.sync(); cin >> Atlag; } //******************************************************* void Diak::kiir()const{ //******************************************************* cout << "Nev: " << Nev << endl; cout << "Atlag: " << Atlag << endl; }

Az osztályban két adattagot definiáltunk: egyet a névnek, egyet az átlagnak. Az

alapértelmezett konstruktor mindkettőt nullázza. Az alapértelmezett másoló konstruktor és = operátor megfelelő, ezért nem cseréljük le őket.

Page 42: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

42

A cout.flush() függvény a kimeneti puffer tartalmát kiírja a képernyőre, ezzel garantálni tudjuk, hogy a szöveg előbb kikerül, mint az ezt követő beolvasás megkezdődne. A cin.sync() függvény ehhez hasonlóan működik: kiüríti a bemeneti puffert, azaz ugyanazt teszi, mint a C-ben a fflush(stdin).

A cin.getline(Nev,40); a gets()-hez hasonlóan teljes sort olvas, méghozzá maximum 40 karaktert.

A DiakTar osztály már érdekesebben alakul a mi szempontunkból: //******************************************************* class DiakTar{ //******************************************************* Diak * lista; unsigned meret; unsigned index; void Novel(); public: DiakTar(){lista=0;meret=index=0;} ~DiakTar(){delete [] lista;} DiakTar(const DiakTar&); DiakTar & operator=(const DiakTar&); bool Beszur(); void Restart(){index=0;} bool Kovetkezo(); double Osszatlag(); }; //******************************************************* DiakTar::DiakTar(const DiakTar & src){ //******************************************************* index=src.index; meret=src.meret; lista=meret?new Diak[meret]:0; for(unsigned i=0;i<meret;i++)lista[i]=src.lista[i]; } //******************************************************* DiakTar & DiakTar::operator =(const DiakTar & src){ //******************************************************* if(&src==this)return *this; delete [] lista; index=src.index; meret=src.meret; lista=meret?new Diak[meret]:0; for(unsigned i=0;i<meret;i++)lista[i]=src.lista[i]; return *this; } //******************************************************* void DiakTar::Novel(){ //******************************************************* meret++; Diak * p=new Diak[meret]; if(lista){ // volt már elem a listában, tehát nem 0 pointer for(unsigned i=0;i<meret-1;i++)p[i]=lista[i]; delete [] lista; } lista=p; } //******************************************************* bool DiakTar::Beszur(){ //******************************************************* cout << "Akar meg beolvasni? (i/n)"; cout.flush(); cin.sync(); char c=0; while(1)switch(c){

Page 43: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

43

case 'i': case 'I': Novel(); lista[meret-1].beolvas(); return true; case 'n': case 'N': return false; default: cin >> c; } } //******************************************************* bool DiakTar::Kovetkezo(){ //******************************************************* if(index>=meret)return false; double osszatlag=Osszatlag(); while(lista[index].atlag()<=osszatlag){ if(index<meret-1)index++; else return false; } lista[index].kiir(); index++; return true; } //******************************************************* double DiakTar::Osszatlag(){ //******************************************************* double sum=0.0; for(unsigned i=0;i<meret;i++)sum+=lista[i].atlag(); return sum/meret; }

A lista nevű változó dinamikus tömböt takar (nem volt előírva láncolt lista, vagy valami

hasonló adatszerkezet). A meret a tömb aktuális mérete, az index pedig azt mutatja, hol jár a kiírás.

Van egy privát függvényünk is, ami megnöveli a tömböt eggyel. Az alapértelmezett konstruktor mindent nulláz (kezdetben nincs tömb). Mivel van dinamikus tagváltozó (a lista), szükség van három függvényre: a destruktorra, a

másoló konstruktorra és az = operátorra. A destruktor akkor fut, amikor megszűnik egy változó. Lokális változóknál ez a függvény

vége, globális változóknál a program vége, dinamikus változóknál a memória felszabadítása, vagyis a delete hívása. A free() nem hívja a destruktort, ezért ne használjuk!

A destruktor, melyből csak egy lehet, és neve a ~ karakter + az osztály neve, ezútttal

mindössze annyit tesz, hogy kitörli a listát. Ha a delete-nek 0 pointert adunk, akkor az nem okoz hibát, egyszerűen nem csinál semmit.

A másoló konstruktor dolga, hogy lefoglaljon egy ugyanakkora tömböt, mint a paraméterként kapott objektumban van, továbbá a két unsigned változó értékét is beállítja. Az újonnan lefoglalt tömbbe bemásolja a forrás tömb elemeit. A másoló konstruktor tehát átmásolja a forrás adatait úgy, hogy helyet foglal a dinamikus adatoknak, nem pedig ugyanoda állítja a lista tömb pointerét, ahová az src-jé is mutat, hiszen abban az esetben hibásan működne a program.

Az = operátor hasonló a konstruktorhoz, csak ebben az esetben két további dologgal is foglalkozni kell. Egyrészt ha a=a; az értékadás, akkor nem törölhetjük le a dinamikus változót, sőt, ekkor semmit sem kell tennünk azon kívül, hogy visszaadjuk a *this-re mutató referenciát azért, hogy az a=b=c típusú értékadások is működjenek.

A másik, amivel többletmunka van, hogy ezúttal már egy látező változó kap értéket, tehát annak van dinamikus adattagja, amit előbb le kell törölni, csak aztán hozhatunk létre újat.

Page 44: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

44

A Beszur() függvény maga olvassa be az adatokat a megfelelő tömbelem beolvas() metódusát hívva.

A Kovetkezo() függvény kiírja azt a következő elemet, amely nagyobb az összátlagnál, amit az Osszatlag() függvény számol ki.

8.3. Gyakorló feladatok:

1 Írjanak egy négyzet osztályt!

• Az osztály képes legyen kiszámolni a téglalap területét, kerületét. • A terület-kerület számító függvényeket az osztályon kívül legyen definiálva. • A téglalap inicializálás után mindig korrekt értékeket tartalmazzon. • Lehessen inicializálni paraméter nélkül (ekkor mindegyik oldal nulla lesz), illetve egy

érték megadásával (ekkor a paraméterben megadott oldalú négyzet lesz), illetve mindkét oldal megadásával.

• Az oldalakat ne lehessen kívülről rosszindulatúan megváltoztatni (negatív értékre beállítani), viszont a téglalap oldalait le lehessen kérdezni.

2. String rejtvény

3. Másik rejtvény

#include <iostream> using namespace std; class A { int k; public: A(const int i = 0) :k(i){ cout << 'k'; } A(const A& a) { k = a.k; cout << 'c'; } void operator=(A& a) { k = a.k; f(a); cout << 'e'; } A& operator*(int i) { cout << i*100; return *this; } void f(A a) { k++; cout << 'f'; } ~A() { cout << 'd'; } }; A& operator*( int i, A& a ) { cout << i; return a; } void main() { A a = 1; cout << '\n'; //kcd a = a * 2; cout << '\n'; //200cfde a = 3 * a; cout << '\n'; //3cfde } //d

Page 45: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

45

9. Öröklés

9.1 Ablakok

Az öröklés legjellegzetesebb felhasználása egy ablakrendszer. Ilyen ablakrendszert tartalmaz

pl. a Microsoft Foundation Class Library, mely 7.0-ás verziójának egy részét alább láthatjuk.

Készítsünk mi is egy ablak osztályhierarchiát! Az alaposztályt nevezzük cAblaknak:

Page 46: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

46

//******************************************************* class cAblak{ //******************************************************* protected: int x,y; unsigned w,h; bool visible,TabStop; int font; // vagy akár konkrét font típus const cAblak * parent; static unsigned ablakok_szama; std::string WindowText; // stb., stb., stb., private: void operator=(const cAblak &); // nem definiáljuk, ezzel meggátoljuk, hogy ilyen keletkezzen cAblak(const cAblak&); // szintén public: cAblak(const int x0=0,const int y0=0,const unsigned w0=0,const unsigned h0=0){ x=y=w=h=0; visible=TabStop=true; font=0; // pl. ez jelzi, hogy Arial parent=0; // nincs szülője, azaz egy ablak sem tartalmazza ablakok_szama++; WindowText=""; } cAblak(const cAblak * szulo){parent=szulo; /* ide jöhet a többi adat inicializálása */} ~cAblak(){ablakok_szama--;} virtual void Rajzol()=0; // tisztán virtuális fv => absztrakt osztály void Mozgat(const int x=0,const int y=0){this->x=x;this->y=y;Rajzol();} void Resize(const unsigned w=0,const unsigned h=0){this->w=w;this->h=h;Rajzol();} int GetX()const{return x;} int GetY()const{return y;}// a w-re és h-ra hasonlóképp void SetWindowText(const char * s){WindowText=s;} void SetWindowText(const std::string s){WindowText=s;} }; unsigned cAblak::ablakok_szama=0;

Először nézzük az adatokat. Ezek protected típusúak lesznek, hogy a leszármazottak is

hozzájuk férhessenek. Minden ablaknak van egy referencia pontja, ahol elhelyezkedik a szülőablakon belül. Ha

nincs szülőablak, a képernyőn belül. (Vagy lehetne minden esetben a képernyőhöz képest is, a példa esetén mindegy.) Elvileg lehet negatív is, ha a referenciapont kívül van a képernyőn. Ebben az esetben az ablak egy része nem látszik.

Van az ablaknak szélessége és magassága, ez a w és h. Két logikai érték: látható-e az ablak, illetve ha a Tab billentyűt megnyomjuk, megálljon-e itt

az input fókusz. A font mondja meg, hogy milyen betűtípust használunk (jelen esetben csak jelképes). Van egy parent nevű pointerünk, ami a szülő ablakra mutat. Ha nincs ilyen, akkor 0. Nem feltétlenül szükséges, de most legyen egy számlálónk, ami azt mondja meg, hogy hány

cAblak osztályú objektum van az adott pillanatban. Ez static változó lesz, azaz az osztályhoz tartozik, nem az objektumhoz (ha van mondjuk 100 ablak, mindegyikhez tartozik pl. x,y,w,h, de összesen csak egy ablakok_szama van, tehát ha az egyik objektum megváltoztatja, az összes számára megváltozik. Mi a konstruktorban növeljük, és a destruktorban csökkentjük az értékét. A statikus változót az osztályon kívül kell definiálni, ezt látjuk az utolsó sorban.

Az utolsó változó a WindowText. Ez tartalmazza az ablak szövegét. Ha keretes ablakról van szó, ez a fejléc szövege, ha mondjuk nyomógombról, akkor a gomb felirata. Olyan ablak is lehet, ahol nem használjuk.

A két private függvény az = operátor és a másoló konstruktor deklarációja. Ezeket a

függvényeket nem valósítjuk meg. Azért tettük be a deklarációkat, hogy ne jöjjenek létre

Page 47: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

47

automatikusan. Így pl. ha véletlenül függvénynek próbálnánk ablakot átadni, az fordítási hibát eredményezne, és pont ez a célunk. Ha nem próbáljuk használni ezeket a függvényeket, akkor nincs gond.

Két konstruktort készítettünk: az egyiknek nincs szülője, a másiknak van (a másodikat csak

részben valósítottuk meg). A destruktornak mindössze annyi a funkciója, hogy a számlálót csökkenti. (minden konstruktorban növelni kell, csak az egyiknél nem töltöttük ki a függvénytörzset rendesen).

Az ablakot a Rajzol()függvény jeleníti meg a képernyőn. Hogy hogyan néz ki az ablak,

attól függ, milyen fajta (szerkesztő mező, rádiógomb, nyomógomb, legördülő lista, stb.) Minden leszármazott osztályban definiálni kell tehát a megfelelő Rajzol() függvényt. A függvényt virtuálisként hozzuk létre. Mit jelent ez, és miért van erre szükség?

Nézzük meg ehhez a Mozgat() függvényt. Ez beállítja az x,y koordinátákat, és kirajzolja az új helyen az ablakot. Mi lenne, ha a Rajzol() nem lenne virtuális (és absztrakt), és lenne egy megvalósítás itt a cAblak osztályban? Akkor a mozgat függvényt hívva bármely leszármazottból, mindig az alaposztály, vagyis a cAblak Rajzol() függvénye futna le, mert az alaposztály m függvénye semmit sem tud a leszármazottakról, neki a fordító fixen befordítja az alaposztály Rajzol() függvényének címét.

Ha egy tagfüggvény virtuális, akkor nem a függvény címe fordítódik be fixen, hanem egy táblázaté, ahol a virtuális függvények címei tárolódnak. Ezt a táblázatot a konstruktorok automatikusan létrehozzák, így tehát mindig a megfelelő függvény hívódhat meg. Fontos megjegyezni, hogy heterogén kollekció esetén (amikor mondjuk egy pointertömbben mindenféle leszármazott ablakok címeit tároljuk, és a tömbben tárolt pointerek típusa cAblak*), akkor t[i]->függvény() esetén ha a függvény virtuális, akkor a

leszármazott osztály függvénye hívódik meg, míg ha nem virtuális, akkor az alaposztályé,

akkor is, ha felüldefiniáltuk a leszármazottban! A Rajzol()=0 azt jelenti, hogy ez a függvény tisztán virtuális (vagyis az =0-nak csak

virtuális függvények esetén van értelme). Ha egy osztályban ilyen van, vagy örököl egy ilyet, és nem definiáljuk felül, akkor ebből az osztályból objektumot példányosítani nem lehet. Pl.: cAblak a; nem jó! Csak olyan leszármazottakból lehet objektumot létrehozni,

amelyekben felüldefiniáltuk a tisztán virtuális tagfüggvényeket. Az olyan osztályokat, melyekben van tisztán virtuális tagfüggvény, absztrakt osztálynak nevezzük.

A többi függvény nem igényel magyarázatot. Származtassunk gomb osztályt az ablakból! class cGomb:public cAblak{ bool benyomhato, benyomva; public: cGomb(const bool sticky=false,const int x0=0,const int y0=0,const unsigned w0=0, const unsigned h0=0):cAblak(x0,y0,w0,h0),benyomhato(sticky),benyomva(false){} void Rajzol(){/* ide jön a konkrét rajzolás */} void OnClick(){ /* ha ráklikkelnek, ez hívódik */ } };

Készítettünk egy konstruktort, implementáltuk a Rajzol() függvényt, és készítettünk egy

eseménykezelőt. Feladatok:

• Alakítsa át a gomb osztályt, hogy rádiógombként és checkboxként is működhessen. • Készítsen

Page 48: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

48

o szerkesztőmező komponenst (egy soros, több soros). o gördítősávot o legördülő listát o dialógusablakot o MDI (több dokumentum tárolására alkalmas ablakot).

A feladatok lényege, hogy minél több lehetséges tulajdonsággal felvértezzük a komponenseket, és az ennek megfelelő függvényeket és változókat megadjuk (nem kell megvalósítani).

9.2. IOSTREAM

Az alábbiakat Szeberényi tanár úr gyűjtötte össze. Ami ezek közül lényeges: flagek, tagfüggvények, manipulátorok. Tudjuk, hogy vannak

ilyenek, és melyek a főbb fajták.

Alapvetően jól használható dokumentáció található a http://www.cplusplus.com/ref/iostream címen, azonban kicsit zavaró, hogy a nyilakat pont fordítvba használja az osztálydiagrammokban.

ios_base

Az ios_base osztály egyik fontos tagja a flags:

fmtflags flags ( ) const; fmtflags flags ( fmtflags fmtfl );

Az fmtflags értékei:

flag effect if set

ios_base::boolalpha input/output bool objects as alphabetic names (true, false).

ios_base::dec input/output integer in decimal base format.

ios_base::hex input/output integer in hexadecimal base format.

ios_base::left the output is filled at the end enlarging the output up to the field width.

ios_base::oct input/output integer in octal base format.

ios_base::right the output is filled at the beginning enlarging the output up to the field width.

ios_base::scientific output floating-point values in scientific notation.

ios_base::showbase output integer values preceded by the numeric base.

ios_base::showpoint output floating-point values including always the decimal point.

ios_base::showpos output non-negative numeric preceded by a plus sign (+).

ios_base::uppercase output uppercase letters replacing certain lowercase letters.

Példa a használatra:

cout.flags ( ios_base::right | ios_base::hex | ios_base::showbase );

A jelzők beállítása a stef tagfüggvénnyel is történhet:

Page 49: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

49

mtflags setf ( fmtflags fmtfl ); fmtflags setf ( fmtflags fmtfl, fmtflags mask );

A kétparaméteres formában az első paraméter a bellítandó, a második a törlendő biteket jelzi.

A jelzők állítását ún. manipulátorok segítik, melyek a stream-hez közvetlenül hozzáfűzhetők:

manipulator meaning

boolalpha insert/extract bool objects as names (true or false).

dec insert/extract integer values in decimal format.

fixed insert floating-point values in fixed format.

hex insert/extract integer values in hexadecimal format.

internal internal-justify.

left left-justify.

noboolalpha insert/extract bool objects as numeric values.

noshowbase do not prefix value with its base.

noshowpoint do not show decimal point if not necessary.

noshowpos do not insert plus sign (+) if number is non-negative.

nouppercase do not replace lowercase letters by uppercase equivalents.

oct insert/extract values in octal format.

right right-justify.

scientific insert floating-point values in scientific format.

showbase prefix value with its base

showpoint always show decimal point when inserting floating-point values

showpos insert plus sign (+) before every non-negative value.

uppercase replace lowercase letters by uppercase equivalents.

Példa a manipulátorok használatra:

cout << hex << 1024 << oct << 1024 << dec << 1024;

Az ios_base osztály másik két fontos tagja width és a precision, amivel a mezőszélességet és a tizedesjegzek számát lehet beállítani ill. lekérdzni. (A steramsize integer mennyiség)

streamsize precision ( ) const; streamsize precision ( streamsize prec );

streamsize width ( ) const; streamsize width ( streamsize wide );

Az ios_base bitmaszk típusa az openmode, amit majd az opennél használunk fel:

bit effect

ios_base::app (append) Seek to the end of the stream before each output operation.

ios_base::ate (at end) Seek to the end of the stream when opening.

ios_base::binary Consider stream as binary rather than text.

ios_base::in Allow input operations on a stream.

ios_base::out Allow output operations on a stream.

ios_base::trunc (truncate) Truncate file to zero when opening.

Page 50: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

50

ios

Az ios osztály fontosabb tagfüggvényei:

member meaning

bad Check if an unrecoverable error has occurred.

eof Check if End-Of-File has been reached.

fail Check if failure has occurred.

fill Get/set the fill character.

good Check if stream is good for i/o operations.

sync_with_stdio Activates / Deactivates synchronization with cstdio functions.

operator void * Convert stream to pointer. Return value: 0 if failbit or badbit is set non-zero otherwise.

operator ! Evaluate stream object. Return value: true if failbit or badbit is set false otherwise.

A két utolsó operátor nagyon hasznos, ugyanis az eof ugyanúgy használhatatlan mint a feof() a C-ben.

istream

Az istream osztály fontosabb tagfüggvényei:

member meaning

istream(streambuf *) Construct from streambuf*

operator>> Perform formatted input operation (extraction)

get Extract unformatted data from stream

getline Get a line from stream

ignore Extract and discard characters

peek Peek next character

read Read a block of data

ostream

Az ostream osztály fontosabb tagfüggvényei:

member meaning

operator<< Perform a formatted output operation (insertion).

flush Flush buffer.

put Put a single character into output stream.

write Write a sequence of characters.

fstream

Az fstream osztály fontosabb tagfüggvényei:

member meaning

ftream(const char *, openmode) Construct an object and optionally open a file.

is_open Check if a file has been opened.

Page 51: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

51

open(const char *, openmode) Open a file.

close Close an open file.

Az openmode az ios_base osztályból származik.

Végül egy "állatorvosi ló":

#include <iostream> #include <fstream> using namespace std;int main () { fstream os("alma.txt", fstream::out | fstream::trunc); int i = 16; os.width(4); os << hex << i; os.width(8); os << oct << i << '\n'; double f = 3.14159; os.fill('.'); os.width(20); os.precision(4); os << f << '\n'; os.close(); os.open("alma.txt", fstream::in); char buf[100]; while (os.getline(buf, sizeof(buf))) cout << buf << endl; os.close(); cin.ignore(1); return 0; }

A futás eredménye:

10 20 ...................3.142

Page 52: Pohl László - users.atw.huusers.atw.hu/progizas123/c/tanulnic++/C++ gyak. és labor jegyzet.pdf · 4 Bevezetés Ez a jegyzet azokat a gyakorlati ismereteket kívánja bemutatni,

52

F Hivatkozások [1] Szeberényi Imre, Ketler Iván, Szigeti Szabolcs: UNIX – A rendszer használata (PANEM 2004) [2] Szeberényi Imre: Bevezetés a UNIX operációs rendszerbe (Műegyetemi kiadó, 2003) [3] http://www.szabilinux.hu/ [4] Bjarne Stroustrup: A C++ programozási nyelv (Kiskapu 2001)