2009-09-17 21 views
11

Ich bin sehr verwirrt über die Verwendung von Destruktoren in Qt4 und hoffe, Sie können mir helfen.
Wenn ich eine Methode wie folgt (mit „Die“ ist eine Klasse):Destruktoren in Qt4

void Widget::create() { 
    Des *test = new Des; 
    test->show(); 
} 

Wie kann ich sicherstellen, dass dieses Widget gelöscht werden soll, nachdem es geschlossen wurde?

Und in der Klasse "Des" Ich habe diese:

Des::Des() 
{ 
    QPushButton *push = new QPushButton("neu"); 
    QHBoxLayout *layout = new QHBoxLayout; 
    layout->addWidget(push); 
    setLayout(layout); 
} 

, wo und wie habe ich * push zu löschen und * Layout? Was sollte im Destruktor Des :: ~ Des() sein?

Antwort

12

Eine weitere Option für die Verwendung von deleteLater() oder Eltern ist die Verwendung der Funktion zum Schließen von delete-on-close für Widgets. In diesem Fall löscht Qt das Widget, wenn es fertig ist, angezeigt zu werden.

Des *test = new Des; 
test->setAttribute(Qt::WA_DeleteOnClose); 
test->show(); 

Ich mag es Baum mit dem Objekt verwenden, die Qt hält, so dass ich lösche-on-close-Set für das Fenster, und alle Widgets im Fenster haben ein richtig Eltern angegeben, so dass sie alle gelöscht werden auch.

+0

das scheint sehr gut zu funktionieren. Vielen Dank. Aber wenn ich zum Beispiel 4 neue "test" -Widgets erstelle und sie wieder schließe, kostet das Erstellen eines weiteren "test" -widgets nicht mehr Speicher, aber die Anwendung verbraucht immer noch so viel Speicher wie die 4 "test" -Widgets würde noch existieren. Ist das normal? – Berschi

+1

@Berschi, ist es möglich, dass entweder Qt oder Ihr Betriebssystem einige Speicheroptimierung durchführt.Wenn das fünfte Widget, das Sie in Ihrem Kommentar erwähnen, nicht mehr Speicher verwendet, würde ich mich nicht zu sehr darum kümmern. Eine andere Möglichkeit, wenn Sie betroffen sind, ist, ein Werkzeug wie valgrind zu finden und Ihr Programm durch es laufen zu lassen. –

2

In den meisten Fällen Sie Widgets auf dem Stapel erstellen sollten:

QPushButton push("neu"); 

Auf diese Weise, sie gelöscht werden, wenn sie aus sich der Anwendungsbereich. Wenn Sie sie wirklich auf dem Heap erstellen möchten, liegt es in Ihrer Verantwortung, delete bei ihnen aufzurufen, wenn sie nicht mehr benötigt werden.

+1

Es ist ein wenig komplexer als das. QT wird in einigen Situationen die Zerstörung einiger Objekte auslösen (wenn sie bei einem Elternteil registriert sind) und somit würde die Anwendung mit einem doppelten freien Speicherplatz beendet werden, wenn sie den zugewiesenen Stapel hätten. –

+3

dribeas, ich denke, wenn ein QObject zerstört wird, wird automatisch die Registrierung von dem Elternobjekt aufgehoben. – rpg

+1

Ich benutze die Stapelzuordnung mit QObjects die ganze Zeit und es funktioniert gut. Danke für das uninformierte Downmod. –

3

This tutorial schlägt vor, dass Sie Widgets, die übergeordneten Widgets hinzugefügt wurden, nicht explizit löschen müssen. Es sagt auch, es tut nicht weh, sie auch zu löschen.

(Ich habe nicht getestet, aber ich denke, solange man sie explizit gelöscht werden, bevor die Mutter Widget gelöscht wird, dies in Ordnung sein sollte.)

+2

+1, aber der Versuch, sie zu löschen, nachdem das übergeordnete Widget gelöscht wurde, endet mit einem doppelten Löschvorgang und dem Löschen der Anwendung. –

