元对象编译器(moc)是一个处理Qt c++扩展的程序。
moc工具读c++头文件,若它发现一个或更多的类声明包含这个O_OBJECT宏;它会提供这个类的包含元对象代码的c++源文件;除此之外,元对象代码还需要用于信号和槽机制,运行时类型信息和动态属性系统;
由moc生成的c++源文件必须被编译并与类的实现链接。
如果你使用qmake来创建你的makefile,构建规则会在需要的时候调用moc,所以你不需要直接使用moc。
信号槽机制
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
~MyClass();
signals:
void mySignal();
public slots:
void mySlot();
};
属性
moc还实现对象属性。Q_PROPERTY()宏声明一个对象属性,而Q_ENUMO)声明类中的枚举类型列表,以便在属性系统中使用。
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()宏声明了作为标志使用的枚举,也就是一起使用OR。另一个宏,Q_CLASSINFO(),允许你附加额外的名称/值对到类的元对象:
class MyClass : public QObject
{
Q_OBJECT
Q_CLASSINFO("Author", "Oscar Peterson")
Q_CLASSINFO("Status", "Active")
public:
MyClass(QObject *parent = 0);
~MyClass();
};
Qt自己创建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到你的源代码(替换你最喜欢的名字)变量和moc_foo.o或moc_-foo.obj到你的对象变量。
这两个例子都假设$(DEFINE)和$(INCPATH)展开为define和include传递给c++编译器的路径选项。moc需要这些文件来预处理源文件。
对于实现中的Q_OBJECT类声明(.Cpp)文件,我们建议这样一个makefile规则:
foo.o: foo.moc
foo.moc: foo.cpp
moc $(DEFINES) $(INCPATH) -i $< -o $@
这保证make会在编译foo之前运行moc.cpp。你可以
#include "foo.moc"
在foo.cpp的末尾,该文件中声明的所有类都是完全已知的。
命令行操作
moc支持的命令行参数如下:
-o<file> | 将输出写入<file>而不是标准输出。 |
-f[<file>] | 强制在输出中生成#include语句。这是扩展名以H或h开头的头文件的默认值。如果你有不遵循标准命名约定的头文件,这个选项很有用。<file>部分是可选的。 |
-i | 不要在输出中生成#include语句。这可以用于在包含一个或多个类声明的c++文件上运行moc。对象中的元对象代码应该#include .cpp文件。 |
-nw | 不要生成任何警告。(不推荐)。 |
-p[path] | 使moc将<path>/前置到生成的#include语句中的文件名。 |
-I<dir> | 添加dir到头文件的包含路径。 |
-E | 进行预处理;不要生成元对象代码。 |
-D<macro>[=<def>] | 定义宏,带有可选的定义。 |
-U<macro> | 未定义的宏 |
-M<key=value> | 向插件添加额外的元数据。如果一个类指定了Q_PLUGIN_METADATA,键值对将被添加到它的元数据中。这将在运行时为插件解析的Json对象中结束(可从QPluginLoader访问)。这个参数通常用于用构建系统解析的信息来标记静态插件。 |
@<file> | 从<file>读取额外的命令行选项。文件的每一行都被视为一个单独的选项。空行将被忽略。注意,选项文件本身不支持这个选项(即一个选项文件不能“包含”另一个文件)。 |
-h | 显示选项的使用和列表。 |
-v | 显示moc版本号。 |
-Fdir | macOS。将框架目录dir添加到要搜索头文件的目录列表的头。这些目录与-I选项指定的目录交错在一起,并按从左到右的顺序扫描(请参阅gcc的manpage)。通常,使用-F /Library/Frameworks/ |
可以显式地告诉moc不要解析头文件的部分内容。moc定义了预处理器符号Q MOC_RUN。
#ifndef Q_MOC_RUN
...
#endif
moc不能处理c++的所有内容。类模板不能有Q_OBJECT宏。
不支持!!!
class SomeTemplate<int> : public QFrame
{
Q_OBJECT
...
signals:
void mySignal(int);
};
多重继承要求QObject放到第一位置
// 正确
class SomeClass : public QObject, public OtherClass
{
...
};
提示:若OtherClass本身就继承自QObject,那么 public QObject就多余了;
//错误
class SomeClass : public OtherClass,public 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 *);
};
有时用继承和虚函数替换函数指针可能会更好。
枚举和类型defs必须完全限定信号和槽位参数
class MyClass : public QObject
{
Q_OBJECT
enum Error {
ConnectionRefused,
RemoteHostClosed,
UnknownError
};
signals:
void stateChanged(MyClass::Error error); //MyClass::Error最好不要写成Error
};
嵌套类不能有信号和槽
class A
{
public:
class B
{
Q_OBJECT
public slots: // 错误的
void b();
};
};