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.
- 2138 megtekintés
Hozzászólások
Fölszabadul az.
Milyen módszerrel méred?
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
- nem kell kinullazni a valtozot C++-ban
- valoszinu, hogy tipusspecifikus szingletont foglal a Qt (az RTTI-hez)
----------------------
while (!sleep) sheep++;
- A hozzászóláshoz be kell jelentkezni
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
- A hozzászóláshoz be kell jelentkezni
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 hozzászóláshoz be kell jelentkezni
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
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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 hozzászóláshoz be kell jelentkezni
A sajat RTTI-jere gondoltam. Az nemileg bovebb, mint a C++ sajatja (ami raadasul platformfuggo).
----------------------
while (!sleep) sheep++;
- A hozzászóláshoz be kell jelentkezni
A sajat RTTI-jere gondoltam. Az nemileg bovebb, mint a C++ sajatja (ami raadasul platformfuggo).
----------------------
while (!sleep) sheep++;
- A hozzászóláshoz be kell jelentkezni
Ok. Így teljesen korrekt.
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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 ?
- A hozzászóláshoz be kell jelentkezni
Igen, ha a szülő-gyermek kapcsolat rendben van.
- A hozzászóláshoz be kell jelentkezni
Egész pontosan akkor, ha a destruktor virtuális.
--
http://www.naszta.hu
- A hozzászóláshoz be kell jelentkezni
Egész pontosan mit értesz ez alatt?
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
"QWidget *torlendoWidget;"
Ez bazisosztalyos cucc.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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.
- A hozzászóláshoz be kell jelentkezni
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).
- A hozzászóláshoz be kell jelentkezni
Köszi így már minden világos.
- A hozzászóláshoz be kell jelentkezni