21

Qt verwendet, was sie nennen object trees und es ist ein bisschen anders vom typischen RAII-Ansatz.

Die QObject Klasse constructor nimmt einen Zeiger auf ein Elternteil QObject. Wenn dieses Elternteil QObject zerstört wird, werden seine Kinder ebenfalls zerstört. Dies ist ein ziemlich weit verbreitetes Muster in Qt's Klassen und Sie werden bemerken, dass viele Konstruktoren einen *parent Parameter akzeptieren.

Wenn Sie sich einige der Qt example programs ansehen, werden Sie feststellen, dass sie tatsächlich die meisten Qt-Objekte auf dem Heap konstruieren und diesen Objektbaum nutzen, um mit der Zerstörung umzugehen. Ich persönlich fand diese Strategie auch nützlich, da GUI-Objekte eine besondere Lebensdauer haben können.

Qt bietet keine zusätzlichen Garantien über C++ hinaus, wenn Sie nicht QObject oder eine Unterklasse von QObject (wie QWidget) verwenden.


In Ihrem speziellen Beispiel gibt es keine Garantie, dass irgendetwas gelöscht wird.

Sie werden so etwas wie dies wollen für Des (Des vorausgesetzt, ist eine Unterklasse von QWidget):

class Des : public QWidget 
{ 
    Q_OBJECT 

public: 
    Des(QWidget* parent) 
    : QWidget(parent) 
    { 
     QPushButton* push = new QPushButton("neu"); 
     QHBoxLayout* layout = new QHBoxLayout(this); 
     layout->addWidget(push); // this re-parents push so layout 
           // is the parent of push 
     setLayout(layout); 
    } 

    ~Des() 
    { 
     // empty, since when Des is destroyed, all its children (in Qt terms) 
     // will be destroyed as well 
    } 
} 

Und würden Sie Klasse Des wie so verwenden:

int someFunction() 
{ 
    // on the heap 
    Des* test = new Des(parent); // where parent is a QWidget* 
    test->show(); 
    ... 
    // test will be destroyed when its parent is destroyed 

    // or on the stack 
    Des foo(0); 
    foo.show(); 
    ... 
    // foo will fall out of scope and get deleted 
} 
+0

ok das ist sehr hilfreich, aber ich habe eine zusätzliche Frage: Wenn ich nur dieses Widget namens "Test" SCHLIESST, bedeutet das nicht, dass es auch zerstört wird, oder? Ich muss es für mich selbst zerstören/löschen. Wie muss ich das machen? – Berschi

+2

Wenn das Widget keine saubere Lebensdauer und keine Eltern hat, mit denen es assoziiert werden kann, ist die beste Option, die Delete-on-Close-Funktionalität zu verwenden, die cjhuitt in einer anderen Antwort erwähnt. – richardwb

+0

nur für die Klarstellung, wenn wir die "Pushbutton" als Member-Variable machen, dann müssen wir die Schaltfläche manuell im widget destructor löschen oder es wird gelöscht, wenn das übergeordnete gelöscht wird .. – Naruto

5

Richardwb Antwort ist ein guter - aber der andere Ansatz ist die Verwendung der deleteLater Slot, wie folgt:

Des *test = new Des; 
test->show(); 
connect(test, SIGNAL(closed()), test, SLOT(deleteLater())); 

Offensichtlich kann das closed() Signal mit dem gewünschten Signal ersetzt werden.

+0

gibt es kein SIGNAL (closed()) für dieses Widget. SIGNALE sind: customContextMenuRequested (QPoint), destroyed() und zerstört (QObject *) – Berschi

+0

OK, nun, es war eher ein allgemeines Konzept als ein konkretes Beispiel. Vielleicht können Sie ein Signal hinzufügen, das ausgesendet wird, wenn Ihr Objekt seine Arbeit beendet hat? Es ist sowieso ein nützliches Muster, es lässt dich etwas erschaffen und vergisst es, vorausgesetzt, du speicherst keine Verweise darauf, und das ist es schließlich. – Thomi