Bevezetés a Programozásba II 10. előadás. Dinamikus memóriakezelés

Hasonló dokumentumok
Alkalmazott modul: Programozás 10. fejezet. Strukturált programozás: dinamikus memóriakezelés. Giachetta Roberto

Alkalmazott modul: Programozás 8. előadás. Strukturált programozás: dinamikus memóriakezelés. Dinamikus memóriakezelés. Dinamikus memóriakezelés

8. gyakorlat Pointerek, dinamikus memóriakezelés

OOP #14 (referencia-elv)

Bevezetés a programozásba I 10. gyakorlat. C++: alprogramok deklarációja és paraméterátadása

Bevezetés a Programozásba II 8. előadás. Polimorfizmus Giachetta Roberto

C memóriakezelés. Mutató típusú változót egy típus és a változó neve elé írt csillag karakterrel hozhatjuk létre.

Bevezetés a Programozásba II 11. előadás. Adatszerkezetek megvalósítása. Adatszerkezetek megvalósítása Adatszerkezetek

Bevezetés a Programozásba II 2. előadás. Adattípusok megvalósítása egységbe zárással. Adattípusok megvalósítása egységbe zárással

Alkalmazott modul: Programozás 9. előadás. Strukturált programozás: dinamikus adatszerkezetek

3. Osztályok II. Programozás II

1. Alapok. Programozás II

Mutatók és mutató-aritmetika C-ben március 19.

Objektumelvű alkalmazások fejlesztése 6. gyakorlat. Öröklődés, polimorfizmus. Öröklődés Kódismétlődés objektum-orientált szerkezetben

JAVA PROGRAMOZÁS 2.ELŐADÁS

Bevezetés a Programozásba II 3. előadás. Biztonságos adattípusok megvalósítása

Programozási nyelvek Java

Bevezetés a Programozásba II 3. előadás. Biztonságos adattípusok megvalósítása. Biztonságos adattípusok megvalósítása

Programozás II. 3. gyakorlat Objektum Orientáltság C++-ban

Készítette: Nagy Tibor István

A C programozási nyelv V. Struktúra Dinamikus memóriakezelés

Pénzügyi algoritmusok

A verem (stack) A verem egy olyan struktúra, aminek a tetejéről kivehetünk egy (vagy sorban több) elemet. A verem felhasználása

Programozás II gyakorlat. 8. Operátor túlterhelés

Pelda öröklődésre: import java.io.*; import java.text.*; import java.util.*; import extra.*;

C programozási nyelv Pointerek, tömbök, pointer aritmetika

