Órai feladat
2. Gyakorlat¶
1. Első osztály¶
-
Készítsünk egy osztályt
Tilenéven! EgyTileegy 1x1 méteres területet jelent. Legyen egy pár adattagja az osztálynak:type: string típusú, a terület típusát adja meg, pl.: "homok", "víz", stb.value: nem negatív egész szám típusú, a terület értékét adja meg.
tile.cpp
1 2 3 4 5 6 7
#include <string> class Tile { public: std::string type; unsigned value; };
2. Osztály használata¶
-
Példányosítsuk egy
Tileosztály példányt (avagy hozzunk létre egy ilyen típusú objektumot) amainfüggvényünkben.tile.cpp
1 2 3 4 5
int main() { Tile obj; return 0; } -
Fejlesszük tovább a programunkat! Írassuk ki a létrehozott példány minden adattagját, egymás után egy szóközzel elválasztva.
tile.cpp
std::cout << "obj.type: " << obj.type << " obj.value: " << obj.valueMit ír ki a program?
$ g++ -Wall -Werror -o program tile.cpp $ ./program obj.type: obj.value: 1334548968- Mi történik ha többször futtatjuk le a programot?
- A string "üres" lett a szám pedig valami furcsaság.
- Miért van ez?
-
Adjunk értéket a létrehozott objektum adattagjainak a kiiratás előtt
tile.cpp
Tile obj; obj.type = "homok"; obj.value = 100;
3. Const-ans probléma¶
-
Adjunk egy
constmódosítót aTileosztálytypeadattagjának.tile.cpp
const std::string type;Sikerül fordítani-e a programot?
$ g++ -Wall -Werror -o program tile.cpp tile.cpp: In function ‘int main()’: tile.cpp:13:16: error: no match for ‘operator=’ (operand types are ‘const std::string’ {aka ‘const std::__cxx11::basic_string<char>’} and ‘const char [6]’) 13 | obj.type = "homok"; | ^~~~~~~Mi lehet a probléma?
A probléma az, hogy a
Tiletypeadattagja most már rendelkezik egyconstmódosítóval, aminek a hatására ezt az adattagot a példány létrehozásakor be kellene állítani, később nem módosítható. -
Hogyan lehetne javítani?
Inicializáljuk az értéket az osztályban!
const std::string type = "homok";
4. Igen... de....¶
-
Szuper, hogy tudjuk az osztályt példányosítani, de mi van ha, nem mindig "homok" típusú területeket szeretnénk létrehozi? Hozzunk, létre egy egyparaméteres konstruktort (constructor, ctor)!
- Ennek a konstruktornak a terület típusát adjuk át.
- A kapott paramétterrel közvetlenül inicializáljuk a osztály megfelelő adattagját! (hint: initializer list)
- Majd, módosítsuk a példányosítás helyét, hogy egy másmilyen típusú területet hozzunk létre.
Konstruktor hozzáadása
class Tile { public: const std::string type; unsigned value; Tile(const std::string& type) // (1) : type(type) // (2) {} };- A típust konstans (
const) referenciaként (&)! Később lesz róla szó. - Az initializer listben a külső név az osztály adattagja, a belső pedig a kapott paraméter van valami egyéb utasítás.
-
Alakítsuk tovább a konstruktor! Legyen kétparaméteres, hogy a terület értékét is megkapja.
Konstruktor adaptálás
Tile(const std::string& type, unsigned value) : type(type), value(value)- Ne felejtsük el a példányosítás helyén is adaptálni a kódunkat!
-
A konstruktorban írjuk ki az eltárolt adatokat egy sorban a
Tile(<type>, <value>)formában.Kiíratás
{ std::cout << "Tile(" << this->type << ", " << this->value << ")" << std::endl; }Mit jelent a
this->?A
thisegy pointer (memóriacím) az éppen aktuálisan használt objektum példányra. Azaz, hogy éppen melyik példánnyal dolgozunk. Ez csak osztály tagfüggvényekben lehet használni.
5. Adatok.. egyém, tiéd, mindenkié?¶
-
Objektum orientált programozás esetén sok esetben nem célszerű egy osztály minden adattagját publikussá tenni. Rejtsük el a
Tileosztály két adattagját, hogy "kívülről" ne lehessen hozzáférni!-
Legyenek az adattagok privátok!
Private adattagok
class Tile { private: // (1) const std::string type; unsigned value; public: Tile(const std::string& type, unsigned value) : type(type), value(value)- Minden adattag és függvény a
private:után privát láthatóságú lesz, amíg másképpen nem rendelkezünk. Amennyiben nincs kiírva,privateláthatoságú lenne.
- Minden adattag és függvény a
-
Milyen problémába ütközünk fordításkor? Mit jelent az üzenet?
Fordítási hiba
$ g++ -Wall -Werror -o program tile.cpp tile.cpp: In function ‘int main()’: tile.cpp:20:38: error: ‘const std::string Tile::type’ is private within this context 20 | std::cout << "obj.type: " << obj.type | ^~~~ tile.cpp:6:23: note: declared private here 6 | const std::string type; | ^~~~ tile.cpp:21:40: error: ‘unsigned int Tile::value’ is private within this context 21 | << " obj.value: " << obj.value | ^~~~~ tile.cpp:7:14: note: declared private here 7 | unsigned value; | ^~~~~ -
Mi lenne rá a megoldás, hogy mégis el lehessen érni az adatokat az osztály példányon kívülről?
Megoldás ötlet
Adjunk az osztályhoz "getter" eljárásokat, amikkel a belső adattagokat "kipublikáljuk".
const std::string& getType() { return type; } unsigned getValue() { return value; } -
Használjuk ezeket a hozzáadott "getter"-eket a kiíratásnál a
main-ben!
-
6. Többféle konstruktor¶
-
Oldjuk meg, hogy lehessen egy
Tilepéldányt csak a típusa alapján példányosítani, egy alapértelmezett 10-esvalue-val. (Ezt többféleképpen is meg lehet csinálni).- Adjunk hozzá egy egyparaméteres konstruktors aminek csak egy típust tartalmazó paramétere van.
- Az inicializációs listában adjunk mindkét adattagnak értéket.
-
Ne felejtsük el, hogy az eredeti konstruktorunkban volt kiíratás, az ide is kell!
Második konstruktor
Tile(const std::string& type) : type(type), value(10) { std::cout << "Tile(" << this->type << ", " << this->value << ")" << std::endl; } -
Használjuk is a konstuktor! A
main-ben hozzunk létre egy másik példányt, aminek legyenhegya típusa.
-
Jó.. jó... de mi van ha kell egy másik konstruktor is, ugyanolyan viselkedéssel? Ismét le kellene másolni a konstruktor "belső" működését? Szerencsére nem!
-
Módósítsuk az egyparaméteres konstruktort, hogy az eredeti kétparaméterest hívja meg. (delegate constructor)
Delegating constructor
Tile(const std::string& type) : Tile(type, 10) {}
-
-
Szuper! Legyen egy default konstruktorunk is (nulla paraméteres konstruktor).
-
A default konstruktor esetén az alapértelmezett típus az
"ures"szöveg és 0-ás értékű legyen a létrehozott példány.Default ctor
Tile() : Tile("ures", 0) {}
-
-
Próbáljuk ki különféle értékekkel a konstruktorokat!
Konstruktorok használata
int main() { Tile obj("viz", 200); std::cout << "obj.type: " << obj.getType() << " obj.value: " << obj.getValue() << "\n"; Tile other("hegy"); std::cout << "obj.type: " << other.getType() << " obj.value: " << other.getValue() << "\n"; Tile empty; // (1) std::cout << "obj.type: " << empty.getType() << " obj.value: " << empty.getValue() << "\n"; return 0; }- a
Tile empty();forma nem helyes, nem konstruktor hívás lenne.
- a
7. Változó értékek¶
-
Sok esetben példányosítás után valamilyen adatot módosítani szeretnék, például a területünk értékét. Jelen esetben azonban ez nem lehetséges. Segítsünk ezen!
-
Hozzunk létre "setter"-t a
valueadattaghoz.Value setter
void setValue(unsigned newValue) { value = newValue; }
Létrehozhatunk-e a
type-hoz settert?Mivel a
typeadattag az osztálybanconst-ként van megadva, így inicializálás után már nem módosíthatjuk. Tehát nincs értelme a "setter"-nek. -
-
Használjuk a "setter"-t minden példányosítás után, de a kiíratások előtt a
main-ben.Setter használata
Tile obj("viz", 200); obj.setValue(400);
8. Destruktor¶
-
Sok-sok példányt létrehoztunk, de lehet "takarítani" is kellene. C++ példány megszünésekor van lehetőség destruktor meghívására (sok esetben automatikusan).
- Adjuk a
Tileosztályhoz egy destruktort (dtor)! - A destruktorban írjuk ki az eltárolt adatokat egy sorban a
~Tile(<type>, <value>)formában.
~Tile
~Tile() { std::cout << "~Tile(" << this->type << ", " << this->value << ")" << std::endl; }- Mikor fut le a destruktor?
- Adjuk a
9. Kis "takarítás"¶
-
Jelenleg több példányt is létrehozunk és mindegyikhez van 1-1 kiíratást végző kódrészlet másolna/begépelve. Azonban nem kell nekünk ennyi kód duplikáció.
- Készítsünk egy függvényt
displaynéven ami egyTilepéldányt kap és kiírja a típusát, értékét hasonló formában mint eddig. (&-t most ne használjunk!)
display(..)
void display(Tile instance) { std::cout << "..type: " << instance.getType() << "..value: " << instance.getValue() << "\n"; } - Készítsünk egy függvényt
-
Használjuk ezt a
displayfüggvényt amain-ben.display(..)
int main() { Tile obj("viz", 200); obj.setValue(400); display(obj); Tile other("hegy"); other.setValue(10000); display(other); Tile empty; empty.setValue(33); display(empty); return 0; } -
A program végrehajtása után mit ír ki?
Fordítás, végrehajtás és kimenet
$ g++ -Wall -o tile tile.cpp $ ./tile Tile(viz, 200) ..type: viz..value: 400 ~Tile(viz, 400) Tile(hegy, 10) ..type: hegy..value: 10000 ~Tile(hegy, 10000) Tile(ures, 0) ..type: ures..value: 33 ~Tile(ures, 33) ~Tile(ures, 33) ~Tile(hegy, 10000) ~Tile(viz, 400)Miért van ilyen sok
~Tilekimenet?Ez a
displayfüggvény paraméteréből jön. Jelen esetben aTile-tTile instance-ként adjuk át, ami azt jelenti, hogy mindig másolással adjuk át az értéket amikor ezt az eljárást meghívjuk.
10. Paraméter átadás¶
-
Az előző feladat végén láthattuk, hogy az osztály destruktora többször is lefut mint gondolnánk.
- Oldjuk meg, hogy a
displayeljárás hívás hatására ne másolódjon le a kapottTiletípusú adat. - Ehhez legyen az eljárás paramétere
Tilereferencia.
Tile referencia
void display(Tile& instance) { // (1)- A
Tile&fontos! A&jelenti azt, hogy ez egy referencia típus. Egy referencia mindig mutat valami létező értékre, nem történik másolás.
Fordítás, végrehajtás és kimenet
$ g++ -Wall -o tile tile.cpp $ ./tile Tile(viz, 200) ..type: viz..value: 400 Tile(hegy, 10) ..type: hegy..value: 10000 Tile(ures, 0) ..type: ures..value: 33 ~Tile(ures, 33) ~Tile(hegy, 10000) ~Tile(viz, 400) - Oldjuk meg, hogy a
11. Védelem a módosítások ellen¶
-
Most, hogy van egy kiírató függvényünk egyből van is vele egy kis probléma: Ez a kiíró függvény módosíthatja a kapott
Tilepéldányt. De egy csak kiíratást végző eljárást miért módosítaná az kapott példányt?-
Oldjuk meg, hogy ne lehessen a
displayeljárásban módosítani a kapott példányt.Megoldás ötlet
Adjunk egy
constmódosítót adisplayeljárás első paraméteréhez.Módosított fejléc
void display(const Tile& instance) { // (1)- A
const-ot jelen esetben a referencia típus elé kell megadni.
- A
-
Okés... és fordul a program? Gyakori hibák
Fordítási hiba
$ g++ -Wall -o tile tile.cpp tile.cpp: In function ‘void display(const Tile&)’: tile.cpp:38:48: error: passing ‘const Tile’ as ‘this’ argument discards qualifiers [-fpermissive] 38 | std::cout << "..type: " << instance.getType() | ~~~~~~~~~~~~~~~~^~ tile.cpp:29:24: note: in call to ‘const std::string& Tile::getType()’ 29 | const std::string& getType() { return type; } | ^~~~~~~ tile.cpp:39:50: error: passing ‘const Tile’ as ‘this’ argument discards qualifiers [-fpermissive] 39 | << "..value: " << instance.getValue() | ~~~~~~~~~~~~~~~~~^~ tile.cpp:30:14: note: in call to ‘unsigned int Tile::getValue()’ 30 | unsigned getValue() { return value; } | ^~~~~~~~Hiba oka
A hiba röviden az, hogy C++-ban minden eljárást amit
consttípusú példányon akarunk végrehajtani jelölni kell.
-
-
Javítsuk meg a programunkat!
-
Adjunk egy-egy
constmódosítót a "getter" eljárások fejléceinek végére.Módosított getter fejlécek
const std::string& getType() const { return type; } unsigned getValue() const { return value; } -
Próbáljuk ki újra a programunkat!
-
Megoldások
1. feladat
1 2 3 4 5 6 7 | |
2. feladat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
3. feladat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
4. feladat
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 | |
5. feladat
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 | |
6. feladat
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 | |
7. feladat
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 | |
8. feladat
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 59 60 | |
9. feladat
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 59 60 61 | |
10. feladat
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 59 60 61 | |
11. feladat
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 59 60 61 | |
Létrehozva: 2026-09-30