Megoldva [Qt]memoriakezelés alapok, jól van ez így ?

 ( zolti | 2010. december 30., csütörtök - 23:29 )


void MainWindow::addNewTab(){
    QWidget *widget=new QWidget;
    ui->tabWidget->addTab(widget,"uresTab");
}
void MainWindow::closeTab(int a){
    QWidget *torlendoWidget;
// http://doc.qt.nokia.com/stable/qtabwidget.html#widget
// tabWidget->widget visszaadja a bezárandó widgetet
    torlendoWidget=ui->tabWidget->widget(a);
    ui->tabWidget->removeTab(a);
    delete torlendoWidget;
    torlendoWidget=0;
}


Van egy tabWidget és 2 függvényem melyek kezelik.
Kérdés az, hogy felszabadítottam a memóriát ?
Az alkalmazás memóriahasználata nem áll vissza a tabok megnyitása előtti szintre.

Szerkesztve:
Köszönöm mindenkinek, nagy hasznát vettem a válaszoknak.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Fölszabadul az.
Milyen módszerrel méred?

köszi.
Csak az Ubuntu alap rendszerfigyelőjével néztem. Van ott memóriatérkép is, de az túl hosszú listát adott ahhoz, hogy végigbogarásszam.
Arra gondoltam még, hogy a felszabadítás csak annyit jelent, hogy nem más programok számára, hanem saját maga részére szabadítja fel, tehát a program használhatja más célra. Ennek nem sok értelmét látom. Bár nekem teljesen új a mutatók világa.

Bevallom őszintén, én sem vagyok 100%-ig tisztában - még az alap C++-os, Qt nélküli - memóriakezeléssel sem, de ez bonyolultabb annál, mint amit az Ubuntu alap rendszerfigyelője szummáz neked. Igazából a leghasznosabb nem is azzal foglalkozni, hogy a virtuális, rezidens, és shared memóriatérépek mekkorák, hanem azzal, hogy programozói hibák miatt előfordul-e memory leak. Bár Qt esetén kicsit bajos, de a Valgrind egy elég jó eszköz arra, hogy ezeket elkerüld.

Nem tudom, a rendszerfigyelőd mit mutat, de lehet, hogy a cache-ben maradt dolgokat is levonja; meg azért elég sok minden szaladgálhat a memóriában már; elég összetett dolog egy mai Linux kernel.

Valgrind szerintem is jó cucc.

Nálam - pont nemrég csináltam egy ellenőrzést - mutat 216 byte egyértelműen eltűnt területet, libQtGui.so.4.6.3, egy nagyon egyszerű alkalmazásra, nem a saját osztályaimban... no mindegy, ettől még nem dőlök ki. :)

Ja, és de; egymás elől is elvehetik a processzek a memóriát. Mondjuk ha garantáltan nem eszik sokat a programod azalatt, amíg fut, akkor döntsd el, hogy érdekel-e... amúgy érdemes megtanulni odafigyelni erre.

- nem kell kinullazni a valtozot C++-ban
- valoszinu, hogy tipusspecifikus szingletont foglal a Qt (az RTTI-hez)
----------------------
while (!sleep) sheep++;

Asszem meg csak delete-t se kell ra hivni. A Qt megoldja. Pontosabban a destruktor.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Hát mivel a widgetet parent paraméter nélkül hozta létre, ezért a Qt nem fogja tudni kisöpörni utána a memóriaszemetet - nincs itt memory mgmt, ez továbbra is C++ marad!

Másrészt ha lett volna parentje, akkor is szükség lehet nagy adatstruktúra törlésére, erre pedig tökéletes volt a kód.

A pointer 0-ra állítása pedig véleményem szerint hasznos, mert C++-ban (pl.: linux alatt biztosan) maradnak danglig, nem nullba mutató pointerek. Ha igazán szép megoldásra vágyik, használjon QPointer<QWidget*> widget deklarációt, majd lehet ellenőrizni widget.isNull()-lal a pointer helyességét.

Egyébként meg érdemes lenne megismerkedni a deleteLater()-rel is.

A pointer 0-ra allitasanak akkor lenne ertelme, ha nem lokalis valtozorol beszelnenk. C++-ban mintha volna olyan dolog, hogy scope, namost annak a pointernek, amirol beszelunk a scope-ja vegeter pontosan a nullara allitas utan. Vagyis onnantol mar senkit nem erdekel, hogy mennyire dangling.

Egyebkent tenyleg az a hiba a peldakoddal, hogy a tabwidgetnek nincs parentje, es ezt igy nem szabad.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Természetesen én általánosságban mondtam, hogy van értelme, jó gyakorlat nullra állítani. Merthogy mvarga azt rta, hogy C++-ban nem kell, nem pedig azt, hogy blokkon belül.

AFAIK a Qt nem használ expliciten C++ RTTI-t, mindent a moc old meg, és fordítási időben készíti el a MetaObjektum-hierarchiát, és onnan kezdve kb. sztringekkel operál (lásd inherits() függvény).

Persze ez nem zárja ki, hogy static változókba (pl.: listákba) regisztráljanak be QObject-eket, de akkor gyanítom delete-kor ki is szedé őket a memóriából - jelen esetben pedig ugye nem is volt parent beállítva, tehát eleve bele sem tudott volna kerülni egy ilyen singleton gyűjteménybe.

A sajat RTTI-jere gondoltam. Az nemileg bovebb, mint a C++ sajatja (ami raadasul platformfuggo).

