除了D指针,Qt中另一个很有意思的部分就是Q_OBJECT宏了。该宏提供了对元对象的访问,使得能够使用比如信号和槽等QObject的更多特性。元对象提供了诸如类名、属性和方法等的信息,也被称为“反射”。
通过使用QMetaObject,我们能够用如下代码显示一些类的信息:
-
QObject
obj; -
const
QMetaObject *metaObj = obj.metaObject(); -
qDebug() <<
"class name: " << metaObj->className();
-
qDebug() <<
"class info count: " <<
metaObj->classInfoCount(); -
qDebug() <<
"methods: ";
-
//
从QMetaObject::methodOffset()开始打印,使其不会显示父类的方法
-
for
(int
i = metaObj->methodOffset(); i < metaObj->methodCount(); ++i) -
qDebug() << metaObj->method(i).methodType()
<<
" " << metaObj->method(i).signature();
由于C++并没有提供对这些信息的任何支持,Qt引入了元对象编译器(moc)来完成相应的工作。moc会读取每个头文件,如果发现其中定义的类是继承自QObject,且定义了Q_OBJECT宏,便会创建一个相应的C++源代码文件(moc_*.cpp),来完成这些工作。通过代码生成的工作,Qt不仅能够获得诸如Java等语言的灵活性,还能很好的保证继承自C++的性能和可扩展性。
假设我们有如下所示的简单类:
-
class
MyObject : public
QObject -
{
-
Q_OBJECT
-
public:
-
explicit
MyObject(QObject *parent = 0); -
void
myFunc(); -
public
slots: -
void
mySlot(int
myParam); -
signals:
-
void
mySignal(int
myParam); -
};
moc会自动创建以下信息:
-
//
保存在QMetaObject::d.data指向的空间,其起始部分是一个QMetaObjectPrivate结构体
-
static
const
uint qt_meta_data_MyObject[] = { -
5,
// 版本号,其内部结构在Qt开发中有所改变
-
0,
// 类名,其值为字符串qt_meta_stringdata_MyObject的偏移量
-
//
以下值为(数量,索引)对
-
0, 0,
// 类信息
-
2, 14,
// 这里定义了两个方法,其起始索引为14(即signal部分)
-
0, 0,
// 属性
-
0, 0,
// 枚举
-
0, 0,
// 构造函数
-
0,
// 标识
-
1,
// signal数量
-
//
对于signal、slot和property,其signature和parameters为字符串qt_meta_stringdata_MyObject的偏移量
-
//
signals: signature, parameters, type, tag, flags
-
18, 10, 9, 9, 0x05,
-
//
slots: signature, parameters, type, tag, flags
-
32, 10, 9, 9, 0x0a,
-
0
// eod
-
};
-
//
保存在QMetaObject::d.stringdata指向的空间
-
static
const
char
qt_meta_stringdata_MyObject[] = { -
"MyObject/0/0myParam/0mySignal(int)/0"
-
"mySlot(int)/0"
-
};
以上信息,及其基类的相关信息,都保存在该类对应的元对象中:
-
const
QMetaObject MyObject::staticMetaObject = { -
{ &QObject::staticMetaObject,
// 指向其基类的元对象,保存在QMetaObject::d.superdata
-
qt_meta_stringdata_MyObject,
qt_meta_data_MyObject, 0 } -
};
这样,如果我们希望对QObject的对象进行类型转换,就不需使用开销较大的运算符dynamic_cast, 而能够直接使用qobject_cast。该模板函数利用了元对象系统的信息,避免了在运行时进行类型转换:
-
template
<class
T> inline
T qobject_cast(QObject *object) -
{
-
#if
!defined(QT_NO_QOBJECT_CHECK)
-
reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(object));
- #endif