The Meta-Object System
Meta-Object 提供了用于类间通信的signal/slot, 运行时类型信息、以及动态属性系统。
Meta-Object 的使用基于以下三个点:
-
QObject 提供了可以使用MetaObject 的一个公共基类。
-
在类的声明里加入Q_OBJECT 宏, 可以开启类的 MetaObject的诸如signal/slot, 动态属性等特性。
-
Meta-Object Compiler (moc)为 QObject 的子类提供了可以支持Meta-Object的代码。
Moc 会为每一个包含了Q_OBJECT宏的类创建一个代码文件, 这个文件能够编译生成obj文件并且链接到类的实现中。
除了对 signal/slot的支持, meta object 还提供了:
-
QObject::metaObject 返回类的MetaObject类
-
QMetaObject::className 在运行时返回类名字符串, 而不需要RTTI 支持。
-
QObject::inherits 在Object继承树种确定某对象是否是某个类的实例
-
QObject::property 通过名称查询和设置属性值
-
QMetaObject::newInstance 创建一个类的实例
QMetaObject 支持在QObject 上使用 qobject_cast, qobject_cast 的作用和 C++ 的 dynamic_cast 相同, 但它不需要 RTTI支持, 并且能够在不同的动态链接库之间使用。
可以在QObject的子类中不适用 Q_OBJECT 宏, 它会导致所有meta-object的特性都不被支持, 从 meta-object 来看, 一个未使用 meta-object的类与离它最近的使用meta-object 的祖先是一样的。 比如, 在这个类中使用 className, 会返回这个祖先的类名。
Meta-Object Compiler
Moc 是用于处理Qt 的c++ 扩展的程序。
Q_FLAG 宏声明可以用做flag 的enum。 Q_CLASSINFO 可以为 meta-object添加name-value 对。
Moc产生的文件和其它C++源文件一样必须被编译、链接,否则, 在链接阶段会出错。 如果使用qmake, 它会遍历项目中的头文件并为那些包含了Q_OBJECT 宏的文件添加moc规则。
一般推荐使用 自动配置的moc, 它能够通过qmake生成的makefile去控制所有的moc步骤。
如果需要自己写makefile, 可以在GNU make中如下描述:
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 和 include 路径, 并被传入到 C++ 编译器中。
Moc 的使用可以在 Qt manual 中 the meta-object system 中找到。
当然也可以使用其它后缀名, 例如 .c .cc .CC .cxx .c++等。
可以在文件中使用
#ifndef Q_MOC_RUN
...
#endif
来通知moc跳过这一段代码。
Moc 能够识别出使用 Q_OOBJECT 宏的类中的一些危险和错误。 但是如果是链接错误, 可能是 className未声明, 或者缺少虚函数表。最常见的是没有添加 moc生成的C++代码。
Moc并非支持所有 C++ 语法
-
moc不支持模板;
-
需要在多继承中把QObject写在最前面, 并且只允许第一个类是Object。
-
函数指针不能用作signal/slots的参数
-
在使用 enum 和 typedef 时, 必须使用完整的定义。 比如在signal参数中, MyClass::MyEnum 和 MyEnum 是两个不同的类型。因此在signal/slots 的声明、connect时, 必须使用完整的类型。
-
嵌套类不能使用 signal/slots
-
Signal/slots的返回值会被当作void处理, 因此无法访问。
-
在 signal/slots区域内, 只能出现信号、槽。
Qt 使用 moc 实现 signal / slot, 并对于它的原因有专门的篇幅描述。 可以参看 Qt-mannual 中 why does Qt use Moc for signals and slots ?