Qt: Eine fehlende vtable bedeutet normalerweise, dass die erste nichtlineare virtuelle Elementfunktion keine Definition hat

8

Es gibt zahlreiche Threads zu diesem Thema. Keiner scheint meiner Rechnung zu entsprechen. Ich bekomme folgende Linkfehler in meinem Code:

%Vor%

Es sollte etwas Offensichtliches sein als - eine fehlende vtable bedeutet normalerweise, dass die erste nichtlineare virtuelle Elementfunktion keine Definition hat. Ich sehe jedoch nicht, was mir fehlt:

Ich habe diese Klassenerklärung in MSFSPlugin.h :

%Vor%

Dann in MSFSPlugin.cpp , ich habe folgendes:

%Vor%

Es folgen Definitionen:

%Vor%

Kurz gesagt, ich glaube nicht, dass ich eine nichtlineare virtuelle Memberfunktionsdefinition vermisse. Ich benutze:

%Vor%

Zusätzliche Informationen:

In meinem moc_MSFSPlugin.cpp sehe ich nicht den automatisch generierten Q_OBJECT Code für die Klasse MSFSPlugin::MSFSPluginImpl , der indirekt (via QThread) von QObject abgeleitet wird. Genauer gesagt, ich sehe keinen Code, der für das Signal generiert wurde, das in dieser Klasse deklariert ist ( loadDirectoryFinished ). Könnte das das Problem sein?

EDIT 1: Der Fehler verschwindet, wenn ich Q_OBJECT aus der Deklaration von MSFSPlugin::MSFSPluginImpl auskommentiere, aber dann verliere ich die Signalfunktionalität.

EDIT 2: Ich sehe, moc arbeitet nur auf Header-Dateien. Könnte dies mit der Tatsache zusammenhängen, dass meine abgeleitete QObject-Klasse & amp; in einer CPP-Datei definiert?

    
Code Poet 11.05.2014, 17:50
quelle

2 Antworten

25

Angenommen, es handelt sich um qmake .

Die goldenen Regeln sind:

  1. Stellen Sie sicher, dass das Makro Q_OBJECT in der Definition aller QObject -abgeleiteten Klassen vorhanden ist.
  2. Stellen Sie sicher, dass Sie Ihre QObject -dedived-Klassen in Ihren Header-Dateien only deklarieren.
  3. Stellen Sie sicher, dass alle Ihre Header-Dateien in Ihrer .pro-Datei in der HEADERS= -Liste aufgeführt sind.
  4. Führen Sie qmake jedes Mal aus, wenn Sie Q_OBJECT zu einer Ihrer Klassen hinzufügen oder Ihre .pro -Datei ändern.

Erklärung

  

EDIT 1: Der Fehler verschwindet, wenn ich Q_OBJECT aus der Deklaration von MSFSPlugin :: MSFSPluginImpl auskommentiere, aber dann verliere ich die Signalfunktionalität.

Ja. Q_OBJECT wird benötigt, um Signale, Slots, Invokabeln, qobject_cast , Übersetzungen, Eigenschaften, Aufzählungen und Methoden introspection zu deklarieren.

Die goldene Regel # 1 kommt von der Tatsache, dass ohne Q_OBJECT du kannst Sachen wie qobject_cast in deiner Klasse nicht benutzen; wenn Sie (selbst indirekt) Introspektionsfunktionen verwenden, zum Beispiel um eine Objekthierarchie zu debuggen oder um alle aktiven Verbindungen zu einem Objekt abzulegen , dann haben die Objekte Ihrer Klasse das echte Klassenname angezeigt (und nicht der der Oberklasse); usw.

Q_OBJECT macht zwei Dinge:

  1. Es weist builsystems an, Aufrufe von moc hinzuzufügen, deren Aufgabe es ist, zusätzlichen Code für die Klasse zu generieren. Dieser Code bietet alle oben aufgeführten Funktionen;
  2. da es ein normales C ++ - Makro ist, wenn es kompiliert wird erweitert auf einige Deklarationen , einschließlich einiger virtueller Elemente: qt_metacall() und metaObject() . moc generiert die Implementierung für diese virtuellen Dateien.

Der Fehler, den Sie erhalten, ist das typische Symptom der Deklaration der virtuellen Dateien (weil das Makro in Ihrem Code erweitert wurde), aber moc wurde nicht ausgeführt, Sie hatten einige nicht implementierte virtuelle Dateien, die die Verknüpfung fehlschlagen lassen.

Mit gcc und GNU ld erhalten Sie einen noch kryptischeren Fehler, etwa undefined reference to vtable for ClassName . Wenn Sie solche Fehler googlen, erfahren Sie sofort, wie Sie das Problem lösen können.

  

EDIT 2: Ich sehe, dass moc nur auf Header-Dateien operiert. Könnte dies mit der Tatsache zusammenhängen, dass meine abgeleitete QObject-Klasse & amp; in einer CPP-Datei definiert?

