Signals und Objektverwaltung

Alles rund um die Programmierung mit Qt
Antworten
patrikD
Beiträge: 22
Registriert: 21. Mai 2007 14:01

Signals und Objektverwaltung

Beitrag von patrikD »

Hallo zusammen,

ich hab eine Frage zu Signals/Slots. Folgende Situation: ich habe ein Objekt welches ein Signal mit einem Objekt verschickt.
z.b.:

Code: Alles auswählen

class myClass: public QObject
{
signals:
  void newEvent(OwnClass* event);
public:
  void doSomeThing()
  {
    ....
   OwnClass* bla = new OwnClass;
   emit newEvent(bla);
  }

}
Dieses Signal kann ja an verschiedene Empfänger gehen u.a. an welche die sich in einem anderen Threadkontext befinden und das Signal dann dort nur in die EventQueue gepostet wird. Also wird das Signal emitted und ich kann nicht davon ausgehen das alle Empfänger dieses Signal bereits abgearbeitet haben. Ergo kann ich das Objekt auch nicht nach dem emit frei geben.
Ich muss das Objekt aber mit seinem Zeiger übergeben, da es nur die Basisklasse ist und die Empfänger unterschiedliche Ableitungen dieser Klasse erhalten.
Hat jemand eine Idee wie ich dem Herr werde und nicht massig Speicherlecks erzeuge?

danke
patrik
marco
Beiträge: 41
Registriert: 6. Dezember 2006 23:46
Wohnort: Niederrhein

Beitrag von marco »

Hallo,

nun, ich weiß nicht wieviele Ableitungen/Nachfahren deine Basisklasse besitzt, aber ich würde einfach ein eigenes Signal für jede Klasse erstellen:

Code: Alles auswählen

signals:
  void signal1(OwnClassA ownClass);
  void signal2(OwnClassB ownClass);
  //...
Gruß, Marco
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Re: Signals und Objektverwaltung

Beitrag von solarix »

patrikD hat geschrieben: Ich muss das Objekt aber mit seinem Zeiger übergeben, da es nur die Basisklasse ist und die Empfänger unterschiedliche Ableitungen dieser Klasse erhalten.
Hat jemand eine Idee wie ich dem Herr werde und nicht massig Speicherlecks erzeuge?
hm.. wie wärs mit einem smart-pointer.. z.B. std::auto_ptr? Solange du deine "OwnClass" nicht von QObject ableitest und in die Obhut eines Parents gibst (=new OwnClass(this)) sollte das keine Probleme bereiten..
RD1978
Beiträge: 84
Registriert: 5. Juni 2007 08:00
Wohnort: Stralsund (DDR)

Beitrag von RD1978 »

Hallo,

leite OwnClass von QObject ab und ruf nach dem emit die Methode deleteLater() auf, dann wird die Instanz durch Qt gelöscht, wenn sie die Arbeit beendet hat.
The object will be deleted when control returns to the event loop.
Bsp.:

Code: Alles auswählen

class myClass: public QObject
{
signals:
    void newEvent(OwnClass* event);
public:
    void doSomeThing()
   {
    ....
    OwnClass* bla = new OwnClass;
    emit newEvent(bla);
    bla->deleteLater();
  }
}
MfG RD1978
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

RD1978 hat geschrieben: leite OwnClass von QObject ab und ruf nach dem emit die Methode deleteLater() auf, dann wird die Instanz durch Qt gelöscht, wenn sie die Arbeit beendet hat.
Ich habe keine Erfahrung mit deleteLayer() .. die doku interpretiere ich aber so, dass das Objekt beim zurückkehren in den Eventloop (pro Thread ein Eventloop..) zerstört wird. Was aber, wenn der empfangende Thread das Event (Signal mit dem Pointer) noch gar nicht abgearbeitet hat (anderer Eventloop)?
patrikD
Beiträge: 22
Registriert: 21. Mai 2007 14:01

Beitrag von patrikD »

Hallo zusammen,

erstmal danke für das ausgiebige Feedback :)
@marco: leider hab ich zuviele unterschiedliche Events mit unterschiedlichem Datengehalt. Für jedes Event signals und slots zu definieren wäre sehr umständlich.
@rd1978: solarix hat recht, das delete later funktioniert leider nur in der selben Eventloop.
@solarix: hab noch nichts mit den smartpointern gemacht. Funktionieren die über threads hinweg? Wisst ihr wie qt Zeiger in signals/slots behandelt? Wenn ich direkt Objekte übergebe werden diese ja für jeden Empfänger kopiert, bzw. eine eigene Instanz erstellt. Wie sieht das bei den Zeigern aus?

