QT:使用元对象编译器 (moc):Using the Meta-Object Compiler (moc)

目录

用法

为调用 moc 编写 Make 规则

命令行选项

诊断程序

构建系统

包含moc 头 文件

使用限制

多重继承要求 QObject 优先

 函数指针不能是信号或槽参数

枚举和类型定义必须完全符合信号和插槽参数

嵌套类不能有信号或槽

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

只有信号和槽可以出现在类signals 和 slots 的部分中。


         元对象编译器 是处理​ Qt C++扩展的程序​。

        该工具读取C++头文件。如果找到一个或多个包含 Q_OBJECT 宏的类声明,它将生成一个包含这些类的元对象代码的C++源文件。除其他事项外,信号和槽机制、运行时类型信息和动态属性系统都需要元目标代码。

        生成的C++源文件必须编译并与类的实现链接。

        如果您使用 qmake 创建生成文件,则将包含在需要时调用 moc 的构建规则,因此您无需直接使用 moc。

用法

        moc 通常与包含如下类声明的文件一起使用:

class MyClass : public QObject
{
    Q_OBJECT

public:
    MyClass(QObject *parent = 0);
    ~MyClass();

signals:
    void mySignal();

public slots:
    void mySlot();
};

        除了上面显示的信号和槽之外,还实现了下一个示例中的对象属性。 Q_PROPERTY() 宏声明一个对象属性,而 Q_ENUM() 声明类中的枚举类型列表可在属性 system.moc 中使用

        在下面的示例中,我们声明了一个枚举类型的属性,该属性也被调用并具有一个 get 函数和一个 set 函数 .Priorityprioritypriority()setPriority()

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)
    Q_ENUMS(Priority)

public:
    enum Priority { High, Low, VeryHigh, VeryLow };

    MyClass(QObject *parent = 0);
    ~MyClass();

    void setPriority(Priority priority) { m_priority = priority; }
    Priority priority() const { return m_priority; }

private:
    Priority m_priority;
};

       Q_FLAGS()宏声明将用作标志的枚举,即“或”在一起。 另一个宏Q_CLASSINFO()允许您将其他名称/值对附加到类的元对象上:

class MyClass : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("Author", "Oscar Peterson")
    Q_CLASSINFO("Status", "Active")

public:
    MyClass(QObject *parent = 0);
    ~MyClass();
};

        产生的输出必须经过编译和链接,就像您程序中的其他 C++ 代码一样;否则,构建将在最后的链接阶段失败。如果您使用 ,这是自动完成的。无论何时运行,它都会解析项目的头文件并生成 make 规则以调用那些包含 Q_OBJECT 宏的文件。

        mocqmakeqmakemoc 如果在文件中找到类声明,则应该将 moc 输出放在名为 .moc 的文件中。然后应该像往常一样编译该文件,从而生成一个目标文件,例如在 Windows 上。然后,该对象应包含在在 program.myclass.hmoc_myclass.cppmoc_myclass.obj 的最终构建阶段链接在一起的对象文件列表中。

为调用 moc 编写 Make 规则

        对于最简单的测试程序以外的任何程序,建议您自动运行。通过向程序的makefile中添加一些规则,可以在必要时运行moc并处理moc输出。mocmake我们建议使用qmake生成文件工具来构建生成文件。此工具生成一个makefile,该文件执行所有必要的处理。如果您想自己创建makefile,这里有一些关于如何包含moc处理的提示。对于头文件中的Q_OBJECT类声明,如果只使用GNU make,以下是一个有用的makefile规则:

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

        如果您想以可移植的方式编写,可以使用以下形式的单独规则:

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

        您还必须记住将moc_foo.cpp添加到SOURCES(替换您喜欢的名称)变量,并将moc_foo.o或moc_foo.obj添加到OBJECTS变量。

        这两个示例都假定 $(DEFINES) 和 $(INCPATH) 扩展到了define 并包括了传递给C ++编译器的路径选项。 moc需要这些来预处理源文件。

        虽然我们更喜欢将C ++源文件命名为.cpp,但是如果愿意,您可以使用任何其他扩展名,例如.C,.cc,.CC,.cxx和.c ++。

        对于实现(.cpp)文件中的Q_OBJECT类声明,我们建议使用以下makefile规则:
        

foo.o: foo.moc

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

        这保证了make将在编译之前运行moc。然后可以放 #include "foo.moc" 在foo.cpp的末尾,该文件中声明的所有类都是完全已知的。

#include "foo.moc"

命令行选项

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

