Gestione della memoria in Qt?


96

Sono abbastanza nuovo in Qt e mi chiedo alcune cose di base con la gestione della memoria e la vita degli oggetti. Quando devo eliminare e / o distruggere i miei oggetti? Tutto questo viene gestito automaticamente?

Nell'esempio seguente, quale degli oggetti che creo devo eliminare? Cosa succede alla variabile di istanza myOtherClassquando myClassviene distrutta? Cosa succede se non elimino (o distruggo) i miei oggetti? Sarà un problema per la memoria?

MyClass.h

class MyClass
{

public:
    MyClass();
    ~MyClass();
    MyOtherClass *myOtherClass;
};

MyClass.cpp

MyClass::MyClass() {
    myOtherClass = new MyOtherClass();

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Come puoi vedere, questa è roba abbastanza facile per i principianti, ma dove posso apprenderla in modo semplice?

Risposte:


100

Se costruisci la tua gerarchia con QObjects, ovvero, inizializzi tutti i nuovi messaggi creati QObjectcon un genitore,

QObject* parent = new QObject();
QObject* child = new QObject(parent);

allora è sufficiente per deletela parent, perché la parents distruttore si prenderà cura di distruggere child. (Lo fa emettendo segnali, quindi è sicuro anche quando elimini childmanualmente prima del genitore.)

Puoi anche eliminare prima il bambino, l'ordine non ha importanza. Per un esempio in cui l'ordine non importa qui è la documentazione su alberi di oggetti .

Se MyClassnon QObjectsei figlio di , dovrai usare il semplice modo C ++ di fare le cose.

Inoltre, si noti che la gerarchia padre-figlio di QObjects è generalmente indipendente dalla gerarchia della gerarchia di classi C ++ / albero di ereditarietà. Ciò significa che un figlio assegnato non deve essere una sottoclasse diretta del suo genitore . Qualsiasi (sottoclasse di) QObjectsarà sufficiente.

Tuttavia, potrebbero esserci dei vincoli imposti dai costruttori per altri motivi; come in QWidget(QWidget* parent=0), dove il genitore deve essere un altro QWidget, ad esempio a causa di flag di visibilità e perché faresti un layout di base in questo modo; ma per il sistema gerarchico di Qt in generale, puoi averne QObjectcome genitore.


21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Questo non è il motivo per cui è sicuro. In Qt 4.7.4, i figli di QObject vengono eliminati direttamente (tramite delete, vedere qobject.cpp, riga 1955). Il motivo per cui è sicuro eliminare prima gli oggetti figlio è che un QObject dice al suo genitore di dimenticarlo quando viene eliminato.
Martin Hennings

5
Aggiungo che devi assicurarti che i distruttori dei discendenti siano virtuali affinché ciò sia vero. Se ClassBeredita da QObjected ClassCeredita da ClassB, allora ClassCverrà correttamente distrutto dalla relazione genitore-figlio di Qt solo se il distruttore di Qt ClassBè virtuale.
Phlucious

1
Il collegamento nella risposta è ora interrotto (non sorprende dopo quasi 4 anni ...), forse era qualcosa del genere qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW

2
Il distruttore di @Phlucious QObject è già virtuale, il che rende automaticamente virtuale il distruttore di ogni sottoclasse.
rubenvb

1
Se una classe da qualche parte nell'albero dell'ereditarietà ha un distruttore virtuale, ogni classe figlia di seguito avrà un distruttore virtuale. Ora, se c'è una classe genitore foglia al di fuori di una tale catena di distruttori virtuali senza un distruttore virtuale, credo che tu possa avere problemi se elimini un puntatore a quella classe specifica quando l'oggetto reale si trova da qualche parte più in basso nella catena. Nel caso di una classe figlia di QObject e l'eliminazione di un puntatore QObject a un'istanza di quella classe figlia, non c'è mai un problema, anche se si dimentica la parola chiave virtuale nella dichiarazione del distruttore di quella sottoclasse.
rubenvb

47

Vorrei estendere la risposta di Debilski sottolineando che il concetto di proprietà è molto importante in Qt. Quando la classe A assume la proprietà della classe B, la classe B viene eliminata quando la classe A viene eliminata. Ci sono diverse situazioni in cui un oggetto diventa proprietario di un altro, non solo quando crei un oggetto e ne specifichi il genitore.

Per esempio:

QVBoxLayout* layout = new QVBoxLayout;
QPushButton someButton = new QPushButton; // No owner specified.
layout->addWidget(someButton); // someButton still has no owner.
QWidget* widget = new QWidget;
widget->setLayout(layout); // someButton is "re-parented".
                           // widget now owns someButton.

Un altro esempio:

QMainWindow* window = new QMainWindow;
QWidget* widget = new QWidget; //widget has no owner
window->setCentralWidget(widget); //widget is now owned by window.

Quindi, controlla spesso la documentazione, generalmente specifica se un metodo influenzerà la proprietà di un oggetto.

Come affermato da Debilski, queste regole si applicano SOLO agli oggetti che derivano da QObject. Se la tua classe non deriva da QObject, dovrai gestire tu stesso la distruzione.


Qual è la differenza tra la scrittura: QPushButton * someButton = new QPushButton (); o QPushButton someButton = new QPushButton o solo QPushButton someButton;
Martin

3
Ehh, c'è un'enorme differenza tra QPushButton * someButton = new QPushButton; e QPushButton someButton ;. Il primo allocherà l'oggetto sull'heap, mentre il secondo lo allocherà sullo stack. Non c'è differenza tra QPushButton * someButton = new QPushButton (); e QPushButton someButton = new QPushButton ;, entrambi chiameranno il costruttore predefinito dell'oggetto.
Austin

Sono molto nuovo a questo, quindi mi dispiace per averlo chiesto, ma qual è la differenza tra "allocare l'oggetto sull'heap" e "allocare sullo stack"? Quando dovrei usare l'heap e quando dovrei usare lo stack? Grazie!
Martin

3
È necessario leggere le allocazioni dinamiche, l'ambito degli oggetti e RAII. Nel caso del semplice C ++, dovresti allocare gli oggetti nello stack ogni volta che è possibile poiché gli oggetti vengono automaticamente distrutti quando escono dall'ambito. Per i membri della classe, è meglio allocare gli oggetti sull'heap a causa delle prestazioni. E ogni volta che vuoi che un oggetto "sopravviva" all'esecuzione di una funzione / metodo, dovresti allocare l'oggetto sull'heap. Ancora una volta, questi sono argomenti molto importanti che richiedono una lettura.
Austin

@Austin L'affermazione generale secondo cui dovresti allocare i membri della classe sul mucchio per le prestazioni è buoi. Dipende davvero e dovresti preferire le variabili con durata di conservazione automatica fino a quando non trovi un problema con le prestazioni.
rubenvb

7

Il padre (l'oggetto QObject o la sua classe derivata) ha un elenco di puntatori ai suoi figli (QObject / il suo derivato). Il genitore eliminerà tutti gli oggetti nel suo elenco figlio mentre il genitore viene distrutto. È possibile utilizzare questa proprietà di QObject per creare oggetti figlio da eliminare automaticamente ogni volta che il genitore viene eliminato. La relazione può essere stabilita utilizzando il seguente codice

QObject* parent = new QObject();
QObject* child = new QObject(parent);
delete parent;//all the child objects will get deleted when parent is deleted, child object which are deleted before the parent object is removed from the parent's child list so those destructor will not get called once again.

Esistono altri modi per gestire la memoria in Qt, utilizzando lo smartpointer. Il seguente articolo descrive vari puntatori intelligenti in Qt. https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/


-2

Per aggiungere a queste risposte, per la vérification, ti consiglierei di utilizzare la Visual Leak Detetorlibreria per i tuoi progetti Visual c ++, inclusi i progetti Qt poiché è basata su c ++, questa libreria è compatibile con le new, delete, free and mallocistruzioni, è ben documentata e facile da usare. Non dimenticare che quando crei la tua classe di interfaccia personale QDialogo QWidgetereditata e quindi crei un nuovo oggetto di questa classe, non dimenticare di eseguire la setAttribute(Qt::WA_DeleteOnClose)funzione del tuo oggetto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.