wake-up-neo.com

Wie funktioniert delete und deleteLater in Bezug auf Signale und Slots in Qt?

Es gibt ein Objekt der Klasse QNetworkReply. Es ist ein Steckplatz (in einem anderen Objekt) mit seinem fertigen () Signal verbunden. Die Signale sind synchron (Standard). Es gibt nur einen Thread.

Irgendwann möchte ich beide Objekte loswerden. Keine Signale mehr oder irgendetwas von ihnen. Ich möchte, dass sie weg sind. Nun, ich dachte, ich werde verwenden

delete obj1; delete obj2;

Aber kann ich das wirklich? Die Spezifikationen für ~ QObject sagen:

Das Löschen eines QObjects, während anstehende Ereignisse auf die Zustellung warten, kann zu einem Absturz führen.

Was sind die anstehenden Ereignisse? Könnte das bedeuten, dass während ich mein delete aufrufe, bereits einige 'ausstehende Ereignisse' zugestellt werden und dass sie einen Absturz verursachen können und ich nicht wirklich überprüfen kann, ob es welche gibt?

Sagen wir also, ich rufe an:

obj1->deleteLater(); obj2->deleteLater();

Sicher sein.

Aber bin ich wirklich in Sicherheit? Das deleteLater fügt ein Ereignis hinzu, das in der Hauptschleife behandelt wird, wenn die Steuerung dort ankommt. Können dort bereits anstehende Ereignisse (Signale) für obj1 Oder obj2 Vorhanden sein, die darauf warten, in der Hauptschleife behandelt zu werden bevor deleteLater wird behandelt? Das wäre sehr bedauerlich. Ich möchte keinen Code schreiben, der auf den Status "Etwas gelöscht" überprüft und das eingehende Signal in allen meinen Slots ignoriert.

72
stach

Das Löschen von QObjects ist normalerweise sicher (d. H. In der normalen Praxis; es kann pathologische Fälle geben, die mir nicht bekannt sind), wenn Sie zwei Grundregeln befolgen:

  • Löschen Sie niemals ein Objekt in einem Slot oder einer Methode, die direkt oder indirekt von einem Signal (synchron, Verbindungstyp "direkt") des zu löschenden Objekts aufgerufen wird. Z.B. Wenn Sie eine Klasse Operation mit einem Signal Operation :: finished () und einem Slot Manager :: operationFinished () haben, möchten Sie das Operationsobjekt, das das Signal in diesem Slot ausgesendet hat, nicht löschen. Das Verfahren, das das Signal "finished ()" ausgibt, kann nach dem Ausgeben weiterhin auf "this" zugreifen (z. B. auf ein Mitglied zugreifen) und dann mit einem ungültigen "this" -Zeiger arbeiten.

  • Löschen Sie niemals ein Objekt in Code, der synchron aus dem Ereignishandler des Objekts aufgerufen wird. Z.B. Löschen Sie ein SomeWidget nicht in seinem SomeWidget :: fooEvent () oder in Methoden/Slots, die Sie von dort aufrufen. Das Ereignissystem bearbeitet das bereits gelöschte Objekt weiter -> Absturz.

Beides kann schwierig aufzuspüren sein, da die Backtraces normalerweise seltsam aussehen (wie Absturz beim Zugriff auf eine POD-Member-Variable), insbesondere wenn Sie komplizierte Signal-/Slot-Ketten haben, bei denen eine Löschung mehrere Schritte nach unten erfolgen kann, die ursprünglich durch ein Signal oder ein Ereignis von ausgelöst wurde das Objekt, das gelöscht wird.

Solche Fälle sind der häufigste Anwendungsfall für deleteLater (). Es stellt sicher, dass das aktuelle Ereignis abgeschlossen werden kann, bevor das Steuerelement zur Ereignisschleife zurückkehrt, die dann das Objekt löscht. Eine andere, oft bessere Möglichkeit ist es, die gesamte Aktion mithilfe einer Verbindung in der Warteschlange/QMetaObject :: invokeMethod (..., Qt :: QueuedConnection) aufzuschieben.

66
Frank Osterfeld

In den nächsten beiden Zeilen Ihrer empfohlenen Dokumente finden Sie die Antwort.

From ~ QObject ,

Das Löschen eines QObjects, während anstehende Ereignisse auf die Zustellung warten, kann zu einem Absturz führen. Sie dürfen das QObject nicht direkt löschen, wenn es in einem anderen Thread existiert als dem, der gerade ausgeführt wird. Verwenden Sie stattdessen deleteLater (), wodurch die Ereignisschleife ausgelöst wird um das Objekt zu löschen, nachdem alle anstehenden Ereignisse an das Objekt übermittelt wurden.

Es sagt uns ausdrücklich, nicht aus anderen Threads zu löschen. Da Sie eine einzelne Thread-Anwendung haben, ist es sicher, QObject zu löschen.

Wenn Sie es in einer Multithread-Umgebung löschen müssen, verwenden Sie deleteLater(), wodurch Ihr QObject gelöscht wird, sobald die Verarbeitung aller Ereignisse abgeschlossen ist.

20
liaK

Sie können eine Antwort auf Ihre Frage finden, indem Sie eine der Delta Object Rules lesen, die Folgendes besagt:

Signalsafe (SS).
Es muss sicher sein, Methoden für das Objekt, einschließlich des Destruktors, innerhalb eines Slots aufzurufen, der von einem seiner Signale aufgerufen wird.

Fragment:

QObject unterstützt im Kern das Löschen während der Signalisierung. Um dies nutzen zu können, müssen Sie lediglich sicherstellen, dass Ihr Objekt nach dem Löschen nicht versucht, auf seine eigenen Mitglieder zuzugreifen. Die meisten Qt-Objekte werden jedoch nicht auf diese Weise geschrieben, und dies ist auch nicht erforderlich. Aus diesem Grund wird empfohlen, deleteLater () immer dann aufzurufen, wenn Sie ein Objekt während eines seiner Signale löschen müssen, da die Wahrscheinlichkeit besteht, dass "delete" nur die Anwendung zum Absturz bringt.

Leider ist nicht immer klar, wann Sie "delete" oder "deleteLater ()" verwenden sollten. Das heißt, es ist nicht immer offensichtlich, dass ein Codepfad eine Signalquelle hat. Häufig verfügen Sie über einen Codeblock, der für einige Objekte, die heute sicher sind, "Löschen" verwendet. Zu einem späteren Zeitpunkt wird dieser Codeblock jedoch von einer Signalquelle aufgerufen, und Ihre Anwendung stürzt plötzlich ab. Die einzige allgemeine Lösung für dieses Problem besteht darin, deleteLater () die ganze Zeit zu verwenden, auch wenn dies auf den ersten Blick unnötig erscheint.

Im Allgemeinen betrachte ich Delta Object Rules als obligatorisch gelesen für jeden Qt-Entwickler. Es ist ein ausgezeichnetes Lesematerial.

14
Piotr Dobrogost

Soweit ich weiß, ist dies hauptsächlich ein Problem, wenn die Objekte in verschiedenen Threads vorhanden sind. Oder vielleicht, während Sie die Signale tatsächlich verarbeiten.

Andernfalls werden beim Löschen eines QObject zunächst alle Signale und Slots getrennt und alle anstehenden Ereignisse entfernt. Da würde ein Aufruf von disconnect () reichen.

2