danke
patrik
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Wisst ihr wie qt Zeiger in signals/slots behandelt? Wenn ich direkt Objekte übergebe werden diese ja für jeden Empfänger kopiert, bzw. eine eigene Instanz erstellt. Wie sieht das bei den Zeigern aus?
Hinter Signal/Slots ist nicht viel Magic.. "Call by Value" erzeugt einfach ne Kopie des Objektes, "Call by Address" kopiert den Pointer (Addresse). Bei Smartpointers wird damit also der Smartpointer (der als Value, nicht als Pointer uebergeben wird) komplett kopiert.
Trotzdem bin ich unsicher geworden.. auto_ptr scheint nicht thread-safe zu sein (code in der STL). Daher hast du folgende Optionen:
1. du schreibst ein eigener, thread-sicherer smartpointer, oder
2. du nimmst "shared_ptr" der "boost"-Library, oder
3. du uebergibst dem Signal normale Pointer, schreibst jedoch eine thread-sichere Pointerverwaltung (kleine Singleton-Klasse welche Referencen auf QObject* zaehlt und bei 0 den Speicher freigibt.. nicht viel Code..).

IMHO bietet da Qt keine Loesung (QSharedDataPointer scheint auch nicht das gewuenschte zu sein).. eigentlich schade..
patrikD
Beiträge: 22
Registriert: 21. Mai 2007 14:01

Beitrag von patrikD »

Hallo Solarix,

danke für die Antwort. Wird mir wohl noch ein wenig Kopfzerbrechen bereiten :) Werd auf jedenfall die Lösung posten, wenn ich eine finde...

lg
FaS
Beiträge: 184
Registriert: 25. Mai 2006 19:48
Kontaktdaten:

Beitrag von FaS »

Vielleicht nicht die eleganteste Lösung, aber zumindest recht einfach:
Du erstellst dir einfach (z.B. direkt in deinem MyClass-Objekt (oder woanders etwas globaler)) eine Liste aller versendeten Objekte zusammen mit der Zahl der Sendungen (quasi-Referenzzähler):

Code: Alles auswählen

QHash<OwnClass*,int> sentObjects;

// Verschicken eines Objektes:
OwnClass* bla = new OwnClass;
sentObjects[bla]++;
emit newEvent(bla);
Beim Empfänger schickst du den Zeiger einfach wieder zurück, sobald er nicht mehr benötigt wird:

Code: Alles auswählen

// Zurückschicken des Objektes:
emit canBeDeletedSignal(bla);

// Objekt mit den sentObjects
MyClass::canBeDeletedSlot(OwnClass* object)
{
  // reference counter runterzählen und Objekt ggf. löschen
  if( sentObjects[object]-- == 0 )
  {
    sentObjects.remove(object);
    delete object;
  }
}
Edit: scheint ja fast solarix' Option Nr. 3 zu entsprechen, nur etwas spezieller...

MfG,
FaS
patrikD
Beiträge: 22
Registriert: 21. Mai 2007 14:01

Beitrag von patrikD »

Hallo FaS,

danke, aber leider würde dann jedes Signal bei einer Kommunikation über Thread-Grenzen für einen doppelten Kontextwechsel sorgen und so sich negativ auf die Performance auswirken :(
Gibt es eine Möglichkeit zur Laufzeit die Anzahl der Connections zu einem Signal abzufragen?

Lg
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Ja:

Code: Alles auswählen

int QObject::receivers ( const char * signal ) const 
patrikD
Beiträge: 22
Registriert: 21. Mai 2007 14:01

Beitrag von patrikD »

Hi,

danke Upsala :) Habs mal damit versucht:
Das Objekt speichert beim emitten die Anzahl der Empfänger. Jeder Empfänger decrementiert den Wert und bei 0 wirft sich das Objekt dann weg. Is nicht sonderlich elegant, aber leichtgewichtig und threadsicher.
Hab die Sourcen in den Anhang gepackt. Für Verbesserungsvorschläge bin ich immer dankbar :)

MfG
Dateianhänge
GuardedEvent.rar
(3.32 KiB) 126-mal heruntergeladen
Antworten