Sziasztok!
Két kérdésem is lenne a dologgal kapcsolatban. Mivel nagyon szorosan összefügg a két kérdés, ezért úgy gondolom nem kell külön topic-ban indítani. Fontos figyelembe venni, hogy Qt4 programozási problémáról van szó!!! (Nem sima C++. Éppen ezért nem vagyok biztos a dolgomban.)
1) Először is van egy saját osztályom, ami nem QWidget-ből, hanem QObject-ből származik. Ennek van két dinamikus adattagja, amelyek szintén nem widget-ek, tehát nem lesz köztük olyan szülő-gyermek viszony, mint amikor egy widget tartalmaz egy másik widget-et. (pl. amikor egy QDialog objektum tartalmaz egy QLabel-t.). A kérdés, hogy meg kell-e írni ilyenkor a destruktor függvényt vagy sem?
Az én estem valahogy így néz ki:
// a header file:
#include <QObject>
class QFile;
class MyClass : public QObject {
Q_OBJECT
public:
MyClass(const QString& inFilePath, const QString& outFilePath);
void doIt();
private:
QFile* inFile;
QFile* outFile;
};
//-----------------------------------------------------------------------
// a cpp fájl:
#include "myclass.h"
MyClass::MyClass(const QString& inFilePath, const QString& outFilePath) : QObject() {
inFile = new QFile(inFilePath);
outFile = new QFile(outFilePath);
}
void MyClass::doIt() {
// csinál valamit.
}
Tehát az első kérdés, hogy meg kell-e írnom ilyen esetben a destruktor függvényt is vagy el lehet hagyni? Ami valahogy így nézne ki:
MyClass::~MyClass() {
delete inFile;
delete outFile;
}
2) Ha a fő programomban, ami egy dialógus ablak, és egy "Start" gomb hatására létrehozok egy ilyen saját osztálybeli objektumot, akkor ezt szükséges-e a használat után delete-vel törölni a memóriából?
void MyDialog::start() {
QString inPath;
QString outPath;
// egyéb részek
MyClass* mc = new MyClass(inPath, outPath);
mc->doIt();
delete mc;
}
Tehát a kérdés, hogy kell-e a "delete mc;" vagy sem?
Nekem mindkét esetben az az érzésem, hogy kellenek a kérdéses dolgok. (Az első esetben a destruktor a másodikban a delete.) Azért gondolom, mert nem widget-ekről van szó, ahol a szülő widget képes felszabadítani a gyermek widget-et.
Várom válaszotokat. Kösz!
- 1330 megtekintés
Hozzászólások
Egy pointer felszabadítása soha nem kötelező, csak memória szemét keletkezik, meg bugos kód. Meg ajánlott. Hacsak nem tettek a Qt4-be GC-t akkor én mindenképp felszabadítanám.
Mivel a konstruktorban hoztad létre ezért úgy illik, hogy a destruktorban (is) legyen felszabadítva, de akárhol máshol a kódban megfelel, csak aztán ne használd ezeket az objektumokat.
Mivel az osztály a QObjecttől származik, ezért én a destructort még megfejelném egy virtual-al is.
Szerintem.
Ja és az a delete mc is kellene.
- A hozzászóláshoz be kell jelentkezni
Természetesen én is virtuális destruktorra gondoltam, csak a header fájlt már nem írtam újra, hogy jelezzem, hogy az virtuális.
A kérdésre visszatérve: Számomra az bizonytalan, hogy a Qt4-ben egyébként nagyon nem szokás delete-t meghívni, mert a szülő widget képes felszabadítani a benne lévő objektumokat.
Ugyanakkor azt a sejtésem, mintha a MOC megoldaná ezeket a felszabadítási kérdéseket (még akkor is, ha nem widgetek-ről van szó.). De ezt nem tudom biztosan. Ezért nem tudom, hogy most szükségesek-e a kérdéses műveletek.
- A hozzászóláshoz be kell jelentkezni
C++-ban elég az ősosztály fv deklarációjában megadni a virtual kulcsszót, a gyerekek ugyanolyan fv-ei maguktól virtual-ak lesznek.
Persze ha mindenhova kiírod az sem hiba...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Ha megnézed milyen forrást generál a MOC, rögtön látod, mit csinál és mit nem. Leginkább a Signal-Slot rendszerért felel, illetve tárol némi infót az osztály típusáról.
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Ha így csinálod kell delete, de tudod úgy csinálni, hogy ne kelljen.
A konkrét esetben:
class MyClass : public QObject {
Q_OBJECT
public:
MyClass(const QString& inFilePath, const QString& outFilePath);
void doIt();
private:
QFile inFile;
QFile outFile;
};
//-----------------------------------------------------------------------
// a cpp fájl:
#include "myclass.h"
MyClass::MyClass(const QString& inFilePath, const QString& outFilePath)
: QObject(),
inFile(inFilePath),
outFile(outFilePath)
{
}
void MyClass::doIt() {
// csinál valamit.
}
void MyDialog::start() {
QString inPath;
QString outPath;
// egyéb részek
MyClass mc(inPath, outPath);
mc.doIt();
}
Azaz minek new, ha jó neked a sima változó. Qt-ben ha nem Widget-ekről van szó, akkor nem kell a new-t erőltetni. (Sőt a fő widget a main-ben is lokális szokot lenni.) Jelen esetben semmi nem indokolja a new használatát (bár ez izlés kérdése is). Sőt ebben az esetben a QObject-ből való származás is felesleges.
Egyébként parent tekintetében két dologról van itt szó:
- QObject leszármazott osztályt a parent-je felszabadítja
- QWidgetnél szintén (hiszen QObject leszármazott), plusz a parent területén jelenik meg (ezért nincs parentje a főablaknak).
Azaz az előbbi kód new-kkal Qt stílusban:
class MyClass : public QObject {
Q_OBJECT
public:
MyClass(const QString& inFilePath,
const QString& outFilePath,
QObject* parent=0);
void doIt();
private:
QFile* inFile;
QFile* outFile;
};
//-----------------------------------------------------------------------
// a cpp fájl:
#include "myclass.h"
MyClass::MyClass(const QString& inFilePath, const QString& outFilePath,QObject* parent)
: QObject(parent),
inFile( new QFile(nFilePath,this)),
outFile( new QFile(outFilePath,this))
{
}
void MyClass::doIt() {
// csinál valamit.
}
void MyDialog::start() {
QString inPath;
QString outPath;
// egyéb részek
MyClass mc(inPath, outPath);
mc.doIt();
}
Mint látható a QObjectek amiknek felszabadítását a Qt-ra bízzuk, megkapják a parent-et a konstruktorban.
A MyClass-t továbbra is helyi változóként használom, mivel logikailag is az. :)
Persze működne ez is:
// Konstruktorban:
MyClass mc=new MyClass(inPath, outPath,this);
// Valahol máshol
mc->doIt();
Ez utóbbi most csak példa, a te esetedben lokális változó kell.
Összefoglalva:
A Qt mem kezelésében nincs semmi mágia. Ha egy QObject leszármazottnak megadod a parentet, akkor az bejelentekzik a parentnél, és a parent destruktora felszabadítja majd. Ha te valamiért előbb megszűnteted delete-tel, akkor a destruktorában megint szól a parentnek, hogy vegye ki a listából. Azaz tehát biztonságosra van megírva, más dolgod nincs, mint beállítani a parenteket becsülettel...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Az attól függ, hogy a QObject létrehozásakor volt-e megadva parent paraméter, és akkor ha töröld azt, a tied is törlődni fog. De pl. a QWidget() konstruktor szülő nélkül hozza létre az objektumot, így ekkor kell a delete.
Ha pl. így hozod létre a QFile-okat:
inFile = new QFile(inFilePath, this);
outFile = new QFile(outFilePath, this)
Akkor már nem fog kelleni a törlés ~MyClass()-ban.
QObject explicit törlésére amúgy célszerűbb a QObject::deleteLater() metódusát használni, mert ha csak úgy kicsapod az objektumot egy delete-tel, akkor ha az event queue-ban van signal, amit még ez az objektum kapna, akkor abból csúnya segfault lesz.
- A hozzászóláshoz be kell jelentkezni
Mind a két választ nagyon szépen köszönöm. "tr3w" írása tényleg nagyon kielégítő volt. Azt hiszem, menni fog.
Már csak egy kérdés zavar. Ha felteszem, hogy tr3w Qt stílusú utolsó megoldását alkalmazom, akkor a start() függvény befejezésekor felszabadul-e a MyClass-hoz felhasznált memória vagy sem? El tudom képzelni, hogy nem, mivel parent=0. Tehát majd csak akkor szabadul fel a memória, ha vége a programnak? Mi történik akkor, ha még egyszer megnyomom a Start gombot és újra lefut a start() függvény? Újabb memóriaterület foglalódik le?
Nekem olyan megoldás szimpatikus, ami a start() függvény befejezésekor biztosan felszabadít minden lefoglalt memóriát.
- A hozzászóláshoz be kell jelentkezni
Ha erre gondolsz:
void MyDialog::start() {
QString inPath;
QString outPath;
// egyéb részek
MyClass mc(inPath, outPath);
mc.doIt();
}
Akkor itt a MyClass nem new-val lett lefoglalva.
Lokális változó, a blokk végén lefut a destruktora, felszabadul.
(NINCS PARENT!)
Ha new-t akarsz (amit nem ajánlok) akkor ez kell:
void MyDialog::start() {
QString inPath;
QString outPath;
// egyéb részek
MyClass* mc=new MyClass(inPath, outPath);
mc->doIt();
delete mc;
}
Nincs megadva parent, mert minek, úgyis fel akarod szabadítani a blokk végén.
Ha nincs delete, de van parent, akkor minden start nyomásra új objektum jön létre, amit majd a MyDialog persze szépen felszabadít.
Ez szigoruan véve nem memory leak, viszont baromság. :)
Egyébként a tanács az, hogy ne használj new-t ahol nem muszáj.
Két okból:
1. Lassú
2. Kimarad a delete
A 2-es Qt esetében, vagy ha "okos mutatót" (smart pointer) használsz, nem szempont.
Néha persze muszáj, pl Qt-ban a gyerek widgeteket nem adhatod meg máshogy.
(Ha tagváltozóként adnád meg, parent megadás nélkül, új ablakot nyitna neki. Ha megadod a parentet, akkor a parent destruktora meghívná rá a delete-et ami szívás mert nem new-val foglaltad.)
Illetve ven olyan megfontolás, hogy az .h fájlokban minél kevesebb másik .h fájlra hivatkozzunk. Ezzel fel lehet gyorsítani a fordítást, viszont csak mutató adattagjaid lehetnek.
Azaz:
#include "b.h"
class A
{
B b;
}
helyett
class B;
class A
{
B* b;
}
Ez nagy .h fájlok esetén érdekes, nagy projecteknél.
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Kösz!
Amikor elküldtem az előző hozzászólásomat, utána eszembe jutott, hogy lokális változóként megadva az objektumot, biztos felszabadul a memória.
Igazán nagyon köszönöm!
- A hozzászóláshoz be kell jelentkezni
A class B; kifejezes nem minosul redeklaracionak? :o
--
()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.
- A hozzászóláshoz be kell jelentkezni
Tudtommal nem, csak jelzed a fordítónak, hogy már valahol van neked egy B osztályod.
szerk: forward declaration-nek nevezik tán
--
A gyors gondolat többet ér, mint a gyors mozdulat.
- A hozzászóláshoz be kell jelentkezni