Keresés

Új hozzászólás Aktív témák

  • mgoogyi

    senior tag

    válasz Teasüti #4155 üzenetére

    Amit lehet a stacken jobb tartani(gyorsabb, nem kell delete), a nagy méretű adatot meg heap-en(van neki hely gazdagon).
    A new+delete párra meg vannak osztályok, amikbe becsomagolva megkapod az eltakarítást.
    std::vector a dinamikus tömbhöz, std::unique_ptr, std::auto_ptr egyéb mutatóhoz.
    Ezek az osztályok a delete-et úgy oldják meg, hogy a destruktorukban meghívódik az általuk tárolt pointerre a delete.
    És ezeket a pointer "wrapper"-eket pedig stack-en tartod valahol és amikor elhagyod a scope-ot, szépen lefut a destruktor és magától felszabadul a memória.
    Ez azért nagyon fontos, mert minden programozó hibázik és nem igazán függ a hibázás gyakorisága attól, hogy mióta űzöd a mesterséget. Ezzel védjük magunkat a memory leak-től.

    A vector persze elsődlegesen nem pointer wrapper, arra találták ki, hogy nagyon kényelmesen legyen egy bármikor átméretezhető tömböd. A belsejében egy dinamikus tömb van.

  • jattila48

    aktív tag

    válasz Teasüti #4153 üzenetére

    "Tehát ami nekem átjött ebből a beszélgetésből, hogy ha new operátorral hozok létre tömböt (foglalok le dinamikus memóriát), akkor az egy ún. heap-re kerül,..."
    Ez igaz.
    "...míg ha new nélkül, akkor a stack-be."
    Ez nem feltétlenül igaz, mert a globális adatterületen is deklarálhatsz tömböt.
    Egyéb gyakorlati jelentősége pedig az, amit eddig leírtam. Röviden összefoglalva:
    - A tömb név nem változó, nem adhatsz neki értéket, és nem képezheted a címét.
    -A pointer változó, ami memória címet tartalmaz (pl. a heapen lefoglalt memória címét, de lehet más pl. "hagyományosan" deklarált tömb címe is). A pointer értéke megváltoztatható, és a címe is képezhető.
    -Tömböt csak konstans méretűt deklarálhatsz (kivéve C99), míg new-val változó méretű memóriát foglalhatsz.
    -Tömböt nem szabad felszabadítani, new-val létrehozott memóriát pedig fel kell szabadítani.
    Kb. ennyi ami gyakorlatban különbség a két fogalom között, és amiről minden programozónak tudnia kell.
    Még egy "apróság": C++ -ban tömb név, mint konstans pointer lehet template argumentum (mivel fordítási időben kiértékelhető), pointer viszont nem (mivel csak futási időben értékelhető ki).

  • jattila48

    aktív tag

    válasz Teasüti #4135 üzenetére

    "De hisz a tömb is egy pointer. :F"

    Nem, nem az! C-ben valóban hasonlóan kezelheted a tömböt és a pointert, de ettől még a tömb nem pointer. Sok félreértés és hiba forrása ez, ugyanakkor nagyon kényelmes is tud lenni. A pointer egy változó, ami történetesen memória címet tartalmaz. Mint minden változónak, neki is van címe, és megváltoztathatod az értékét (balérték). Ezzel szemben a tömb egy konstans memória cím (ahol a tönb kezdődik). Ezt a konstanst a fordító/linker/betöltő számolja ki, amit te nem változtathatsz meg, és a címét sem képezheted (hiszen nem is létezik a memóriában).
    int tomb[10];
    tomb=new int[10]; //Hiba! tombnek nem lehet erteket adni, hiszen az konstans
    int **ppi=&tomb; //Hiba! tombnek nem kepezheto a cime, mert az nem valtozo
    int *pi=new int[10]; //OK
    int **ppi=π //OK valtozonak kepezheto a cime
    pi=new int[10]; //OK valtozo erteke megvaltoztathato

    Amikor tömb elemre hivatkozol (kiindexeled), a fordító ehhez a konstans címhez adja hozzá az indexet, és erről a címről veszi elő a tömb elemet (intelnél egyetlen utasítás pl. : mov eax, DWORD PTR[ebx+0x1000]; indirekt indexelt címzési mód, ahol 0x1000-nél kezdődik a tömb, ebx-ben pedig az elérni kívánt tömbelem indexe található). Tehát itt a kódba a fordító beleírja a tömb konstans kezdőcímét.
    Ha new-val foglalsz helyet, akkor a lefoglalt memória kezdőcímét egy változó kapja értékül, ez a változó a pointer. Ha ezen a memória területen tömb szintaxissal hivatkozol egy elemre, akkor a fordítónak (intel esetén) ehhez legalább két utasításra lesz szüksége. Az elsővel regiszterbe tölti a változó (pointer) címéről a változó értékét (ez maga a new-val foglalt memória kezdőcíme lesz). Ha pl. a pointered címe eax-ben van, akkor mov eax,DWORD PTR[eax] utasítás ezt megoldja. Majd az elem elérése mov eax,DWORD PTR[eax+esi] utasítással történik, ahol az esi tartalmazza azi indexet. Látható, hogy ez jóval lassabb művelet, mint az "igazi" tömb kiindexelése.

  • mgoogyi

    senior tag

    válasz Teasüti #4135 üzenetére

    Igen, így van, a tömböt is pointerként kezeled. Gyakorlatilag a 0. elemére mutató pointer van a változóban értékként.

    A kérdésedre a válasz igen, mindig más memóriaterületen kapsz egy új összefüggő memóriaterületet az új tömbödnek. A pointert írasd ki és látod, hogy mindig más értéket kap. Ez a logikai címe a kezdeti memóriacellának (a programod csak a memória egy számára logikailag elkülönített részét látja).
    A régi tömb ugyanúgy meglesz az előző ciklusodból, csak a címe már nem lesz meg és anélkül nehéz felszabadítani.
    Csináld ciklus belsejében simán, amit írtál. Ha jó sok kört fut a ciklusod, megzabálja a géped memóriáját.
    Azért figyelj arra, hogy ne zabálja meg az összeset, kevés elemű int tömböt foglalj le 1000-szer ciklusban, kísérletnek elég.

  • mgoogyi

    senior tag

    válasz Teasüti #4132 üzenetére

    A new meg nem new között az a különbség, hogy a new-val a heap-en foglalsz memóriát, new nélkül meg a stack-en. (itt nem erre a nem szabványos dologra gondolok: int p [I]= {};)
    Ez utóbbi elég limitált tud lenni, ha majd egyszer írsz rekurzív(önmagát hívó) függvényt, belefuthatsz.
    Ezt hívják stackoverflow-nak. Olvass utána, hogy mi az a heap és mi az a stack.
    Egyébként paraméterezett méretű tömböt new nélkül nehéz lértehozni.

    A new (new []) esetében egy pointer-t kapsz vissza és a te feladatod ezt eltakarítani delete-tel ( delete []).
    Azért, hogy ezt ne felejtsd el, javasolt olyan osztályokat használni, amik megteszik ezt helyetted.
    A vector meghívja helyetted a new[]-t és a delete[]-t, menet közben át is méretezi a benne lévő array-t.
    Kényelmessé és biztonságossá teszi a tömbkezelést.
    A unique_ptr pedig általánosságban jó arra, hogy egy objektum felszabadításával ne kelljen foglalkozni.

    Ha ezeket nem használod és elfelejteted a delete-et, akkor keresheted, hogy miért növekszik a progid memóriahasználata (memóriaszivárgás).

  • dabadab

    titán

    válasz Teasüti #4132 üzenetére

    "Még sose alkalmaztam new-t. Mi szükségem volna rá, ha meg tudom oldani lokális változókkal?"

    Ha meg tudod oldani, akkor semmi :) Akkor van rá szükséged, ha nem tudod megoldani lokális változókkal (pl mert azt a p[] tömböt visszatérési értékként akarod használni a függvényedben).

  • doc

    nagyúr

    válasz Teasüti #4129 üzenetére

    "nem kell foglalkozni külön a felszabadításával sem"
    a masodik esetben valoban nem :)
    az elsoben kell ra hivni egy delete[]-et mikor mar nem kell

Új hozzászólás Aktív témák

Hirdetés