QT的元对象系统(Meta-Object System)

原文在这里点击打开链接

QT的元对象系统为对象间的通信提供信号槽机制,运行时的类型信息和动态的属性系统。

元对象系统建立在以下三个基础之上:

1、QObject 类作为基类,使其它的对象能够利用元对象系统。

2、类内部私有部分声明的 Q_OBJECT 宏使对象能够使用元对象特性, 像动态属性和信号槽这些。

3、元对象编译提供了QOject 子对象需要的代码来实现元对象特性。

moc 工具读入一个C++源文件,如果碰到有类中包含了Q_OBJECT 宏, 它会产生另外一个C++源文件,在这个C++源文件中包含了这些类的元对象代码。这个产生的C++源文件或者是通过#include 包含进原C++源文件中,更加通常的做法是,编译和链接进类的实现中。

除了为对象之间的通信提供信号和槽机制以外,元对象代码提供了以下这些特性:

QObject::metaObject() 返回类相关的元对象。

QMetaObject::className() 在运行时以字符串的形式返回类的名称,而需要使用标准C++编译器的运行时类型信息(RTTI)的支持。

QObject::inherits() 这个函数用来返回类对象的类是否是QObject 继承层次树上的一个指定类的派生类。

QObject::setProperty() 和QObject::property()  用来动态获取属性的名称和通过属性的名称来设置属性。

QMetaObject::newInstance()  用来创建一个类的实例。

同样的,可以通过 qobject_cast() 来动态的进行QObject 类型之间的转换。qobject_cast() 函数的功能同C++ 标准库中的 dynamic_cast() 函数的功能类似。不过其优点是不需要RTTI 的支持,能够跨动态库的边界工作。qobject_cast() 尝试转化它的参数为尖括号中的模板参数的类型,如果指针运行时实际所指对象含有尖括号中的类型的子对象,那么转换成功,返回一个非空指针,否则返回一个空指针 0。

比如,假设MyWidget 类继承自 QWidget , 并且在QWidget 中定义有 Q_OBJECT 宏:

QObject *obj = new MyWidget;

obj 对象是个指针,类型是QObject*, 实际指向的是 MyWideget 对象,所以我们可以合适的转换成功。

QWidget *widget = qobject_cast<QWidget *>(obj);

从QObject 指针到 QWidget 指针的转化是成功的,因为实际上 obj 所指的对象是一个MyWidget 对象,MyWidget 对象内部含有 QWidget 子对象。当然知道obj 指针实际所指的对象是MyWidget ,当然也可以将obj 转换为MyWidget*


 MyWidget *myWidget = qobject_cast<MyWidget *>(obj);

转换为MyWidget 指针是成功的,因为 qobject_cast() 函数的转换对于 Qt 内置的类型和用户自定义的类型没有差别。

QLabel *label = qobject_cast<QLabel *>(obj);

上边这个转换就失败了,label 指针被置空,这就使得能够在运行时基于类型,对不同类型的对象进行不同的处理:

 if (QLabel *label = qobject_cast<QLabel *>(obj)) {
        label->setText(tr("Ping"));
    } else if (QPushButton *button = qobject_cast<QPushButton *>(obj)) {
        button->setText(tr("Pong!"));
    }

当然,也可以将QObject 对象作为基类然而不包含Q_OBJECT 宏和元对象代码,如果Q_OBJECT 没有被包含,上边描述的运行时类型信息,动态属性系统和信号槽机制都将变得不可用。就元对象系统的观点来看,QObejct 不包含元代码的子对象同该子对象包含元对象代码的一个最近的祖先之间是等价的。这就意味着,比如说,QMetaObject::className() 函数返回的字符串名称不是该对象的,而是该对象最相近的一个祖先的。

因此,我们强烈的建议所有继承自QObejct 类的派生类去包含 Q_OBJECT 宏,尽管派生类可能不会使用信号槽和动态属性系统。


另外自己的说明:

Qt功过动态属性系统来实现自省功能,是基于元对象系统实现的。所谓Qt中,所谓的属性实际上就是成员变量,是类的数据成员,只不作为属性,可以被类的元对象访问。QObject的子对象中的属性用Q_PROPERTY() 宏来定义。

下边的代码用来打印一个类的所有属性:

const QMetaObject *metaobject = this->metaObject();
    int count = metaobject->propertyCount();
    for (int i=0; i<count; ++i) {
        QMetaProperty metaproperty = metaobject->property(i);
        qDebug() << metaproperty.name();

属性的读写可以通过QObject :: property() 和 QObject :: setProperty() 进行,此时除了属性的名称,并不需要类的其他信息。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值