Verwendung des Meta-Object Compiler (moc)
Meta-Object Compiler, moc
, ist das Programm, das mit den C++-Erweiterungen von Qt arbeitet.
Das moc
Werkzeug liest eine C++ Header-Datei. Wenn es eine oder mehrere Klassendeklarationen findet, die das Makro Q_OBJECT enthalten, erzeugt es eine C++-Quelldatei, die den Meta-Objekt-Code für diese Klassen enthält. Der Meta-Objektcode wird unter anderem für den Signal- und Slot-Mechanismus, die Laufzeit-Typinformationen und das dynamische Eigenschaftssystem benötigt.
Die von moc
erzeugte C++-Quelldatei muss kompiliert und mit der Implementierung der Klasse gelinkt werden.
Sowohl qmake als auch CMake erzeugen Makefiles mit Build-Regeln, die moc
entsprechend aufrufen, so dass Sie moc
nicht direkt verwenden müssen. qmake
fügt diese Build-Regeln standardmäßig hinzu, während Sie mit CMake die AUTOMOC-Eigenschaft verwenden können, um moc
automatisch zu behandeln. Weitere Hintergrundinformationen zu moc
finden Sie unter Why Does Qt Use Moc for Signals and 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 nächsten Beispiel. Das Makro Q_PROPERTY() deklariert eine Objekteigenschaft, während Q_ENUM() eine Liste von Aufzählungstypen innerhalb der Klasse deklariert, die innerhalb des Eigenschaftssystems verwendet werden können.
Im folgenden Beispiel deklarieren wir eine Eigenschaft des Aufzählungstyps Priority
, die auch priority
genannt wird und über eine get-Funktion priority()
und 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 Aufzählungen, die als Flags verwendet, d.h. miteinander ODER-verknüpft werden sollen. Ein weiteres Makro, Q_CLASSINFO(), ermöglicht es Ihnen, zusätzliche Name/Wert-Paare an das Meta-Objekt der Klasse anzuhängen:
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 kompiliert und gelinkt werden, genau wie der andere C++-Code in Ihrem Programm; andernfalls schlägt der Build in der abschließenden Link-Phase fehl. Wenn Sie qmake
verwenden, wird dies automatisch erledigt. Immer wenn qmake
ausgeführt wird, analysiert es die Header-Dateien des Projekts und generiert Make-Regeln, um moc
für diejenigen Dateien aufzurufen, die ein Q_OBJECT Makro enthalten. Ähnlich verhält es sich, wenn AUTOMOC auf ON
gesetzt wird. CMake scannt die Header- und Quelldateien zur Build-Zeit und ruft moc
entsprechend auf.
Wenn die Klassendeklaration in der Datei myclass.h
gefunden wird, sollte die moc-Ausgabe in einer Datei namens moc_myclass.cpp
abgelegt werden. Diese Datei sollte dann wie üblich kompiliert werden, was zu einer Objektdatei führt, z. B. moc_myclass.obj
unter Windows. Dieses Objekt sollte dann in die Liste der Objektdateien aufgenommen werden, die in der abschließenden Bauphase des Programms miteinander verknüpft werden.
Schreiben von Make-Regeln zum Aufrufen moc
Für alle anderen als die einfachsten Testprogramme wird empfohlen, die Ausführung von moc
zu automatisieren. Durch Hinzufügen einiger Regeln zum Makefile Ihres Programms kann make
die Ausführung von moc bei Bedarf und die Behandlung der moc-Ausgabe übernehmen.
Sie können CMake oder qmake verwenden, um Makefiles zu erzeugen, die alle notwendigen moc
Handlungen durchführen.
Wenn Sie Ihre Makefiles selbst erstellen wollen, finden Sie hier einige Tipps, wie Sie die moc-Behandlung einbauen können.
Für Q_OBJECT Klassendeklarationen in Headerdateien gibt es hier eine nützliche Makefile-Regel, wenn Sie nur GNU make verwenden:
moc_%.cpp: %.h moc $(DEFINES) $(INCPATH) $< -o $@
Wenn Sie portabel schreiben wollen, können Sie individuelle Regeln der folgenden Form verwenden:
moc_foo.cpp: foo.h moc $(DEFINES) $(INCPATH) $< -o $@
Sie müssen auch daran denken, moc_foo.cpp
zu Ihrer SOURCES
(ersetzen Sie Ihren Lieblingsnamen) Variable hinzuzufügen und moc_foo.o
oder moc_foo.obj
zu Ihrer OBJECTS
Variable.
Beide Beispiele gehen davon aus, dass $(DEFINES)
und $(INCPATH)
zu den Optionen define und include path expandieren, die an den C++-Compiler übergeben werden. Diese werden von moc
benötigt, um die Quelldateien vorzuverarbeiten.
Während wir es vorziehen, unsere C++-Quelldateien .cpp
zu nennen, können Sie jede andere Erweiterung, wie .C
, .cc
, .CC
, .cxx
und .c++
, verwenden, wenn Sie es vorziehen.
Für Q_OBJECT Klassendeklarationen in Implementierungsdateien (.cpp
) schlagen wir eine makefile-Regel wie diese vor:
foo.o: foo.moc foo.moc: foo.cpp moc $(DEFINES) $(INCPATH) -i $< -o $@
Dies garantiert, dass make das moc ausführt, bevor es foo.cpp
kompiliert. Sie können dann
#include "foo.moc"
an das Ende von foo.cpp
setzen, wo alle in dieser Datei deklarierten Klassen vollständig bekannt sind.
Kommandozeilen-Optionen
Hier sind die Kommandozeilenoptionen, die von moc unterstützt werden:
Option | Beschreibung |
---|---|
-D<macro>[=<def>] | Makro definieren, mit optionaler Definition. |
-E | Nur vorverarbeiten; keinen Meta-Objekt-Code erzeugen. |
-f[<file>] | Erzwingt die Erzeugung einer #include -Anweisung in der Ausgabe. Dies ist der Standard für Header-Dateien, deren Erweiterung 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ügen Sie das Framework-Verzeichnis dir an den Anfang der Liste der Verzeichnisse, die nach Header-Dateien durchsucht werden sollen. Diese Verzeichnisse werden mit den Verzeichnissen verschachtelt, die mit den Optionen -I angegeben wurden, und werden in der Reihenfolge von links nach rechts durchsucht (siehe die Manpage für gcc). Normalerweise verwenden Sie -F /Library/Frameworks/ |
-h | Zeigt die Verwendung und die Liste der Optionen an. |
-i | Erzeugen Sie keine #include -Anweisung in der Ausgabe. Dies kann verwendet werden, um das moc auf eine C++-Datei anzuwenden, die eine oder mehrere Klassendeklarationen enthält. Sie sollten dann #include den Meta-Objekt-Code in der Datei .cpp . |
-I<dir> | Fügen Sie dir zum Include-Pfad für Header-Dateien hinzu. |
-M<key=value> | Fügen Sie zusätzliche Metadaten zu Plugins hinzu. Wenn eine Klasse Q_PLUGIN_METADATA angegeben hat, wird das Schlüssel-Wert-Paar zu ihren Metadaten hinzugefügt. Dies landet in dem Json-Objekt, das zur Laufzeit für das Plugin aufgelöst wird (zugänglich über QPluginLoader). Dieses Argument wird typischerweise für die Kennzeichnung von statischen Plugins mit Informationen verwendet, die vom Build-System aufgelöst werden. |
-nw | Erzeugen Sie keine Warnungen. (Nicht empfohlen.) |
-o<file> | Schreibt die Ausgabe auf <file> statt auf die Standardausgabe. |
-p<path> | Bringt moc dazu, <path>/ dem Dateinamen in der generierten #include -Anweisung voranzustellen. |
-U<macro> | Makro entdefinieren. |
@<file> | Liest zusätzliche Befehlszeilenoptionen aus <file> . Jede Zeile der Datei wird als eine 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 "einschließen"). |
-v | Die Versionsnummer von moc anzeigen. |
Sie können moc explizit anweisen, Teile einer Header-Datei nicht zu parsen. moc
definiert das Präprozessor-Symbol Q_MOC_RUN
. Jeder Code, der von
#ifndef Q_MOC_RUN ... #endif
umgeben ist, wird von moc
übersprungen.
Diagnosen
moc
warnt Sie vor einer Reihe von gefährlichen oder illegalen Konstrukten in den Deklarationen der Klasse Q_OBJECT.
Wenn Sie in der letzten Erstellungsphase Ihres Programms Verknüpfungsfehler erhalten, die besagen, dass YourClass::className()
undefiniert ist oder dass YourClass
keine vtable hat, haben Sie etwas falsch gemacht. Meistens haben Sie vergessen, den von moc erzeugten C++-Code zu kompilieren oder #include
zu verwenden, oder (im ersten Fall) die Objektdatei in den Link-Befehl aufzunehmen. Wenn Sie qmake
verwenden, versuchen Sie, es erneut auszuführen, um Ihr Makefile zu aktualisieren. Dies sollte den Zweck erfüllen.
Systeme bauen
Einbindung von Header-Moc-Dateien
qmake und CMake verhalten sich unterschiedlich, wenn es um das Einbinden von Header-Moc-Dateien geht.
Um dies anhand eines Beispiels zu verdeutlichen, nehmen Sie an, dass Sie zwei Header mit entsprechenden Quelldateien haben: a.h
, a.cpp
, b.h
, und b.cpp
. Jeder Header hat ein Q_OBJECT Makro:
// 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"
Mit qmake werden a.cpp
, b.cpp
, moc_a.cpp
und moc_b.cpp
separat kompiliert, wenn Sie die moc-generierte Datei (moc_a.cpp
/moc_b.cpp
) nicht einschließen. Dies kann zu langsameren Builds führen. Wenn Sie die moc-generierten Dateien einschließen, müssen nur a.cpp und b.cpp kompiliert werden, da der moc-generierte Code in diesen Dateien enthalten ist.
Mit CMake, wenn Sie die Dateien nicht einschließen, wird eine einzelne zusätzliche Datei von moc generiert (nennen wir sie für das Beispiel cmake.cpp
). cmake.cpp
würde sowohl moc_a.cpp
als auch moc_b.cpp
einschließen. Das Einschließen der von moc generierten Datei ist mit CMake immer noch möglich, aber nicht notwendig.
Weitere Informationen über CMakes moc-Unterstützung zu diesem Thema finden Sie unter Einbindung von Header-moc-Dateien in Quellen.
Einschränkungen
moc
beherrscht nicht ganz C++. Das Hauptproblem ist, dass Klassenvorlagen nicht das Q_OBJECT Makro haben können. Hier ist ein Beispiel:
class SomeTemplate<int> : public QFrame { Q_OBJECT ... signals: void mySignal(int); };
Die folgenden Konstrukte sind illegal. Für alle gibt es Alternativen, die wir für gewöhnlich für besser halten, so dass die Beseitigung dieser Einschränkungen für uns keine hohe Priorität hat.
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 können keine Signal- oder Slot-Parameter sein
In den meisten Fällen, in denen Sie die Verwendung von Funktionszeigern als Signal- oder Slot-Parameter in Betracht ziehen, halten wir die Vererbung für die bessere Alternative. Hier ist ein Beispiel für eine illegale 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 kann es sogar besser sein, 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 verschiedene Typen behandelt. Um diese Einschränkung zu umgehen, stellen Sie sicher, dass Sie die Datentypen bei der Deklaration von Signalen und Slots und beim Aufbau 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 können keine Signale oder Slots haben
Hier ist ein Beispiel für das beanstandete Konstrukt:
class A { public: class B { Q_OBJECT public slots: // WRONG void b(); }; };
Signal/Slot-Rückgabetypen können keine Referenzen sein
Signale und Slots können Rückgabetypen haben, aber Signale oder Slots, die Referenzen zurückgeben, werden als leere Rückgabe behandelt.
Nur Signale und Slots dürfen in den Sektionen signals
und slots
einer Klasse vorkommen.
moc
wird sich beschweren, wenn Sie versuchen, andere Konstrukte als Signale und Slots in die Abschnitte signals
oder slots
einer Klasse zu stellen.
Siehe auch Meta-Objektsystem, Signale und Slots, und Qt's Property System.
© 2025 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.