Speicherverwaltung in Qt?

96

Ich bin ziemlich neu in Qt und wundere mich über einige grundlegende Dinge mit Speicherverwaltung und dem Leben von Objekten. Wann muss ich meine Objekte löschen und / oder zerstören? Wird dies automatisch erledigt?

Welche der von mir erstellten Objekte muss ich im folgenden Beispiel löschen? Was passiert mit der Instanzvariablen, myOtherClasswenn sie myClasszerstört wird? Was passiert, wenn ich meine Objekte überhaupt nicht lösche (oder zerstöre)? Wird das ein Problem für das Gedächtnis sein?

MyClass.h

class MyClass
{

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

MyClass.cpp

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

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Wie Sie sehen, ist dies ein ziemlich einfaches Zeug für Anfänger, aber wo kann ich auf einfache Weise davon erfahren?

Martin
quelle

Antworten:

100

Wenn Sie Ihre eigene Hierarchie mit QObjects erstellen , dh alle neu erstellten QObjects mit einem übergeordneten Element initialisieren.

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

dann reicht es deletedem parent, denn der parents destruktor kümmert sich um die zerstörung child. (Dies geschieht durch Ausgabe von Signalen, sodass es auch dann sicher ist, wenn Sie es childmanuell vor dem übergeordneten Element löschen .)

Sie können das Kind auch zuerst löschen, die Reihenfolge spielt keine Rolle. Für ein Beispiel , in dem der Auftrag tut Sache hier ist die Dokumentation über das Objekt Bäume .

Wenn Sie MyClasskein Kind von sind QObject, müssen Sie die einfache C ++ - Methode verwenden, um Dinge zu tun.

Beachten Sie außerdem, dass die Eltern-Kind-Hierarchie von QObjects im Allgemeinen unabhängig von der Hierarchie des C ++ - Klassenhierarchie- / Vererbungsbaums ist. Das bedeutet, dass ein zugewiesenes Kind keine direkte Unterklasse seines Elternteils sein muss . Jede (Unterklasse von) QObjectwird ausreichen.

Es kann jedoch einige Einschränkungen geben, die von den Konstruktoren aus anderen Gründen auferlegt werden. B. in QWidget(QWidget* parent=0), wo das übergeordnete Element ein anderes sein muss QWidget, z. B. aufgrund von Sichtbarkeitsflags und weil Sie auf diese Weise ein grundlegendes Layout erstellen würden. Für das Hierarchiesystem von Qt im Allgemeinen dürfen Sie jedoch eines QObjectals übergeordnetes Element haben.

Debilski
quelle
21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Dies ist nicht der Grund, warum es sicher ist. In Qt 4.7.4 werden QObject-Kinder direkt gelöscht (via delete, siehe qobject.cpp, Zeile 1955). Der Grund, warum es sicher ist, untergeordnete Objekte zuerst zu löschen, besteht darin, dass ein QObject sein übergeordnetes Objekt anweist, es zu vergessen, wenn es gelöscht wird.
Martin Hennings
5
Ich würde hinzufügen, dass Sie sicherstellen müssen, dass die Destruktoren von Nachkommen virtuell sind, damit dies wahr ist. Wenn ClassBerbt von QObjectund ClassCerbt von ClassB, ClassCwird es von Qts Eltern-Kind-Beziehung nur dann ordnungsgemäß zerstört, wenn ClassBder Destruktor virtuell ist.
Phlucious
1
Der Link in der Antwort ist jetzt unterbrochen (nicht überraschend nach fast 4 Jahren ...), vielleicht war es so etwas wie qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW
2
Der Destruktor von @Phlucious QObject ist bereits virtuell, wodurch der Destruktor jeder Unterklasse automatisch virtuell wird.
Rubenvb
1
Wenn eine Klasse irgendwo im Vererbungsbaum einen virtuellen Destruktor hat, hat jede untergeordnete Klasse unten einen virtuellen Destruktor. Wenn es eine übergeordnete Blattklasse außerhalb einer solchen virtuellen Destruktorkette ohne einen virtuellen Destruktor gibt, können Probleme auftreten, wenn Sie einen Zeiger auf diese bestimmte Klasse löschen, wenn sich das eigentliche Objekt irgendwo weiter unten in dieser Kette befindet. Bei einer untergeordneten Klasse von QObject und dem Löschen eines QObject-Zeigers auf eine Instanz dieser untergeordneten Klasse tritt kein Problem auf, selbst wenn Sie das virtuelle Schlüsselwort in der Destruktordeklaration dieser Unterklasse vergessen.
Rubenvb
47

Ich möchte Debilskis Antwort erweitern, indem ich darauf hinweise, dass das Konzept des Eigentums in Qt sehr wichtig ist. Wenn Klasse A das Eigentum an Klasse B übernimmt, wird Klasse B gelöscht, wenn Klasse A gelöscht wird. Es gibt verschiedene Situationen, in denen ein Objekt Eigentümer eines anderen wird, nicht nur, wenn Sie ein Objekt erstellen und dessen übergeordnetes Objekt angeben.

Zum Beispiel:

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.

Ein anderes Beispiel:

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

Überprüfen Sie die Dokumentation daher häufig. Sie gibt im Allgemeinen an, ob eine Methode den Besitz eines Objekts beeinflusst.

Wie von Debilski angegeben, gelten diese Regeln NUR für Objekte, die von QObject abgeleitet sind. Wenn Ihre Klasse nicht von QObject abgeleitet ist, müssen Sie die Zerstörung selbst erledigen.

Austin
quelle
Was ist der Unterschied zwischen dem Schreiben: QPushButton * someButton = new QPushButton (); oder QPushButton someButton = neuer QPushButton oder nur QPushButton someButton;
Martin
3
Ehh, es gibt einen großen Unterschied zwischen QPushButton * someButton = new QPushButton; und QPushButton someButton;. Ersteres ordnet das Objekt auf dem Heap zu, während letzteres es dem Stapel zuweist. Es gibt keinen Unterschied zwischen QPushButton * someButton = new QPushButton (); und QPushButton someButton = new QPushButton; rufen beide den Standardkonstruktor des Objekts auf.
Austin
Ich bin sehr neu in diesem Bereich, es tut mir leid, dass ich gefragt habe, aber was ist der Unterschied zwischen "Objekt auf dem Heap zuweisen" und "Auf dem Stapel zuordnen"? Wann sollte ich Heap verwenden und wann sollte ich Stack verwenden? Vielen Dank!
Martin
3
Sie müssen sich über dynamische Zuordnungen, Objektbereich und RAII informieren. Im Fall von einfachem C ++ sollten Sie Objekte auf dem Stapel nach Möglichkeit zuweisen, da die Objekte automatisch zerstört werden, wenn ihnen der Gültigkeitsbereich ausgeht. Für Klassenmitglieder ist es aufgrund der Leistung besser, die Objekte auf dem Heap zuzuweisen. Und wann immer Sie möchten, dass ein Objekt die Ausführung einer Funktion / Methode "überlebt", sollten Sie das Objekt auf dem Heap zuweisen. Auch dies sind sehr wichtige Themen, die etwas gelesen werden müssen.
Austin
@Austin Die allgemeine Aussage, dass Sie Klassenmitglieder auf dem Heap für die Leistung zuweisen sollten, ist Ochsen. Es hängt wirklich davon ab und Sie sollten Variablen mit automatischer Speicherdauer bevorzugen, bis Sie ein Problem mit der Leistung feststellen.
Rubenvb
7

Das übergeordnete Objekt (entweder das QObject-Objekt oder seine abgeleitete Klasse) verfügt über eine Liste mit Zeigern auf seine untergeordneten Elemente (QObject / seine abgeleitete Klasse). Das übergeordnete Objekt löscht alle Objekte in seiner untergeordneten Liste, während das übergeordnete Objekt zerstört wird. Mit dieser Eigenschaft von QObject können Sie untergeordnete Objekte automatisch löschen lassen, wenn das übergeordnete Objekt gelöscht wird. Die Beziehung kann mit dem folgenden Code hergestellt werden

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.

Es gibt andere Möglichkeiten, den Speicher in Qt mithilfe von Smartpointer zu verwalten. Der folgende Artikel beschreibt verschiedene intelligente Zeiger in Qt. https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/

yesraaj
quelle
-2

Um diese Antworten zu ergänzen, würde ich Ihnen zur Überprüfung empfehlen, eine Visual Leak DetetorBibliothek für Ihre Visual C ++ - Projekte zu verwenden, einschließlich Qt-Projekten, da diese Bibliothek auf C ++ basiert, mit new, delete, free and mallocAnweisungen kompatibel ist, gut dokumentiert und einfach zu verwenden ist. Vergessen Sie nicht, dass Sie beim Erstellen Ihrer eigenen QDialogoder QWidgetgeerbten Schnittstellenklasse und dann beim Erstellen eines neuen Objekts dieser Klasse nicht vergessen, die setAttribute(Qt::WA_DeleteOnClose)Funktion Ihres Objekts auszuführen .

Khaled
quelle