Verwendung des „ Meta-Object Compiler “ (moc)
Der „ Meta-Object Compiler “ (moc), moc, ist das Programm, das die C++-Erweiterungen von Qt verarbeitet.
Das Tool „ moc “ liest eine C++-Header-Datei ein. Findet es eine oder mehrere Klassendeklarationen, die das Makro „ Q_OBJECT “ enthalten, erzeugt es eine C++-Quelldatei mit dem Metaobjektcode für diese Klassen. Der Metaobjektcode wird unter anderem für den Signal- und Slot-Mechanismus, die Laufzeittypinformationen und das dynamische Eigenschaftssystem benötigt.
Die von „ moc “ generierte C++-Quelldatei muss kompiliert und mit der Implementierung der Klasse verknüpft werden.
Sowohl qmake als auch CMake generieren Makefiles mit Build-Regeln, die „ moc “ entsprechend aufrufen, sodass Sie „ moc “ nicht direkt verwenden müssen. „ qmake “ fügt diese Build-Regeln standardmäßig hinzu, während Sie bei CMake die Eigenschaft „AUTOMOC“ verwenden können, um „ moc “ automatisch zu verarbeiten. Weitere Hintergrundinformationen zu „ moc “ finden Sie unter „Warum verwendet Qt XML Moc für Signale und Slots?“.
Verwendung
moc wird typischerweise mit einer Eingabedatei verwendet, die Klassendeklarationen wie diese enthält:
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
~MyClass();
signals:
void mySignal();
public slots:
void mySlot();
};Zusätzlich zu den oben gezeigten Signalen und Slots implementiert „ moc “ auch Objekteigenschaften, wie im folgenden Beispiel gezeigt. Das Makro „ Q_PROPERTY()“ deklariert eine Objekteigenschaft, während „ Q_ENUM()“ eine Liste von Aufzählungstypen innerhalb der Klasse deklariert, die im Eigenschaftssystem verwendet werden können.
Im folgenden Beispiel deklarieren wir eine Eigenschaft vom Aufzählungstyp „ Priority “, die ebenfalls „ priority “ heißt und über eine Get-Funktion „ priority() “ sowie eine Set-Funktion „ setPriority() “ verfügt.
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Priority priority READ priority WRITE setPriority)
public:
enum Priority { High, Low, VeryHigh, VeryLow };
Q_ENUM(Priority)
MyClass(QObject *parent = 0);
~MyClass();
void setPriority(Priority priority) { m_priority = priority; }
Priority priority() const { return m_priority; }
private:
Priority m_priority;
};Das Makro ` Q_FLAG()` deklariert Enumerationen, die als Flags verwendet werden sollen, d. h. miteinander verknüpft (OR). Ein weiteres Makro, ` Q_CLASSINFO()`, ermöglicht es Ihnen, dem Metaobjekt der Klasse zusätzliche Name-Wert-Paare hinzuzufügen:
class MyClass : public QObject
{
Q_OBJECT
Q_CLASSINFO("Author", "Oscar Peterson")
Q_CLASSINFO("Status", "Active")
public:
MyClass(QObject *parent = 0);
~MyClass();
};Die von „ moc “ erzeugte Ausgabe muss genau wie der übrige C++-Code in Ihrem Programm kompiliert und verlinkt werden; andernfalls schlägt der Build in der abschließenden Verknüpfungsphase fehl. Wenn Sie ` qmake` verwenden, geschieht dies automatisch. Bei jeder Ausführung von ` qmake ` werden die Header-Dateien des Projekts analysiert und Make-Regeln generiert, um ` moc ` für jene Dateien aufzurufen, die das Makro ` Q_OBJECT ` enthalten. Ähnlich verhält es sich, wenn Sie `AUTOMOC` auf ` ON` setzen: CMake durchsucht dann die Header- und Quelldateien während des Build-Vorgangs und ruft ` moc ` entsprechend auf.
Wird die Klassendeklaration in der Datei „ myclass.h “ gefunden, sollte die Moc-Ausgabe in eine Datei namens „ moc_myclass.cpp “ geschrieben werden. Diese Datei sollte dann wie gewohnt kompiliert werden, was zu einer Objektdatei führt, z. B. „ moc_myclass.obj “ unter Windows. Dieses Objekt sollte anschließend in die Liste der Objektdateien aufgenommen werden, die in der abschließenden Bauphase des Programms miteinander verknüpft werden.
Erstellen von Make-Regeln zum Aufrufen moc
Außer bei den einfachsten Testprogrammen wird empfohlen, die Ausführung von „ moc “ zu automatisieren. Durch Hinzufügen einiger Regeln zum Makefile Ihres Programms kann „ make “ dafür sorgen, dass „moc“ bei Bedarf ausgeführt und die „moc“-Ausgabe verarbeitet wird.
Sie können CMake oder qmake verwenden, um Makefiles zu generieren, die die gesamte erforderliche Verarbeitung von „ moc “ übernehmen.
Wenn Sie Ihre Makefiles selbst erstellen möchten, finden Sie hier einige Tipps, wie Sie die Verarbeitung von „moc“ einbinden können.
Für „ Q_OBJECT “-Klassendeklarationen in Header-Dateien gibt es hier eine nützliche Makefile-Regel, falls du ausschließlich GNU make verwendest:
moc_%.cpp: %.h
moc $(DEFINES) $(INCPATH) $< -o $@Wenn du portabel programmieren möchtest, kannst du einzelne Regeln in der folgenden Form verwenden:
moc_foo.cpp: foo.h
moc $(DEFINES) $(INCPATH) $< -o $@Sie müssen außerdem daran denken, „ moc_foo.cpp “ zu Ihrer Variable „ SOURCES “ (ersetzen Sie dies durch einen Namen Ihrer Wahl) hinzuzufügen und „ moc_foo.o “ oder „ moc_foo.obj “ zu Ihrer Variable „ OBJECTS “.
Beide Beispiele gehen davon aus, dass $(DEFINES) und $(INCPATH) zu den Optionen für den Define- und Include-Pfad erweitert werden, die an den C++-Compiler übergeben werden. Diese werden von moc benötigt, um die Quelldateien vorzuverarbeiten.
Wir bevorzugen zwar die Namenskonvention „ .cpp “ für unsere C++-Quelldateien, Sie können jedoch auch jede andere Dateiendung verwenden, wie beispielsweise „ .C “, „ .cc “, „ .CC “, „ .cxx “ und „ .c++ “, wenn Sie dies bevorzugen.
Für „ Q_OBJECT “-Klassendeklarationen in Implementierungsdateien (.cpp) empfehlen wir eine Makefile-Regel wie diese:
foo.o: foo.moc
foo.moc: foo.cpp
moc $(DEFINES) $(INCPATH) -i $< -o $@Dies stellt sicher, dass „make“ das „moc“-Programm ausführt, bevor es „ foo.cpp “ kompiliert. Sie können dann
#include "foo.moc"am Ende von „ foo.cpp “ einfügen, wo alle in dieser Datei deklarierten Klassen vollständig bekannt sind.
Befehlszeilenoptionen
Hier sind die von moc unterstützten Befehlszeilenoptionen:
| Option | Beschreibung |
|---|---|
-D<macro>[=<def>] | Makro definieren, mit optionaler Definition. |
-E | Nur vorverarbeiten; keinen Meta-Objektcode generieren. |
-f[<file>] | Erzwingt die Generierung einer #include -Anweisung in der Ausgabe. Dies ist die Standardeinstellung für Header-Dateien, deren Dateiendung mit H oder h beginnt. Diese Option ist nützlich, wenn Sie Header-Dateien haben, die nicht den Standard-Namenskonventionen entsprechen. Der Teil <file> ist optional. |
-Fdir | macOS. Fügt das Framework-Verzeichnis „ dir “ an den Anfang der Liste der Verzeichnisse hinzu, in denen nach Header-Dateien gesucht wird. Diese Verzeichnisse werden mit den durch die -I-Optionen angegebenen Verzeichnissen vermischt und in der Reihenfolge von links nach rechts durchsucht (siehe die Manpage zu gcc). Verwenden Sie normalerweise -F /Library/Frameworks/ |
-h | Zeigt die Verwendung und die Liste der Optionen an. |
-i | Keine „ #include “-Anweisung in der Ausgabe generieren. Dies kann verwendet werden, um „moc“ auf eine C++-Datei anzuwenden, die eine oder mehrere Klassendeklarationen enthält. Anschließend sollten Sie den Metaobjektcode in der Datei „ .cpp “ mit „ #include “ verarbeiten. |
-I<dir> | Fügen Sie „dir“ zum Include-Pfad für Header-Dateien hinzu. |
-M<key=value> | Füge Plugins zusätzliche Metadaten hinzu. Wenn für eine Klasse „ Q_PLUGIN_METADATA “ angegeben ist, wird das Schlüssel-Wert-Paar zu ihren Metadaten hinzugefügt. Dies landet schließlich in dem JSON-Objekt, das zur Laufzeit für das Plugin aufgelöst wird (zugänglich über ` QPluginLoader`). Dieses Argument wird typischerweise verwendet, um statische Plugins mit Informationen zu versehen, die vom Build-System aufgelöst werden. |
-nw | Keine Warnungen ausgeben. (Nicht empfohlen.) |
-o<file> | Schreibt die Ausgabe an <file> statt an die Standardausgabe. |
-p<path> | Bewirkt, dass der MOC dem Dateinamen in der generierten Anweisung „ #include “ den Präfix „ <path>/ “ voranstellt. |
-U<macro> | Makro aufheben. |
@<file> | Liest zusätzliche Befehlszeilenoptionen aus der Datei „ <file> “ ein. Jede Zeile der Datei wird als einzelne Option behandelt. Leere Zeilen werden ignoriert. Beachten Sie, dass diese Option innerhalb der Optionsdatei selbst nicht unterstützt wird (d. h., eine Optionsdatei kann keine andere Datei „einbinden“). |
-v | Zeigt die Versionsnummer von moc an. |
Sie können dem moc explizit mitteilen, Teile einer Header-Datei nicht zu analysieren. „ moc “ definiert das Präprozessorsymbol „ Q_MOC_RUN “. Jeder Code, der von
#ifndef Q_MOC_RUN
...
#endif, wird vom „ moc “ übersprungen.
Diagnose
moc warnt Sie vor einer Reihe gefährlicher oder unzulässiger Konstrukte in den Klassendeklarationen von „ Q_OBJECT “.
Wenn in der abschließenden Bauphase Ihres Programms Verknüpfungsfehler auftreten, die besagen, dass „ YourClass::className() “ undefiniert ist oder dass „ YourClass “ keine vtable besitzt, ist etwas schiefgelaufen. Meistens haben Sie vergessen, den von moc generierten C++-Code zu kompilieren oder mit „ #include “ zu verknüpfen, oder (im erstgenannten Fall) diese Objektdatei in den Link-Befehl einzubinden. Wenn Sie „ qmake “ verwenden, versuchen Sie, das Programm erneut auszuführen, um Ihr Makefile zu aktualisieren. Das sollte das Problem beheben.
Build-Systeme
Einbinden von moc-Header-Dateien
qmake und CMake verhalten sich beim Einbinden von moc-Header-Dateien unterschiedlich.
Um dies anhand eines Beispiels zu veranschaulichen: Angenommen, Sie haben zwei Header-Dateien mit entsprechenden Quelldateien: a.h, a.cpp, b.h und b.cpp. Jeder Header enthält das Makro „ Q_OBJECT “:
// a.h
class A : public QObject
{
Q_OBJECT
public:
// ...
};// a.cpp
#include "a.h"
// ...
#include "moc_a.cpp"// b.h
class B : public QObject
{
Q_OBJECT
public:
// ...
};// b.cpp
#include "b.h"
// ...
#include "moc_b.cpp"Wenn Sie bei qmake die von moc generierte Datei (moc_a.cpp/moc_b.cpp) nicht einbinden, werden a.cpp, b.cpp, moc_a.cpp und moc_b.cpp separat kompiliert. Dies kann zu langsameren Kompilierungsvorgängen führen. Wenn Sie die von moc generierten Dateien einbinden, müssen nur a.cpp und b.cpp kompiliert werden, da der von moc generierte Code in diesen Dateien enthalten ist.
Wenn Sie die Dateien mit CMake nicht einbinden, wird von moc eine einzelne zusätzliche Datei generiert (nennen wir sie der Einfachheit halber „ cmake.cpp “). „ cmake.cpp “ würde sowohl „ moc_a.cpp “ als auch „ moc_b.cpp “ einbinden. Das Einbinden der von moc generierten Datei ist mit CMake weiterhin zulässig, aber nicht erforderlich.
Weitere Informationen zur moc-Unterstützung von CMake in diesem Zusammenhang finden Sie unter „Einbinden von moc-Header-Dateien in Quelldateien“.
Einschränkungen
moc Es werden nicht alle Aspekte von C++ unterstützt. Das Hauptproblem besteht darin, dass Klassentemplates nicht das Makro „ Q_OBJECT “ enthalten dürfen. Hier ein Beispiel:
class SomeTemplate<int> : public QFrame
{
Q_OBJECT
...
signals:
void mySignal(int);
};Die folgenden Konstrukte sind unzulässig. Für alle gibt es Alternativen, die unserer Meinung nach in der Regel besser sind, daher hat die Beseitigung dieser Einschränkungen für uns keine hohe Priorität.
Mehrfachvererbung erfordert, dass „QObject“ an erster Stelle steht
Wenn Sie Mehrfachvererbung verwenden, geht „ moc “ davon aus, dass die erste geerbte Klasse eine Unterklasse von „ QObject “ ist. Stellen Sie außerdem sicher, dass nur die erste geerbte Klasse eine „ QObject “ ist.
// correct
class SomeClass : public QObject, public OtherClass
{
...
};Virtuelle Vererbung mit „ QObject “ wird nicht unterstützt.
Funktionszeiger dürfen nicht als Signal- oder Slot-Parameter verwendet werden
In den meisten Fällen, in denen Sie die Verwendung von Funktionszeigern als Signal- oder Slot-Parameter in Betracht ziehen würden, halten wir Vererbung für eine bessere Alternative. Hier ist ein Beispiel für eine unzulässige Syntax:
class SomeClass : public QObject
{
Q_OBJECT
public slots:
void apply(void (*apply)(List *, void *), char *); // WRONG
};Sie können diese Einschränkung wie folgt umgehen:
typedef void (*ApplyFunction)(List *, void *);
class SomeClass : public QObject
{
Q_OBJECT
public slots:
void apply(ApplyFunction, char *);
};Manchmal ist es sogar besser, den Funktionszeiger durch Vererbung und virtuelle Funktionen zu ersetzen.
Enums und Typedefs müssen für Signal- und Slot-Parameter vollständig qualifiziert sein
Bei der Überprüfung der Signaturen seiner Argumente vergleicht ` QObject::connect()` die Datentypen wörtlich. Daher werden ` Alignment ` und ` Qt::Alignment ` als zwei unterschiedliche Typen behandelt. Um diese Einschränkung zu umgehen, stellen Sie sicher, dass Sie die Datentypen bei der Deklaration von Signalen und Slots sowie beim Herstellen von Verbindungen vollständig qualifizieren. Zum Beispiel:
class MyClass : public QObject
{
Q_OBJECT
enum Error {
ConnectionRefused,
RemoteHostClosed,
UnknownError
};
signals:
void stateChanged(MyClass::Error error);
};Verschachtelte Klassen dürfen keine Signale oder Slots haben
Hier ist ein Beispiel für die fehlerhafte Konstruktion:
class A
{
public:
class B
{
Q_OBJECT
public slots: // WRONG
void b();
};
};Rückgabetypen von Signalen/Slots dürfen keine Referenzen sein
Signale und Slots können Rückgabetypen haben, aber Signale oder Slots, die Referenzen zurückgeben, werden so behandelt, als würden sie `void` zurückgeben.
In den Abschnitten „ signals “ und „ slots “ einer Klasse dürfen nur Signale und Slots vorkommen
moc gibt eine Fehlermeldung aus, wenn Sie versuchen, andere Konstrukte als Signale und Slots in die Abschnitte „ signals “ oder „ slots “ einer Klasse einzufügen.
Siehe auch „Meta-Objekt-System“, „Signale und Slots“ sowie „Das Eigenschaftssystem von Qt“.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.