5. Gyakorlat. struct diak {

C++ programozási nyelv Konstruktorok-destruktorok

Programozás alapjai. 10. előadás

Osztályok. 4. gyakorlat

Java II. I A Java programozási nyelv alapelemei

Bevezetés a Programozásba II 12. előadás. Adatszerkezetek alkalmazása (Standard Template Library)

Programozás BMEKOKAA146. Dr. Bécsi Tamás 5. előadás

Programozás II. 4. Dr. Iványi Péter

C++ programozási nyelv Konstruktorok Gyakorlat

Bevezetés a programozásba I.

Java II. I A Java programozási nyelv alapelemei

Pénzügyi algoritmusok

Bevezetés, a C++ osztályok. Pere László

Programozás C++ -ban

Programozas 1. Strukturak, mutatok

Bevezetés a programozásba Előadás: Objektumszintű és osztályszintű elemek, hibakezelés

1.1. A forrásprogramok felépítése Nevek és kulcsszavak Alapvető típusok. C programozás 3

Statikus adattagok. Statikus adattag inicializálása. Speciális adattagok és tagfüggvények. Általános Informatikai Tanszék

1. Template (sablon) 1.1. Függvénysablon Függvénysablon példányosítás Osztálysablon

Programozás I. 3. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar

Programozás. (GKxB_INTM021) Dr. Hatwágner F. Miklós április 4. Széchenyi István Egyetem, Gy r

Objektumorientált programozás C# nyelven

A C programozási nyelv III. Pointerek és tömbök.

Széchenyi István Egyetem. Programozás III. Varjasi Norbert

Programozási nyelvek Java

1. Mi a fejállományok szerepe C és C++ nyelvben és hogyan használjuk őket? 2. Milyen alapvető változókat használhatunk a C és C++ nyelvben?

Java programozási nyelv 4. rész Osztályok II.

Már megismert fogalmak áttekintése

A C programozási nyelv III. Pointerek és tömbök.

Programozási Nyelvek: C++

Maximum kiválasztás tömbben

Visual C++ osztály készítése, adattagok, és metódusok, láthatóság, konstruktor, destruktor. Objektum létrehozása, használata, öröklés.

Alkalmazott modul: Programozás 11. előadás. Objektumorientált programozás: öröklődés

Számítógép és programozás 2

Memóriakezelés, dinamikus memóriakezelés

Programozási nyelvek a közoktatásban alapfogalmak II. előadás

Számítástechnika II. BMEKOKAA Előadás. Dr. Bécsi Tamás

Programozás alapjai II. (4. ea) C++

Alprogramok, paraméterátadás

Bevezetés a programozásba. 8. Előadás: Függvények 2.

Elemi Alkalmazások Fejlesztése II.

Java és web programozás

Bevezetés a programozásba Előadás: Tagfüggvények, osztály, objektum

Globális operátor overloading

A C# programozási nyelv alapjai

Számítástechnika II. BMEKOKAA Előadás. Dr. Bécsi Tamás

OOP: Java 8.Gy: Abstract osztályok, interfészek

Járműfedélzeti rendszerek II. 3. előadás Dr. Bécsi Tamás

tétel: különböző típusú adatokat csoportosít, ezeket egyetlen adatként kezeli, de hozzáférhetünk az elemeihez is

Bevezetés a C++ programozási nyelvbe

C# osztálydeníció. Krizsán Zoltán 1. .net C# technológiák tananyag objektum orientált programozás tananyag

Programozás C és C++ -ban

C++ programozási nyelv

C programozás. 6 óra Függvények, függvényszerű makrók, globális és

Programozás C++ -ban 2007/4

Kivételkezelés, beágyazott osztályok. Nyolcadik gyakorlat

találhatók. A memória-szervezési modell mondja meg azt, hogy miként

Programozás II. 2. Dr. Iványi Péter

A 32 bites x86-os architektúra regiszterei

C++ programozási nyelv

Programozás alapjai II. (9. ea) C++ többszörös öröklés, cast, perzisztencia

Bánsághi Anna 2014 Bánsághi Anna 1 of 33

Programozás II gyakorlat. 6. Polimorfizmus

500. AA Megoldó Alfréd AA 500.

Programozási nyelvek Java

1. Egyszerű (primitív) típusok. 2. Referencia típusok

Bevezetés a programozásba Előadás: A const

Programozási nyelvek Java

Programozási nyelvek I. 5. előadás (Gregorics Tibor anyagának felhasználásával)

7. fejezet: Mutatók és tömbök

500. CC Megoldó Alfréd CC 500.

Programozás alapjai. 7. előadás

Eseményvezérelt alkalmazások fejlesztése I 11. előadás. Szoftverek tesztelése

Bevezetés a Programozásba II 5. előadás. Objektumorientált programozás és tervezés

Átírás:

Pázmány Péter Katolikus Egyetem Információs Technológiai és Bionikai Kar Bevezetés a Programozásba II 10. előadás Dinamikus memóriakezelés 2014.04.28. Giachetta Roberto groberto@inf.elte.hu http://people.inf.elte.hu/groberto

Memóriaszegmensek Az operációs rendszer minden futó program számára fenntart egy területet a memóriából, ezt nevezzük memóriaszegmensnek minden program a saját szegmensében dolgozik, a szegmens mérete változhat futás közben minden szegmensbeli memóriahely (bájt) rendelkezik egy sorszámmal, ez a szegmensbeli memóriacíme, amelyen keresztül elérhető a programban A memória(szegmens) tekinthető egy vektornak, a memóriacím pedig annak egy indexe (általában hexadecimálisan adjuk meg, pl. 0x34c410) 0x34c40f 0x34c410 0x34c411 PPKE ITK, Bevezetés a programozásba II 10:2

Memóriacím lekérdezése Minden változó rendelkezik memóriacímmel ezt C++-ban hasonlóan kezelhetjük, mint magát a változót mivel a változó típusától függően több bájton is tárolódhat, mindig csak az első bájt címét kapjuk vissza Egy változó memóriacímét az & operátorral kérdezhetünk le, ez a referenciaoperátor, &<változónév> a változó első bájtjának memóriabeli címe Pl.: int i = 128; cout << i << " " << &i; // lehetséges eredmény: 128 0x22ff6c PPKE ITK, Bevezetés a programozásba II 10:3

Műveletek memóriacímekkel Lehetőségünk van a memóriában történő ugrásra a memóriacímet számként kezelhetjük, növelhetjük, illetve csökkenthetjük (a +, -, ++, -- operátorokkal) azonban egy egyszeri növelés esetén a címérték nem eggyel fog nőni, hanem a következő változó címét adja vissza Pl.: cout << i << << &i << << &i+1; // lehetséges eredmény: 128 0x22ff6c 0x22ff70 0x22ff6c 0x22ff70 PPKE ITK, Bevezetés a programozásba II 10:4

Referencia változók A referencia változók (vagy álnevek) olyan változók, amelyek nem a változók értékét, hanem memóriacíműket másolják át, így ugyanarra a területre mutatnak a memóriában Pl.: int i = 128; int j = i; // egyszerű változó int& k = i; // referencia változó i j k PPKE ITK, Bevezetés a programozásba II 10:5

Mutatók deklarálása Egy még speciálisabb változótípus a mutató (pointer), amely memóriacímet tárol értékként mutató létrehozásával egy új adatot viszünk a memóriába, amely másik adat memóriacímét tartalmazza általánosabb célú, mint a referencia A mutató létrehozásakor meg kell adnunk, milyen típusú változó címét fogja eltárolni, és ez onnantól nem változtatható egy típushoz a hozzá tartozó mutató típus a <típusnév>* mutató létrehozása: <típus> *<mutatónév>; pl.: int* ip; // egy int-re mutató pointer PPKE ITK, Bevezetés a programozásba II 10:6

Mutatók használata A mutatók hasonlóan viselkednek, mint más változóink értéket adhatunk nekik, élettartammal rendelkeznek nem kell nekik adni kezdőértéket, ekkor egy véletlenszerű címet tartalmaznak kezdetben az értéküket lehet növelni, csökkenteni (+, -, ++, --), ekkor a megfelelő memóriacímbeli objektumra ugranak mutatókat nem csak egyértékű változókra, hanem tömbökre, függvényekre, vagy bármilyen típusra állíthatunk A mutató mérete rögzített minden típusra, 32 bites architektúrában 4 byte (emiatt csak 4GB memória címezhető meg), 64 bites architektúrában 8 byte PPKE ITK, Bevezetés a programozásba II 10:7

Mutatók használata Mutató értékadására használhatjuk a referencia operátort, így ráállíthatjuk egy már létező változó memóriacímére, pl.: char ch = 'a'; char *chp = &ch; // lekérdezzük ch memóriacímét, amit értékül // adunk chp mutatónak, innentől rámutat chp ch PPKE ITK, Bevezetés a programozásba II 10:8

Mutatók lekérdezése Amikor mutatók értékét kezeljük, akkor egy memóriacímet kapunk, ha az általa mutatott változó értékére vagyunk kíváncsiak, akkor ismét használnunk kell a * operátort, pl.: char ch = 'a', *chp = &ch; // deklarálunk egy karakter változót és egy // mutatót cout << chp; // lekérdezzük a chp tartalmát, azaz ch címét // tehát az eredmény a memóriacím cout << *chp; // lekérdezzük a chp által mutatott változó // tartalmát, az eredmény a lesz cout << &chp; // lekérdezzük a chp mutató címét PPKE ITK, Bevezetés a programozásba II 10:9

Biztonságos használat Mutatók használata veszélyes lehet, ha olyan mutatóra hivatkozunk, amely nem mutat megfelelő helyre (pl. nem adtunk neki értéket, vagy amit adtunk, már megsemmisült), szegmenshibát kapunk (futási időben) Célszerű a mutatónak létrehozás után a nulla (NULL, 0) kezdőértéket adni, mert így utólag ellenőrizhető lesz pl.: int *ip = NULL; // vagy int *ip = 0; if (ip){ /* ez az ág akkor hajtódik végre, ha ip nullától különböző értéket tárol */ } PPKE ITK, Bevezetés a programozásba II 10:10

Mutató, mint bejáró A mutatókat ráállíthatjuk tömbre is (pontosabban az első elemére), illetve használhatjuk tömbök bejárására is, így nem csupán indexeléssel férhetünk hozzá az adatokhoz pl.: int array[10]; for (int* p = array; p!= array + 10; p++) // mutató használata indexelés helyett, // ugyanúgy 10 lépést teszünk meg cin >> *p; // tömb eleminek feltöltése // ugyanez rövidebben: int array[10], *p = array; while (p!= array + 10) cin >> *p++; PPKE ITK, Bevezetés a programozásba II 10:11

A referencia, mint mutató A referencia tekinthető úgy is, mint egy korlátozott felhasználású mutató, amely mindig garantáltan biztonságos Ugyanakkor a referencia használata is kiválthat szegmenshibát, lokális változó értékének visszaadásakor, pl.: int& BadFunction(){ // cím szerint adja vissza a változót int value = 1; return value; } // itt a lokális változó megsemmisül int val = BadFunction(); cout << val << endl; // szegmenshiba, a változó már nem létezik PPKE ITK, Bevezetés a programozásba II 10:12

Konstans mutatók és referenciák Referencia, illetve mutató változók is lehetnek konstansok referencia esetén az érték nem módosítható: <típus> const &<név> = <változó>; mutató esetén kétféle módon is korlátozhatjuk a használatot lehet a mutatott érték konstans, ekkor nem változtatható a hivatkozott változó értéke, de a mutatót átállítható: <típus> const *<név>; lehet a mutató konstans, ekkor nem állítható át másik memóriacímre, de a mutatott érték változtatható: <típus> * const <név> = <változó>; lehet a mutató és a mutatott érték is konstans: <típus> const * const <név>; PPKE ITK, Bevezetés a programozásba II 10:13

Konstans mutatók és referenciák Pl: double d1 = 10, d2 = 50; double const &d1r = d1; // konstans referencia double const * d1p1 = &d1; // mutató konstansra double * const d1p2 = &d1; // konstans mutató double const * const d1p3 = &d1; // konstans mutató konstans értékre d1r = 100; // HIBA, az érték nem módosítható *d1p1 = 50; // HIBA, az érték nem módosítható *d1p2 = 50; // az érték módosítható *d1p3 = 50; // HIBA d1p1 = &d2; // átállíthatjuk más memóriacímre d1p2 = &d2; // HIBA, a mutató nem állítható át d1p3 = &d2; // HIBA PPKE ITK, Bevezetés a programozásba II 10:14

Mutatóra állított mutatók és referenciák Mivel a mutatók is értékek, rájuk is lehet mutatót állítani ekkor jeleznünk kell, hogy a mutató célja is mutató, azaz halmoznunk kell a * jelet pl.: int value = 0; int *intp = &value; int **intpp = &intp; // mutatóra állított mutató cout << **intpp; // kiírja value értékét hasonlóan referencia is állítható mutatóra, így a mutató is használható cím szerinti paraméterátadáskor, pl.: int *&intpref = intp; cout << *intpref; // kiírja value értékét PPKE ITK, Bevezetés a programozásba II 10:15

Memóriafoglalási lehetőségek Memóriahelyeket kétféleképpen foglalhatunk le: automatikusan: változó létrehozásakor lefoglalódik hozzá egy memóriahely is, ezt nem befolyásolhatjuk manuálisan (dinamikusan): lehetőségünk van explicit megadni a kódban, hogy lefoglalunk egy a memóriahelyet ehhez a new operátort használjuk, és meg kell adnunk a típusát is, pl. new double; a létrehozás visszaad egy memóriacímet, amelyet a helyfoglalás megkapott (illetve annak az első bájtját) A lefoglalással visszakapott memóriacímet megkaphatja egy mutató, pl.: int *ip = new int; PPKE ITK, Bevezetés a programozásba II 10:16

Memóriafoglalási lehetőségek szétválaszthatjuk a változó deklarációját a hozzá tartozó memóriaterület lefoglalásától, pl.: int *ip; // ekkor i még csak egy mutató ip = new int; // új memóriaterület a mutatónak két hely kerül lefoglalásra a memóriában, egy a mutatónak, egy az értéknek többször is lefoglalhatunk helyet egy mutatónak, pl.: int *ip = new int; ip = new int; ip = new int; új memóriaterület foglalásakor a régi memóriaterület is bent marad a szegmensben, de a mutatón keresztül már nem elérhető (de memóriaműveletekkel igen) PPKE ITK, Bevezetés a programozásba II 10:17

Memóriaterületek A programok a használat szempontjából három területet különböztetnek meg: globális terület (global): konstansok és globális változók, amelyek a program futása során mindig jelen vannak verem (stack): a lokális változók, amelyeket automatikusan hoztunk létre működésében olyan, mint egy verem, mert mindig az utolsó blokkban létrehozott változó törlődik elsőként a blokk végeztével kupac (heap): a manuálisan lefoglalható memóriaterület, általában a legnagyobb részét képezi a szegmensnek a tömbök és szövegek is ide kerülnek PPKE ITK, Bevezetés a programozásba II 10:18

Memóriahely felszabadítás Ahogy lefoglalunk, úgy lehetőségünk van törölni is memóriahelyet programunkban az automatikusan lefoglalt memória törlését a program magától végzi, ezt nem befolyásoljuk a manuálisan létrehozott memóriahelyeket nekünk kell törölnünk, vagy a program végéig a memóriában maradnak a törlésre a delete operátor szolgál pl.: float* flp = 0; // flp nem hivatkozik semmire flp = new float; // manuálisan lefoglaljuk a helyet delete flp; // töröljük a lefoglalt helyet PPKE ITK, Bevezetés a programozásba II 10:19

Biztonságos dinamikus helyfoglalás Minden new operátornak kell rendelkeznie egy delete párral, azaz a dinamikusan létrehozott változókat törölni is kell A nem törölt változók használatuk után is foglalják a memóriát, bár már nincs mutató rájuk állítva, az ilyen területeket nevezzük memóriaszemétnek, pl.: int *ip = new int; // dinamikusan lefoglaltuk a memóriaterületet ip = new int; // ekkor az előző terület memóriaszemét lesz Sosem a mutatót, csak a dinamikusan lefoglalt területet töröljük, így ha több mutató hivatkozik ugyanarra a területre, elég egyszer elvégeznünk a törlést PPKE ITK, Bevezetés a programozásba II 10:20

Többszörös dinamikus foglalás Egyszerre több memóriahelyet is lefoglalhatunk azonos típusból a [] operátorral, ekkor azok egymás után helyezkednek el a memóriában, pl.: int *ip = new int[5]; // öt memóriahely lefoglalása a törléshez delete operátornak jelölnünk kell, hogy több helyről van szó, szintén a [] operátorral, pl.: delete[] ip; ha törlésnél véletlenül lefelejtjük a tömb jelölést, akkor csak az első érték törlődik, a többi a memóriában marad a törlés után a mutató továbbra is használható, de az értékek elvesznek PPKE ITK, Bevezetés a programozásba II 10:21

Példa Pl.: int *ip = NULL; // mutató létrehozása ip PPKE ITK, Bevezetés a programozásba II 10:22

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás ip PPKE ITK, Bevezetés a programozásba II 10:23

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás *ip = 1000; // új érték ip 1000 PPKE ITK, Bevezetés a programozásba II 10:24

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás *ip = 1000; // új érték ip = new int; // új érték, az előzőből memóriaszemét lesz ip 1000 PPKE ITK, Bevezetés a programozásba II 10:25

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás *ip = 1000; // új érték ip = new int; // új érték, az előzőből memóriaszemét lesz delete ip; // memóriahely törlése, ip-ben megmarad a cím ip 1000 PPKE ITK, Bevezetés a programozásba II 10:26

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás *ip = 1000; // új érték ip = new int; // új érték, az előzőből memóriaszemét lesz delete ip; // memóriahely törlése, ip-ben megmarad a cím ip = new int[2]; // 2 memóriahely foglalása ip 1000 PPKE ITK, Bevezetés a programozásba II 10:27

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás *ip = 1000; // új érték ip = new int; // új érték, az előzőből memóriaszemét lesz delete ip; // memóriahely törlése, ip-ben megmarad a cím ip = new int[2]; // 2 memóriahely foglalása delete[] ip; // törlés ip 1000 PPKE ITK, Bevezetés a programozásba II 10:28

Példa Pl.: int *ip = NULL; // mutató létrehozása ip = new int; // memóriahely foglalás *ip = 1000; // új érték ip = new int; // új érték, az előzőből memóriaszemét lesz delete ip; // memóriahely törlése, ip-ben megmarad a cím ip = new int[2]; // 2 memóriahely foglalása delete[] ip; // törlés ip = NULL; // újbóli kinullázás ip 1000 PPKE ITK, Bevezetés a programozásba II 10:29

A primitív dinamikus tömb A többszöri memóriafoglalással lényegében egy tömböt hozhatunk létre, amely a primitív tömb dinamikus megfelelője az elemei elérhetőek a [ ] operátorral, 0-tól indexelve működése lényegében megegyezik a statikus dinamikus tömbével, de paraméterben megadható változó is méretnek pl.: int size; cin >> size; int* array = new int[size]; // tömb lefoglalása for (int i = 0; i < size; i++) cin >> array[i]; // elemek bekérése delete[] array; // tömb törlése PPKE ITK, Bevezetés a programozásba II 10:30

Tömbelem címzés A tömbelem indexelés igazából a memóriában való címelérés, mivel a memóriacímek a + operátorral is elérhetőek azaz a[i] leírható *(a+i) formában is, hiszen a tömb címével a változó mennyiséggel arrébb lévő memóriacím értékét akarjuk kiolvasni (ezért indexelünk 0-tól) mivel az összeadás kommutatív, ezért az index és a tömbnév fel is cserélhető a kifejezésben pl.: float* a = new float[10]; cin >> a[5]; // ugyanez: cin >> *(a+5); // ugyanez: cin >> 5[a]; PPKE ITK, Bevezetés a programozásba II 10:31

Többdimenziós tömbök Lehetőségünk van többdimenziós tömbök létrehozására is a tömbök tömbje módszerrel azaz mutatókra mutatót állítunk, így a külső tömbünk fogja tartalmazni a mutatókat, amelyek a mátrix soraira hivatkoznak létrehozzuk a mutatókat tároló tömböt, majd utána mindegyikre felfűzzük az értékeket tároló tömböt, tehát egy ciklusra van szükségünk, pl.: float** matrix = new float*[4]; // 4 sora lesz a mátrixnak for (int i = 0; i < 4; i++) matrix[i] = new float[3]; // 3 oszlopa lesz a mátrixnak PPKE ITK, Bevezetés a programozásba II 10:32

Többdimenziós tömbök a mátrix megjelenése a memóriában: matrix matrix[0] matrix[3] matrix[0][0] matrix[1][0] PPKE ITK, Bevezetés a programozásba II 10:33

Többdimenziós tömbök a létrehozást követően az indexelés és a sorok hozzáférése a megszokott módon történik, pl.: cin >> matrix[3][2]; // elem bekérése cout << **matrix; // mátrix 1. sorának 1. eleme float* row = matrix[3]; // sor átadása egy mutatónak törléskor külön kell törölnünk minden sort, majd a mutatókat tartalmazó tömböt, pl.: for (int i = 0; i < 4; i++) delete[] matrix[i]; // sorok törlése delete[] matrix; // mutatók törlése ugyanez megvalósítható magasabb dimenziókban is, pl.: float*** m3d = new float**[4]; PPKE ITK, Bevezetés a programozásba II 10:34

Objektumorientált környezetben Objektumorientált környezetben is számos helyen előfordul dinamikus memóriakezelés hivatkozásokat (aggregációkat) mutatók segítségével valósíthatunk meg, hogy ne másolódjanak az objektumaink polimorfizmus esetén mutatók segítségével tudjuk biztosítani a dinamikus típus futás során történő megadását egy objektum bármilyen mezője lehet mutató, és a hozzá tartozó értéket lefoglalhatjuk dinamikusan (de ekkor gondoskodnunk kell annak törléséről is) Egy objektum mindig tisztában van saját memóriacímével (objektumhivatkozás), amelyet lekérhetünk PPKE ITK, Bevezetés a programozásba II 10:35

Objektumhivatkozások Egy típuspéldányban elérhető annak valamennyi mezője és metódusa, akár rejtett, akár nem Ugyanakkor lehetőség van magát a teljes objektumot (pontosabban a memóriacímét) elérni saját magán belül a this kulcsszó használatával tehát ez egy mutatót ad vissza az aktuális példányra ugyanúgy használható, mint bármely más mutató, amelyet az objektumra állítottunk, azaz lekérdezhető általa az összes tag: this-><tagnév> ha a konkrét objektumra van szükségünk, akkor a *this-t használunk PPKE ITK, Bevezetés a programozásba II 10:36

Objektumhivatkozások Pl.: class MyClass { private: int _value; public: void SetValue(int value){ _value = value; // ugyanez: this->_value = value; } MyClass* ReturnMyPointer(){ return this; // visszaadja a mutatót a példányra } PPKE ITK, Bevezetés a programozásba II 10:37

Objektumhivatkozások Pl.: }; MyClass ReturnCopyOfMyself(){ return *this; // visszaadja az objektum másolatát } MyType t; MyType* tp = t.returnmypointer(); // hivatkozás tp->setvalue(10); // másként: t.setvalue(10); MyType tcopy = t. ReturnCopyOfMyself(); // másolat tcopy.setvalue(20); // nem befolyásolja t értékét PPKE ITK, Bevezetés a programozásba II 10:38

Dinamikusan foglalt mezők Természetesen típusok mezői is lehetnek mutatók, és allokálhatunk nekik dinamikusan memóriaterületet ezt általában a konstruktorban végezzük de az így létrehozott értékeket manuálisan kell törölni, különben a példány megsemmisülése után is megmarad a törlést akkor kell elvégezni, amikor a példány törlődik A típuspéldány megsemmisítéséért felelős műveletet nevezzük destruktornak a destruktor automatikusan lefut, amikor törlődik a változó (lokális változó esetén a blokk végére érünk, dinamikus létrehozás esetén meghívjuk a delete műveletet) PPKE ITK, Bevezetés a programozásba II 10:39

Destruktor A destruktorban olyan utasításokat tárolunk benne, amelyek kitakarítják az általunk használt memóriaterületet a dinamikusan foglalt mezőket töröljük, a többit nem kell ha nincs dinamikusan foglalt mező, akkor nem kell A destruktor a ~<típusnév> nevet kapja, mindig publikus, nincs típusa, nincs paramétere, ezért nem túlterhelhető: class <típus> { public: <típusnév>() { } // konstruktor ~<típusnév>() { } // destruktor }; PPKE ITK, Bevezetés a programozásba II 10:40

Destruktor Pl.: class MyClass { public: MyClass(){ cout << "Hello! "; } // konstruktor ~MyClass(){ cout << "Byebye!"; } // destruktor }; int main(){ MyClass mc; // itt fut le a konstruktor return 0; } // itt fut le a destruktor // eredménye: Hello! Byebye! ELTE IK, Alkalmazott modul: Programozás 8:41

Destruktor Pl.: class MyClass { private: int* t; // dinamikusan lefoglalt tömb public: MyClass(){ t = new int[10]; } // konstruktor // nincs destruktor }; int somefunction(){ MyClass mc; // létrejön a t tömb } // a t tömb a memóriában marad (memóriaszemét) ELTE IK, Alkalmazott modul: Programozás 8:42

Destruktor Pl.: class MyClass { private: int* t; // dinamikusan lefoglalt tömb public: MyClass(){ t = new int[10]; } // konstruktor ~MyClass(){ delete[] t; } // destruktor }; int somefunction(){ MyClass mc; // létrejön a t tömb } // törlődik a t tömb (nincs memóriaszemét) ELTE IK, Alkalmazott modul: Programozás 8:43

Destruktor öröklődése A destruktor (a konstruktorhoz hasonlóan) automatikusan öröklődik és minden ős destruktor megívódik a leszármazott destruktor meghívásakor elsőként a leszármazotté, majd a sorrendben az ősök destruktorai felfelé az öröklődési fán (vagyis pont fordított sorrendben, mint a konstruktor esetén) Pl.: class MyBaseClass { public: MyBaseClass() { cout << "1 Hello! "; } ~MyBaseClass() { cout << "1 Byebye! "; } }; PPKE ITK, Bevezetés a programozásba II 11:44

Destruktor öröklődése class MyChildClass : public MyBaseClass { public: MyChildClass() { cout << "2 Hello! "; } ~MyChildClass() { cout << "2 Byebye! "; } }; int main(){ MyChildClass mcc; // konstruktor hívás return 0; } // destruktor hívás // 1 Hello! 2 Hello! 2 Byebye! 1 Byebye! PPKE ITK, Bevezetés a programozásba II 11:45

Virtuális destruktor Polimorfizmus esetén a program az objektum megsemmisítésénél a statikus típust veszi figyelembe, ezért annak destruktorát hívja meg pl.: ~MyBaseClass() int main(){ MyBaseClass* mbc = new MyChildClass(); delete mbc; return 0; } // 1 Hello! 2 Hello! 1 Byebye! PPKE ITK, Bevezetés a programozásba II 11:46

Virtuális destruktor ha a dinamikus típus szerint akarjuk elvégezni a törlést, akkor a statikus típusban a destruktort virtuálisnak kell megadni pl.: virtual ~MyBaseClass() int main(){ MyBaseClass* mbc = new MyChildClass(); delete mbc; return 0; } // 1 Hello! 2 Hello! 2 Byebye! 1 Byebye! PPKE ITK, Bevezetés a programozásba II 11:47

Példa Feladat: Készítsünk egy teljesen objektumorientált grafikus felületű alkalmazást, amelyben egy gombbal tudjuk módosítani egy címke feliratát, egy másikkal pedig ki tudunk lépni. a programunk akkor lesz teljesen objektumorientált, ha a főprogram csupán elindít egy objektumot, más tevékenységet nem végez ehhez megvalósítunk egy saját alkalmazás osztályt (MyApplication), amely az alkalmazás leszármazottja lesz konstruktorában létrehozza a vezérlőket, így azokat nem kell a főprogramban létrehozni mivel dinamikusan hoztuk őket létre, törölni is kell, így készítünk egy destruktort is PPKE ITK, Bevezetés a programozásba II 10:48

Példa Tervezés: class MyApplication Application # _widgets :std::vector<widget*> # _focus :unsigned int # _isexiting :bool # _sizex :int # _sizey :int + Application(int, int) + ~Application() + addwidget(widget*) :void + run() :void + shutdown() :void + width() :int {query} + height() :int {query} MyApplication -_widgets * Widget # _posx :int # _posy :int # _sizex :int # _sizey :int # _isfocused :bool + Widget(int, int, int, int) + ~Widget() + show() :void {query} + handleevent(genv::event) :void + setfocus(bool) :void + ismouseover(int, int) :bool {query} + isfocused() :bool {query} + positionx() :int {query} + positiony() :int {query} + width() :int {query} + height() :int {query} + MyApplication(int, int) + ~MyApplication() PPKE ITK, Bevezetés a programozásba II 10:49

Példa Megoldás (myapplication.cpp): MyApplication::MyApplication(int sx, int sy) : Application(sx, sy) // átadjuk a méretet { Label* l = new Label(50, 50, "HUHU"); // hozzáadjuk a vezérlőket addwidget(l); } MyApplication::~MyApplication() { for (int i = 0; i < _widgets.size(); i++) // töröljük a létrehozott vezérlőket delete _widgets[i]; } PPKE ITK, Bevezetés a programozásba II 10:50

Függvénymutatók Mutatók nem csupán adatokra, de tevékenységekre, azaz alprogramokra, függvényekre is állíthatóak, ezeket nevezzünk függvénymutatóknak (function pointer) célja, hogy a végrehajtott függvény cserélhető legyen, azaz futás közben cserélhető legyen a konkrét végrehajtandó függvény a függvény nevét helyezzük mutatóba, és megadjuk a teljes szintaxist: <típus> (*<mutató név>)(<paraméterek>) a mutatott függvény szintaxisának meg kell egyeznie a mutató szintaxisával, a paramétereknek csak a típusát adjuk meg PPKE ITK, Bevezetés a programozásba II 10:51

Függvénymutatók Pl.: // létrehozunk szabványos függvényeket int add(int a, int b){ return a + b; } int mul(int a, int b){ return a * b; } int main() { // létrehozunk egy függvénymutatót int (*op)(int, int); // a mutató neve op, két egész számot fogad, // és egész számot ad vissza op = NULL; // ugyanúgy nullázható, mint bármely // más mutató PPKE ITK, Bevezetés a programozásba II 10:52

Függvénymutatók // beállíthatjuk azonos szintaxisú függvényre: op = add; // beállítjuk az add-ra cout << (*op)(2, 3) << endl; // meghívjuk a mutató mögötti // függvényt, eredménye: 5 op = mul; // beállítjuk az mul-ra } cout << (*op)(2, 3) << endl; // meghívjuk a mutató mögötti // függvényt, eredménye: 6 PPKE ITK, Bevezetés a programozásba II 10:53

Függvénymutatók A függvénymutatókat leginkább paraméterátadásnál használjuk, ezáltal jobban személyre szabhatjuk a függvény viselkedését Pl.: int runoperation(vector<int> values, int start, (int)(*op)(int, int)) // a harmadik paraméter egy függvénymutató { int result = start; for (int i = 0; i < values.size(); i++) result = (*op)(result, values[i]); // végrehajtjuk a tevékenységet return result; } PPKE ITK, Bevezetés a programozásba II 10:54

Függvénymutatók int main() { vector<int> values(10); // elemek megadása } cout << runoperation(values, 0, add) << endl; // összeadjuk az elemeket cout << runoperation(values, 1, mul) << endl; // összeszorozzuk az elemeket A működés hasonló a virtuális függvényéhez (futás közben cserélhetjük a viselkedést), de jóval rugalmasabb annál, és nem szükséges öröklődés hozzá PPKE ITK, Bevezetés a programozásba II 10:55

Függvénymutatók objektumorientált környezetben Függvénymutatót metódusokra is létesíthetünk, ám ebben az esetben a hívónak mindig tisztában kell lennie, mely objektum metódusát kezeljük deklarációkor jelölnünk kell a függvény osztályát: <típus> (<osztálynév>::*<mutató név>)(< >) átadáskor is jelölnünk kell az osztályt: <mutató név> = &<osztálynév>::<függvény név>; végrehajtáskor az objektumot kell megadnunk: (<objektum>.*<mutató név>)(<paraméterek>) megadhatjuk ugyanazon osztály egy metódusát is: (this->*<mutató név>)(<paraméterek>) PPKE ITK, Bevezetés a programozásba II 10:56

Függvénymutatók objektumorientált környezetben Pl.: class MyClass { public: int add(int a, int b){ return a + b; } int mul(int a, int b){ return a * b; } void setoperation( (int)(myclass::*op)(int, int)); // jelöljük a metódus osztályát is void RunOperation(); private: int (MyClass::*op) (int, int) _operation; // mezőként is jelöljük az osztályt PPKE ITK, Bevezetés a programozásba II 10:57

Függvénymutatók objektumorientált környezetben void MyClass::setOperation( (int)(myclass::*op)(int, int) op) { _operation = op; } void MyClass::runOperation() { (this->*_operation)( ) // hívás az objektum jelölésével } MyClass mc; mc.setoperation(&myclass::add); // megadás az osztály jelölésével PPKE ITK, Bevezetés a programozásba II 10:58

Példa Feladat: Készítsünk egy teljesen objektumorientált grafikus felületű alkalmazást, amelyben egy gombbal tudjuk módosítani egy címke feliratát, egy másikkal pedig ki tudunk lépni. alakítsuk át a gombok működését úgy, hogy egyetlen gomb típussal megvalósíthassunk tetszőleges tevékenységet ehhez a gomb működését átírjuk úgy, hogy függvénymutatóban (void (*action)()) kapja meg a végrehajtandó tevékenységet a konkrét tevékenységeket (címke átírása, kilépés az alkalmazásból) a MyApplication osztályban írjuk meg metódusként PPKE ITK, Bevezetés a programozásba II 10:59

Példa Tervezés: class CustomActions Application MyApplication - _label :Label* + MyApplication(int, int) + ~MyApplication() - changelabelaction() :void - exitapplicationaction() :void -_label Label # _text :std::string + Label(int, int, std::string) + show() :void {query} + settext(std::string) :void + gettext() :std::string {query} #_app Button # _text :std::string # _app :MyApplication* # _action :void (*)() + Button(int, int, int, int, std::string) + show() :void {query} + handleevent(genv::event) :void + settext(std::string) :void + setaction(myapplication*, void (*)()) :void + gettext() :std::string {query} Widget PPKE ITK, Bevezetés a programozásba II 10:60

Példa Megoldás (myapplication.cpp): MyApplication::MyApplication(int sx, int sy) : Application(sx, sy) // átadjuk a méretet { _label = new Label(50, 50, "HUHU"); } Button* lmb = new Button(50, 150, 100, 25, "Modify"); lmb->setaction(this, &MyApplication::changeLabelAction); // beállítjuk a végrehajtandó akciót // (függvénymutató segítségével) PPKE ITK, Bevezetés a programozásba II 10:61