使用 moc (元对象编译器)

元对象编译器 moc 程序用于处理 Qt 的 C++ 扩展 .

The moc 工具读取 C++ 头文件。若找到一个或多个类声明包含 Q_OBJECT macro, it produces a C++ source file containing the meta-object code for those classes. Among other things, meta-object code is required for the signals and slots mechanism, the run-time type information, and the dynamic property system.

The C++ source file generated by moc must be compiled and linked with the implementation of the class.

Both qmake and CMake generate makefiles with build rules that will invoke moc accordingly, so you will not need to use the moc 直接。 qmake will add these build rules by default, whereas with CMake, you can use the AUTOMOC property to handle moc automatically. For more background information on moc ,见 Qt 为什么将 MOC (元对象编译器) 用于信号和槽?

用法

moc is typically used with an input file containing class declarations like this:

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass(QObject *parent = 0);
    ~MyClass();
signals:
    void mySignal();
public slots:
    void mySlot();
};
					

In addition to the signals and slots shown above, moc also implements object properties as in the next example. The Q_PROPERTY () macro declares an object property, while Q_ENUM () declares a list of enumeration types within the class to be usable inside the 特性系统 .

In the following example, we declare a property of the enumeration type 优先级 that is also called priority and has a get function priority() and a set function setPriority() .

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;
};
					

The Q_FLAG () macro declares enums that are to be used as flags, i.e. OR'd together. Another macro, Q_CLASSINFO (), allows you to attach additional name/value pairs to the class's meta-object:

class MyClass : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("Author", "Oscar Peterson")
    Q_CLASSINFO("Status", "Active")
public:
    MyClass(QObject *parent = 0);
    ~MyClass();
};
					

The output produced by moc must be compiled and linked, just like the other C++ code in your program; otherwise, the build will fail in the final link phase. If you use qmake , this is done automatically. Whenever qmake is run, it parses the project's header files and generates make rules to invoke moc for those files that contain a Q_OBJECT macro. Similarly, when setting AUTOMOC to ON , CMake will scan the header and source files at build time and invoke moc accordingly.

If the class declaration is found in the file myclass.h , the moc output should be put in a file called moc_myclass.cpp . This file should then be compiled as usual, resulting in an object file, e.g., moc_myclass.obj on Windows. This object should then be included in the list of object files that are linked together in the final building phase of the program.

编写 Make 规则为援引 moc

For anything but the simplest test programs, it is recommended that you automate running the moc . By adding some rules to your program's makefile, make can take care of running moc when necessary and handling the moc output.

可以使用 CMake or qmake to generate makefiles that does all the necessary moc 处理。

If you want to create your makefiles yourself, here are some tips on how to include moc handling.

For Q_OBJECT class declarations in header files, here is a useful makefile rule if you only use GNU make:

moc_%.cpp: %.h
        moc $(DEFINES) $(INCPATH) $< -o $@
					

If you want to write portably, you can use individual rules of the following form:

moc_foo.cpp: foo.h
        moc $(DEFINES) $(INCPATH) $< -o $@
					

You must also remember to add moc_foo.cpp to your SOURCES (substitute your favorite name) variable and moc_foo.o or moc_foo.obj to your OBJECTS 变量。

Both examples assume that $(DEFINES) and $(INCPATH) expand to the define and include path options that are passed to the C++ compiler. These are required by moc to preprocess the source files.

While we prefer to name our C++ source files .cpp , you can use any other extension, such as .C , .cc , .CC , .cxx ,和 .c++ , if you prefer.

For Q_OBJECT class declarations in implementation ( .cpp ) files, we suggest a makefile rule like this:

foo.o: foo.moc
foo.moc: foo.cpp
        moc $(DEFINES) $(INCPATH) -i $< -o $@
					

This guarantees that make will run the moc before it compiles foo.cpp . You can then put

#include "foo.moc"
					

at the end of foo.cpp , where all the classes declared in that file are fully known.

命令行选项

这里是 moc 支持的命令行选项:

选项 描述
-D<macro>[=<def>] 定义宏采用可选定义。
-E Preprocess only; do not generate meta-object code.
-f[<file>] Force the generation of an #include statement in the output. This is the default for header files whose extension starts with H or h . This option is useful if you have header files that do not follow the standard naming conventions. The <file> part is optional.
-Fdir macOS. Add the framework directory dir to the head of the list of directories to be searched for header files. These directories are interleaved with those specified by -I options and are scanned in a left-to-right order (see the manpage for gcc). Normally, use -F /Library/Frameworks/
-h 显示用法和选项列表。
-i Do not generate an #include statement in the output. This may be used to run the moc on a C++ file containing one or more class declarations. You should then #include the meta-object code in the .cpp 文件。
-I<dir> 添加 dir 添加到 Header (头) 文件的 include 路径。
-M<key=value> Append additional meta data to plugins. If a class has Q_PLUGIN_METADATA specified, the key-value pair will be added to its meta data. This will end up in the Json object that gets resolved for the plugin at run time (accessible from QPluginLoader ). This argument is typically used for tagging static plugins with information resolved by the build system.
-nw 不生成任何警告 (不推荐)。
-o<file> 写入输出到 <file> 而不是到标准输出。
-p<path> Makes the moc prepend <path>/ to the file name in the generated #include 语句。
-U<macro> Undefine 宏。
@<file> Read additional command-line options from <file> . Each line of the file is treated as a single option. Empty lines are ignored. Note that this option is not supported within the options file itself (i.e. an options file can't "include" another file).
-v 显示 moc 的版本号。

You can explicitly tell the moc not to parse parts of a header file. moc defines the preprocessor symbol Q_MOC_RUN . Any code surrounded by

#ifndef Q_MOC_RUN
    ...
#endif
					

is skipped by the moc .

诊断

moc will warn you about a number of dangerous or illegal constructs in the Q_OBJECT 类声明。

If you get linkage errors in the final building phase of your program, saying that YourClass::className() is undefined or that YourClass lacks a vtable, something has been done wrong. Most often, you have forgotten to compile or #include the moc-generated C++ code, or (in the former case) include that object file in the link command. If you use qmake , try rerunning it to update your makefile. This should do the trick.

构建系统

包括头 moc 文件

qmake and CMake behave differently with regards to including header moc files.

To illustrate this with an example, suppose that you have two headers with corresponding source files: a.h , a.cpp , b.h ,和 b.cpp . Each header has a 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"
					

With qmake, if you don't include the moc-generated file ( moc_a.cpp / moc_b.cpp ), a.cpp , b.cpp , moc_a.cpp ,和 moc_b.cpp will be compiled separately. This can result in slower builds. If you include the moc generated files, only a.cpp and b.cpp will need to be compiled, as the moc generated code is included in those files.

With CMake, if you don't include the files, an single additional file is generated by moc (let's call it cmake.cpp for the sake of the example). cmake.cpp would include both moc_a.cpp and moc_b.cpp . Including the moc-generated file is still allowed with CMake, but it's not necessary.

For more information on CMake's moc support regarding this topic, see Including header moc files in sources .

局限性

moc does not handle all of C++. The main problem is that class templates cannot have the Q_OBJECT macro. Here is an example:

class SomeTemplate<int> : public QFrame
{
    Q_OBJECT
    ...
signals:
    void mySignal(int);
};
					

The following constructs are illegal. All of them have alternatives which we think are usually better, so removing these limitations is not a high priority for us.

多重继承要求 QObject 是第一个

若正使用多继承, moc assumes that the first inherited class is a subclass of QObject . Also, be sure that only the first inherited class is a QObject .

// correct
class SomeClass : public QObject, public OtherClass
{
    ...
};
					

虚拟继承采用 QObject is not 被支持。

函数指针不可以是信号或槽参数

In most cases where you would consider using function pointers as signal or slot parameters, we think inheritance is a better alternative. Here is an example of illegal syntax:

class SomeClass : public QObject
{
    Q_OBJECT
public slots:
    void apply(void (*apply)(List *, void *), char *); // WRONG
};
					

You can work around this restriction like this:

typedef void (*ApplyFunction)(List *, void *);
class SomeClass : public QObject
{
    Q_OBJECT
public slots:
    void apply(ApplyFunction, char *);
};
					

It may sometimes be even better to replace the function pointer with inheritance and virtual functions.

枚举和类型定义必须完全合格对于信号和槽参数

当校验其自变量的签名时, QObject::connect () 会逐字比较数据类型。因此, Alignment and Qt::Alignment are treated as two distinct types. To work around this limitation, make sure to fully qualify the data types when declaring signals and slots, and when establishing connections. For example:

class MyClass : public QObject
{
    Q_OBJECT
    enum Error {
        ConnectionRefused,
        RemoteHostClosed,
        UnknownError
    };
signals:
    void stateChanged(MyClass::Error error);
};
					

嵌套类不能有信号或槽

这里是令人不快构造的范例:

class A
{
public:
    class B
    {
        Q_OBJECT
    public slots:   // WRONG
        void b();
    };
};
					

信号/槽返回类型不能是引用

Signals and slots can have return types, but signals or slots returning references will be treated as returning void.

仅信号和槽可以出现在 signals and slots 类区间

moc will complain if you try to put other constructs in the signals or slots sections of a class than signals and slots.

另请参阅 元对象系统 , 信号和槽 ,和 Qt 的特性系统 .