Die Frage lautet also: Warum wurde moc nicht in einer Datei ausgeführt, die eine Definition einer Klasse mit dem Makro Q_OBJECT enthält?

Wenn wir qmake verwenden, um Ihre Makefiles zu erzeugen, dann wird qmake alle Header-Dateien durchsuchen, die in der HEADERS -Variable aufgelistet sind. Wenn ein Header eine Klassendefinition mit dem Makro Q_OBJECT enthält, gibt er auch Anweisungen aus (im Makefile), um moc über diesen Header auszuführen, kompiliert moc 's Ausgabe und verknüpft das resultierende Objekt in der Endziel.

Und wir haben die Regeln # 2, # 3, # 4 genau hier.

# 2 sagt uns, dass wir Q_OBJECT classes in die Header setzen sollen; und das liegt daran, dass HEADERS Header, nicht Quellen auflistet.

# 3 sagt uns, dass wir alle Header in die HEADERS Liste schreiben sollen. Das liegt natürlich daran, dass, wenn ein Header, der Q_OBJECT enthält, nicht in dieser Liste enthalten ist, qmake ihn nicht finden und die Regeln ausgeben wird. (Für Header, die keine QObject-Unterklassen enthalten, ist dies zwar nicht unbedingt erforderlich, aber es ist ratsam, jeden Header dort einzufügen, um keine zu vergessen.)

# 4 sagt uns, qmake jedesmal neu zu starten, wenn wir Q_OBJECT hinzufügen oder die .pro -Datei modifizieren. Der Grund für den ersten Teil der Regel ist, dass wenn qmake bereits einen Header gescannt hat und keine Q_OBJECT gefunden hat, keine Regeln im Makefile ausgegeben wurden. Aber das Hinzufügen von Q_OBJECT benötigt auch diese Regeln; Daher brauchen wir qmake , um die Header neu zu scannen, und genau das ist es, was qmake wieder tut.

Der gleiche Grund liegt vor, wenn .pro geändert wird (zum Beispiel wenn mehr Header hinzugefügt werden - vielleicht mit Q_OBJECT unter HEADERS ).

Wenn Sie ein GNU-ähnliches make verwenden, gibt qmake eine spezielle Regel aus, die make anweist, qmake erneut auszuführen und dann das Makefile neu zu starten, wenn .pro wird nach dem Makefile geändert. Aus diesem Grund müssen Sie in der Regel unter UNIX qmake nicht manuell erneut ausführen, wenn Sie Ihre .pro ändern. Wenn Sie nur make ausführen, wird auch qmake erneut ausgeführt. Aber das funktioniert nicht überall .

Ist es also unmöglich, eine Klassendefinition mit Q_OBJECT in einer .cpp -Datei zu haben?

Nein, das ist durchaus möglich , erfordert jedoch die Verwendung eines irgendwie undokumentierten Features qmake . Der Trick besteht darin, eine Zeile wie folgt hinzuzufügen:

%Vor%

am Ende der Datei foobar.cpp , Datei, die eine oder mehrere Klassendefinitionen mit Q_OBJECT enthält.

qmake findet diese spezielle Inklusion und generiert eine Regel für moc , um foobar.moc zu erstellen, die dann von .cpp mit aufgenommen und damit zusammen kompiliert wird. (Daher wird es keine zusätzlichen Regeln geben, um foobar.moc zu kompilieren oder das Ergebnis zu verknüpfen.)

    
peppe 11.05.2014, 20:02
quelle
0
  

EDIT 1: Der Fehler verschwindet, wenn ich Q_OBJECT aus der Deklaration von MSFSPlugin :: MSFSPluginImpl auskommentiere, aber dann verliere ich die Signalfunktionalität.

Ja, das Makro Q_OBJECT ist für Signale, Steckplätze, Eigenschaften usw. erforderlich.

  

EDIT 2: Ich sehe, dass moc nur auf Header-Dateien operiert. Könnte dies mit der Tatsache zusammenhängen, dass meine abgeleitete QObject-Klasse & amp; in einer CPP-Datei definiert?

Ja und nein. Ich werde es erklären ..

Normalerweise wird es beim Schreiben durch Trennung gelöst, aber es ist auch möglich, die MOC-Datei nach Ihrer Klassendefinition einzufügen, damit es funktioniert, aber Sie müssen daran denken, nicht mehr als eins einzufügen, um seltsame Konsequenzen zu vermeiden .

Daher könnten Sie in Ihrem Fall eine Datei MSFSPlugin_p.h oder MSFSPluginImpl.h für den Implementierungskopf erstellen.

Übrigens ist es eine schlechte Idee, pimpl zu schützen. Das Pimpl-Idiom bedeutet private Implementierung, nicht geschützt.

    
lpapp 11.05.2014 18:49
quelle

Tags und Links