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:

OptionBeschreibung
-D<macro>[=<def>]Makro definieren, mit optionaler Definition.
-ENur 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.
-FdirmacOS. 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/
-hZeigt die Verwendung und die Liste der Optionen an.
-iErzeugen 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.
-nwErzeugen 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").
-vDie 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.