2. gyakorlat¶
Ismétlés¶
- stdio.h -> iostream
- scanf -> cin
- printf -> cout
C++ string¶
Az előző órán láttuk, hogyan bírjuk a stream-eket használni kiíratásra és olvasásra. Mi történik az alábbi programmal akkor, ha a fix méretű karakter tömbbe hosszabb méretű karakterláncot adunk meg?
1 2 3 4 5 6 7 8 9 10 11 |
|
Az eredmény (ha a program lefut):
1 2 |
|
Azonban ha túl hosszú a bemenet, akkor a program hibával megállhat. A megoldás az, hogy az inputnak megfelelő méretű területet foglalunk, és oda írjuk be a felhasználó által megadott nevet. A C++-ban a "szövegeknek" van egy hatékonyabb, kevésbé körülményes megvalósítása is, a string
osztály (osztályokról később lesz szó, most csak azt mutatjuk be, hogyan lehet használni a string
-et). A félév folyamán stringek alatt ezt a reprezentációt értjük, nem pedig a char*
-ot.
A string objektumok használatához, string műveletekhez szükségünk lesz a string
include-olására is (és nem string.h
). Ezt követően kényelmesen használhatjuk a szövegeket, ahogy már Javaban is megszoktuk. Itt csak a fontosabb használati eseteket emeljük ki, bővebb leírásokhoz linkek:
- http://www.cplusplus.com/reference/string/string/
- https://en.cppreference.com/w/cpp/string/basic_string
A string
fontosabb metódusai, amelyek a leggyakrabban használunk:
Létrehozás, azaz a konstruktorok¶
1 2 3 4 |
|
Beolvasás, kiírás¶
1 2 3 |
|
A scanf
nem használható a string
beolvasására, de a cin
segítségével be tudjuk olvasni, és a cout
segítségével ki tudjuk írni.
Összehasonlítás¶
1 2 3 4 5 6 |
|
A string
-eket az ==
operátor segítéségével össze lehet hasonlítani.
Összefűzés¶
1 2 3 4 |
|
Méret, üres-e, tartalom törlése¶
1 2 3 4 5 6 7 |
|
i-edik elem¶
1 2 3 4 5 |
|
Ha olyan indexre hivatkozunk, amelyik érvénytelen, azaz a string hosszánál nagyobb, akkor nem definiált viselkedés lesz az eredménye.
Konverzió szám és string
között¶
Számból string
-gé a to_string
segítségével konvertálhatunk.
1 2 3 4 |
|
string
-et számmá konvertálni az stoi
függvénnyel lehet.
1 2 3 |
|
Konverziók a különböző típusokra:
int
: stoilong
: stollong long
: stollunsigned long
: stoulunsigned long long
: stoullfloat
: stofdouble
: stodlong double
: stold
Mi történik,
- ha a szöveg elején van szám, de van mögötte ,,egyéb'', akkor a számot átkonvertálja
1 2 |
|
- ha nem fér bele a tartományba, akkor
std::out_of_range
kivétel dobódik (kivételkezelés, const és referencia később)
1 2 3 4 5 6 7 |
|
- ha nem lehet konvertálni, akkor
std::invalid_argument
kivétel dobódik
1 2 3 4 5 6 7 |
|
Operátor felüldefiniálás¶
Ahhoz, hogy meg tudjuk nézni, hogyan is működik az operátorfelüldefiniálás, először meg kell néznünk, hogyan működnek az operátorok. Ezt a +=
operátor segítségével mutatjuk be.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Ha részletesebben megnézzük az a += 3
és b += a
sorokat, látjuk, hogy a +=
operátort hívtuk, egyszer integer literált majd egy int típusú változót adva neki. Természetesen tudjuk, hogy ekkor a baloldali változó értékéhez hozzáadódik a joboldali érték. Ezt megírhatnánk egy növel függvénnyel is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
A fenti kódot lefuttatva ugyan azt kapjuk mint mikor a +=
operátort használtuk, csupán a kód olvashatósága rosszabb. Sokszor hasznos lenne ezt az olvashatóságot egyéb típusokra is használni nem csak a beépített s primitív típusokra.
Ha bankszámlát szeretnénk kezelni, akkor a már ismert struktúrát használhatjuk.
1 2 3 4 |
|
Ha ezen a struktúrán bármilyen műveletet szeretnénk végezni (pl. utalás), meg kell írni hozzá egy függvényt.
utalás függvény¶
Az utaláskor tudnunk kell, hogy melyik számlát kell módosítani és milyen összeggel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Látszódik, hogy létrehozunk egy bankszámlát, inicializáljuk az értékeket és kiíratjuk a számlán lévő összeget az utalás előtt és után is. Azonban pont mint a számok esetében, jobban olvasható kódot kapunk, ha nem egy függvényhívást, hanem pl. a +=
operátort használjuk. Mivel az operátorok ugyan olyan elemei a kódnak mint pl. az utalas
függvényünk, cseréljük le a nevét.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Ahogy látjuk, a névcsere után is megfelelően működik a függvény. Ami fontos, hogy a jelölésmód cseréje után is, hiszen a fordító tudja, hogy Bankszamla
típust kell az első helyre keresnie, melyet baloldali operandusként meg is talál. Azt is felismeri, hogy a jobboldali operandus pont a második paraméternek felel meg, így be tudja azonosítani azt a függvényt amit meg kell hívnia.
befizetés függvény¶
Sokszor a bankszámlánk segítségével csekkeket is be tudunk fizetni. Ekkor hozzárendelünk egy csekket a számlánkhoz.
Erre az újonnan bevezetett jelöléssel a következőt írhatjuk:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Ez azonban fordítási hibát eredményez, hiszen mikor a baloldali operandust vizsgálja a fordító, azt nem tudja a +=
operátor második paramétereként értelmezni.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
Érdekesség, hogy míg az ,,eredeti'' +=
operátor a jobb oldalon szereplő értéket nem változtatja meg, addig az általunk implementált operátor arra is hatással van.
Miután megvalósítottuk azt a függvényt, aminek a neve a +=
operátor és mind a két paraméter megfelel az átadott paraméternek, a fordító megtalálja, hogy mit kell odaraknia s sikeresen futtathatjuk a kódot.
Látszódik, hogy a két operátorfelüldefiniálás nem akad össze, hiszen a paramétereik megkülönböztetik azokat.
Dinamikus memóriakezelés¶
A bankszámlák kezelése során sokszor logok alapján vissza lehet állítani az éppen aktuális összeget. Ha tudjuk egy fix ponton a pontos összeget és az onnantól bekövetkezett az összes változást, meg tudjuk mondani az aktuális összeget. Ez a tranzakciós történet legyen egy tömb. A tömbben a pozitív érték növekedést a negatív érték csökkenést jelentsen!
Ezt a tömböt dinamikusan fogjuk foglalni, hogy meglássuk a C és C++ nyelvek közti különbséget.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Órai gyakorló feladatok¶
-
Írj egy
Cseszealj
struktúrát, aminek két adattagja van:aktualis_terheles
:unsigned
max_terheles
:unsigned
Ezek megadják aktuálisan hány kilót szállít a csészealj illetve maximálisan mennyit bír el.
-
Írj egy
void elrabol(Cseszealj& cs, unsigned t)
függvényt.- A második paraméter a csészealj által elrabolni kívánt tárgy tömege.
- Legyen ez hozzáadva a csészealj
aktualis_terheles
adattagjához, amennyiben az összeg nem haladná meg amax_terheles
értékét. - Ha meghaladná, akkor legyen ez kiírva a standard outputra:
Elrablas nem lehetseges
.
-
Valósítsd meg a
|=
operátort úgy, hogy a bal oldali operandusa egy csészealj legyen, a jobboldali pedig egyunsigned
érték, visszatérési értéke pedig void.- Az operátor logikája egyezzen meg az
elrabol
függvény logikájával. - Miután elkészültél, cseréld le a main-ben az elrabol függvény használatát a
|=
operátor használatára!
- Az operátor logikája egyezzen meg az
-
Írj egy Tehen struktúrát. Két adattagja van:
nev
:string
tomeg
:unsigned
A string a tehén nevét tartalmazza, a második pedig, hogy hány kiló.
-
Próbáld ki, hogy mi történik, ha egy tehenet próbálsz a
|=
operátorral a csészealjhoz adni.- Javítsd a hibát úgy, hogy a tehenekre is működjön a
|=
operátor. A logika hasonló a 2-es feladatban használt logikához. - Add hozzá a tehén tömegét a csészealj
aktualis_terheles
értékéhez, amennyiben az a tehén tömegével együtt még nem haladja meg amax_terheles
értéket. Ha meghaladja legyen a következő szöveg kiírva:<tehen nev> elrablasa nem lehetseges
.
- Javítsd a hibát úgy, hogy a tehenekre is működjön a
-
Valósítsd meg a
|
operátort is a Cseszalj struktúrára. Bal oldali operandusa egy csészealj, a jobb oldali pedig egy unsigned érték vagy egy tehén (valósítsd meg mindkettőre!). A|
működése annyiban különbözik a|=
operátorétól, hogy nem módosítja a paraméterkben kapott cséeszaljat, hanem visszaad egy új csészealjat, aminek meg van növelve azaktualis_terheles
értéke (amennyiben lehetséges!). Ha nem lehetséges a növelés a 2-es illetve az 5-ös feladatban használt hibaüzenetek legyenek kiírva, a visszatérési érték pedig legyen az eredeti csészealj módosítatlan másolata. -
Valósitsd meg az unáris
~
operátort aCseszealj
struktúrára.- Visszatérési értéke
void
legyen. - Az operátor feladata a paraméterben kapott csészealj kiürítése, azaz az
aktualis_terheles
értékének a 0-ra állítása.
- Visszatérési értéke
A feladatsor és a megoldása letölthető itt és itt.
Otthoni gyakorló feladatok¶
-
Írjunk programot, mely bekér két stringet és összefűzi azokat úgy, hogy bekérés sorrendjétől függetlenül mindig a hosszabb kerül előre (egyenlő hosszúságnál a bekérés sorrendjében fűzze össze)! Az eredményt írja is ki!
Megoldás
- Kód: 1. feladat
-
Írjunk egy programot, mely beolvas egy stringet és egy számot. Írja ki a string elemeit a kapott indextől! Ha az index nem valid, írja ki, az alapértelmezett hiba csatornára, hogy ,,invalid index''!
Megoldás
- Kód: 2. feladat
-
A stringhez kapcsolódó referencia oldalak segítségével írjunk olyan programot, mely bekér két stringet és a hosszabban megkeresi a rövidebbet! Ha nem található, írja ki, az alapértelmezett hibacsatornára, hogy "not found"! Ha megtalálta, írja ki a pozíciót ahol kezdődik!
Megoldás
- Kód: 3. feladat
-
A referencia oldalak segítségével írjunk olyan programot, mely a legjobboldalabbi 'b' betű pozícióját írja ki egy bekért szövegből! Nézzük meg, mit kapunk, ha nem található 'b' betű a szövegben!
Megoldás
- Kód: 4. feladat
-
Írjunk programot, mely kideríti [-100;100] -on, hogy melyik számnak egyezik meg az értéke és a string reprezentáció hossza!
Megoldás
- Kód: 5. feladat
-
Írjunk programot, mely bekér 2 stringet és 2 számot (lehetséges értékek: 2,8,10,16)! A program a 2 stringet rendre a 2 bekért szám szerinti számrendszerben vett számként értelmezze és váltsa át azokat tízes számrendszerbeli számmá! (megj.: a konvertáló függvények átnézése a referencia oldalakon!)
Megoldás
- Kód: 6. feladat
-
Miért nem működik a
-=
operátor, hogyan lehetne megoldani a problémát?Megoldás
- Kód: 7. feladat
-
A
-=
operátor nélkül ,,vonjunk'' le a számláról (negatív hozzáadása)!Megoldás
- Kód: 8. feladat
-
Csak akkor működjön a
+=
operátor, ha a csekkben meghatározott szervezet "Vizmu"!Megoldás
- Kód: 9. feladat
-
Csak akkor működjön a
+=
operátor, ha a csekkben meghatározott szervezet "Vizmu" a cél számla tulaja "Szilveszter Janos" és szilveszteri ajándékként a forrásszámla 0,75-szörösét vonja le negatív egyenleg esetén, különben 1,5-szörösét!Megoldás
- Kód: 10. feladat
-
Valósítsuk meg a roll_back_multiple függvényt a
/=
operátor segítségével!Megoldás
- Kód: 11. feladat
-
Valósítsuk meg a
/=
operátort (roll_back_multiple), hogyconst int* const
típusú legyen a második paraméter!Megoldás
- Kód: 12. feladat
-
Írjunk a fenti példák alapján egy programot, mely bekér egy számot (tranzakciók darabszáma), akkora dinamikus tömböt foglal, majd bekéri a tranzakciók összegét! Egy alap számlán hajtsa végre a tranzakciókat a
/=
operátor segítségével! Ne felejtsük el a memória felszabadítást sem!Megoldás
- Kód: 13. feladat