选项描述
-o<file>将输出写入<file>而不是标准输出。
-f[<file>]强制在输出中生成#include语句。这是扩展名以H或h开头的头文件的默认设置。如果头文件不遵循标准命名约定,则此选项很有用。 <file>部分是可选的。
-i不要在输出中生成#include语句。这可用于在包含一个或多个类声明的C ++文件上运行moc。然后,您应该在.cpp文件中#include元对象代码。
-nw不要产生任何警告。 (不建议。)
-p<path>使moc在生成的#include语句中的文件名前加<path> /。
-I<dir>将dir添加到头文件的include路径。
-E仅预处理;不生成元对象代码。
-D<macro>[=<def>]定义宏,带有可选定义。
-U<macro>取消定义宏。
-M<key=value>将额外的元数据附加到插件。如果一个类指定了 Q_PLUGIN_METADATA,键值对将被添加到它的元数据中。这将在运行时为插件解析的 Json 对象中结束(可从 QPluginLoader 访问)。此参数通常用于使用构建系统解析的信息标记静态插件。​​​
@<file>从<file>中阅读其他命令行选项。 文件的每一行都被视为一个选项。 空行将被忽略。 请注意,选项文件本身不支持此选项(即,选项文件不能“包含”另一个文件)。
-h显示用法和选项列表。
-v显示Moc的版本号。
-Fdir将框架目录添加到要搜索头文件的目录列表的头部。这些目录与 -I 选项指定的目录交错,并按从左到右的顺序扫描(参见 gcc 的手册页)。通常,使用 -F /Library/Frameworks/dir

        您可以明确告诉Moc不要解析头文件的各个部分。 moc定义了预处理程序符号Q_MOC_RUN。 被以下内容包围的任何代码被Moc跳过。

#ifndef Q_MOC_RUN
    ...
#endif

诊断程序

        moc 会在 Q_OBJECT 类声明中警告您一些危险或非法的构造。

        如果在程序的最终构建阶段遇到链接错误,说未定义YourClass :: className()或YourClass缺少vtable,则说明操作有误。 大多数情况下,您忘记编译或#include moc生成的C ++代码,或者(在前一种情况下)在link命令中包括该对象文件。 如果使用qmake,请尝试重新运行它以更新您的Makefile。 这应该可以解决问题。

构建系统

包含moc 头 文件

qmake 和 CMake 在包含 moc 头文件方面表现不同。

为了用一个示例说明这一点,假设您有两个头文件:a.h    b.h ,分别带有相应的源文件:a.cpp  b.cpp,每个头文件都有一个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"

        使用 qmake,如果不包含 moc 生成的file (/), 将单独编译。这可能会导致构建速度变慢。如果包含 moc 生成的文件,则只需要编译 a.cpp 和 b.cpp,因为 moc 生成的代码包含在这些文件中。moc_a.cpp   moc_b.cpp   a.cpp    b.cpp    moc_a.cpp    moc_b.cpp。

        使用CMake,如果不包含文件,则moc将生成一个附加文件(为了示例起见,我们称之为)。CMake仍然允许包含moc生成的文件,但不是必需的。CMake.cpp   cmake.cpp   moc_a.cpp   mac_b.cpp。

        有关 CMake 对此主题的 moc 支持的更多信息,请参阅在源文件中包含头 moc 文件

使用限制

        moc 不能处理所有的 C++。主要问题是类模板不能有 Q_OBJECT 宏。这是一个例子:

class SomeTemplate<int> : public QFrame
{
    Q_OBJECT
    ...

signals:
    void mySignal(int);
};

        以上构造是非法的。他们都有我们认为通常更好的替代方案,因此消除这些限制对我们来说不是一个高度优先事项。

多重继承要求 QObject 优先

        如果使用多重继承,假设第一个继承的类是 QObject 的子类。另外,请确保只有第一个继承的类是 QObject.moc

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

        不支持使用 QObject 进行虚拟继承。

 函数指针不能是信号或槽参数

        在大多数情况下,您会考虑使用函数指针作为信号或槽参数,我们认为继承是更好的选择。以下是非法语法的示例:

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(void (*apply)(List *, void *), char *); // WRONG
};

        您可以像这样解决此限制:

typedef void (*ApplyFunction)(List *, void *);

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(ApplyFunction, char *);
};

        有时用继承和虚函数替换函数指针可能会更好。

枚举和类型定义必须完全符合信号和插槽参数

        在检查其参数的签名时,QObject::connect() 会逐字比较数据类型。因此,Alignment 和 Qt::Alignment 被视为两种不同的类型。要解决此限制,请确保在声明信号和插槽以及建立连接时完全限定数据类型。例如

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

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

信号和槽可以有返回类型,但返回引用的信号或槽将被视为返回 void。

只有信号和槽可以出现在类signals 和 slots 的部分中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值