----------------------
while (!sleep) sheep++;

A sajat RTTI-jere gondoltam. Az nemileg bovebb, mint a C++ sajatja (ami raadasul platformfuggo).

----------------------
while (!sleep) sheep++;

Ok. Így teljesen korrekt.

Ha egy widget szülője QWidget akkor a Qt megszabadul tőle automatikusan ha a szülő törlődik, tehát így:

QWidget *torlendowidget = new QWidget(this);

Persze előfordulhat, hogy nem akarod megvárni amíg a szülő elpusztul, (pl mainwindow sokáig eléldegél), ekkor a haszontalan widgetet törölni kell. Viszont nem jó a sima delete dolog, mivel nem tudhatod, hogy közben valamilyen oknál fogva már nem e pusztult meg a widgeted. (Pl felugró ablakot legyilkolta a user). Ez esetben nem definiált, hogy mire mutat a mutató. A helyes megoldás a régebbi Qt verzióknál a QPointer használata:

QPointer < QWidget > torlendowidget = new QWidget;
...
csinálsz ezt-azt
...
delete torlendowidget;

A QPointer abban különbözik a normál pointertől, hogy automatikusan nullázodik ha az általa mutatott objektum törlésre került, míg egyébként ez nem garantált. Új Qt verzióban még egyszerűbb a dolog, azthiszem a 4.6-os verzió óta ott a QScopedPointer, ami pedig automatikusan törli az objektumot ha befejeződik az adott szakasz (scope) végrehajtása.

Itt kell a delete, mert a tabwidget parent nelkul lett letrehozva, es ugy meg nem tud szegeny megdogleni rendesen.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

addTab meghivasakor nem allitodik a parent automatikusan a tabwidget-re? mert ha igen, akkor a Qt majd gondoskodik a lefoglalt memóriaterület felszabadításáról.

--
A gyors gondolat többet ér, mint a gyors mozdulat.

Logikus lenne, de attól tartok nem:

void QTabWidget::removeTab ( int index )
Removes the tab at position index from this stack of widgets. The page widget itself is not deleted.

Leírtam a sok memória okosságot, de nem néztem meg közelebbről a problémát. A legegyszerűbb megoldás a Qt-re bízni a törlést, azaz a delete meghívása helyett a deleteLater()-t használni:

ui->tabWidget->widget(a)->deleteLater();

Ez gondoskodik arról hogy a megfelelő időben kerüljön sor a törlésre + a többszörös törlés problémakörét is kezeli.

Ha egy objektumot törlök akkor az meghívja az összes gyermekének destruktorát ?
Tehát ha törlök egy Widget-et akkor minden törölve lesz ami rajta van ?
Pl nem kell nekem törölnöm a treeWidget minden elemét (QTreeWidgetItem), mert azok is fel lesznek szabadítva a form destruktorával ?

Igen, ha a szülő-gyermek kapcsolat rendben van.

Egész pontosan akkor, ha a destruktor virtuális.
--
http://www.naszta.hu

Egész pontosan mit értesz ez alatt?

Ez hulyeseg. Akkor szamit a virtualis destruktor, ha bazisosztalyra mutato pointeren keresztul akarod felszabaditani a memoriat.

pl.

class A
{
...
};

class B : public A
{
...
};

void foo()
{
B* b = new B();
...
delete b;
}

Itt meghivodik B es A destruktora is, akar virtualis akar nemvirtualis.

Amire te utaltal, viszont kisse offtopic.

void foo()
{
A* b = new B();
...
delete b;
}

Itt nem csak az a problema, hogy csak az ososztaly destruktora hivodik meg, hanem az, hogy a delete operator viselkedese ilyen esetben nem definialt a szabvany szerint.

"QWidget *torlendoWidget;"
Ez bazisosztalyos cucc.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Ha egy osztály destruktora virtuális, akkor az összes leszármazott destruktora virtuális. Van egy tippem arra, hogy a QWidget destruktora virtuális...

Amúgy még mindig azt mondom, hogy a deleteLater() talán a legjobb megoldás ilyenre.

Ráadásul ha egy függvény virtuális egy osztályban, akkor az öröklődési fa összes alatta levő ugyanazon függvénye is virtuális lesz, függetlenül attól, hogy kiírja a virtual kulcsszót.

Elterelodott a tema a lenyegrol. Az altalad tapasztaltak nem QT, de meg csak nem is annyira C++ specifikusak. Alapbol sem a c, sem a c++ program nem adja vissza a memoriat az operacios rendszernek felszabaditas utan, csak a sajat heap-je szamara tartja fenn. Ez annyi elonnyel jar, hogy kesobbi foglalasoknak mar nem kell az operacios rendszerhez fordulni memoriaert (ami egy viszonylag koltseges rendszerhivast jelentene), tovabba az OS-nek is csak akkor adhato vissza a memoria, ha az adott lapon mar nincs tobb elo objektum.

Ez a viselkedes tobbnyire jo, de nehany alkalmazas eseteben eleg rossz megoldas. Tipikusan hosszan futo processzek, amik rovid idore kepesek sok memoriat foglalni, azok tudjak elegge kifektetni az oprendszert ettol. Ilyen esetben neked kell sajat allokatorral gondoskodni valamilyen jobb megoldasrol. A jobb megoldas lehet pl. sajat memoriateruleteket letrehozni a nagy memoriaigenyu objektumoknak (mmap) es felszabaditaskor visszaadni oket az os-nek (munmap).

Köszi így már minden világos.