QObject
-Objekten kann über den Konstruktor bzw. die Methode setParent()
ein Elternobjekt zugewiesen werden. Dadurch ändern sich zwei wichtige Dinge für das Kindobjekt.
Wenn ein QObject
-Objekt gelöscht wird, löscht es automatisch seine Kinder. Damit kann man Speicherlecks verhindern, es kann aber auch zu scheinbar unerklärlichen Abstürzen kommen, wenn man sich dessen nicht bewusst ist. Im folgenden Beispielprogramm erstellen wir eine Eltern- und Kindklasse, die beide von QObject
abgeleitet sind. Sie haben keine besondere Funktion, lediglich Ausgaben in deren Konstruktoren und Desktruktoren.
// main.cpp #include "ParentObject.h" #include "ChildObject.h" int main() { ParentObject *parentObj = new ParentObject(); // Elternobjekt anlegen ChildObject *childObj = new ChildObject( parentObj ); // Kindobjekt mit Elternobjekt anlegen delete parentObj; // Elternobjekt löschen return 0; }
// ParentObject.h #ifndef PARENTOBJECT_H #define PARENTOBJECT_H #include <QObject> #include <iostream> class ParentObject : public QObject { public: ParentObject(); ~ParentObject(); }; #endif
// ParentObject.cpp #include "ParentObject.h" ParentObject::ParentObject() { std::cout << "Elternobjekt erstellt" << std::endl; } ParentObject::~ParentObject() { cout << "Elternobjekt zerstoert" << endl; }
// ChildObject.h #ifndef CHILDOBJECT_H #define CHILDOBJECT_H #include "ParentObject.h" #include <QObject> #include <iostream> class ChildObject : public QObject { public: ChildObject( ParentObject *parent ); // Elternobjekt muss über Konstruktor festgelegt werden ~ChildObject(); }; #endif
// ChildObject.cpp #include "ChildObject.h" // Elternobjekt an Basisklassen-Konstruktor weitergeben ChildObject::ChildObject( ParentObject *parent ) : QObject( parent ) { std::cout << "Kindobjekt erstellt" << std::endl; } ChildObject::~ChildObject() { std::cout << "Kindobjekt zerstoert" << std::endl; }
Ausgabe:
Elternobjekt erstellt Kindobjekt erstellt Elternobjekt zerstoert Kindobjekt zerstoert
Da das Elternobjekt das Kindobjekt löscht kommt es hier auch nicht zu einem Speicherleck. Würden wir das Kindobjekt nach dem Elternobjekt löschen würde das zu einem Programmabsturz führen (Segmentation fault). Umgekehrt können Kinder jedoch bedenkenlos vor ihren Eltern gelöscht werden.
Das bedeutet für das obige Beispiel:
Funktioniert:
delete childObj; // Kindobjekt löschen delete parentObj; // Elternobjekt löschen
Funktioniert nicht:
delete parentObj; // Elternobjekt löschen delete childObj; // Kindobjekt löschen
Bei der Erstellung von grafischen Oberflächen haben Elternobjekte verschiedene, typabhängige Einflüsse auf Kindobjekte. Ein Layout kann z.B. direkt auf ein Widget angewandt werden, indem man das Widget als sein Parentobjekt angibt. Ein weiterer Fall wären grafische Listen bei denen Elemente durch Angabe der Liste als Parentobjekt automatisch in sie eingefügt werden. Die wichtigsten dieser Beziehungen werden im weiteren Verlauf dieses Tutorials behandelt.