QT对C++进行了扩展,提供了三个主要的功能:信号槽、运行时类型信息和动态属性,这三个扩展功能都是由“元对象系统”提供的。
元对象系统基于三个支撑点:
1 OObject为需要使用元对象系统有点的类提供了基类。
2 Q_OBJECT宏声明在类的私有段中,可用来启用元对象特征,如动态属性,信号槽。
3 元对象编译器(moc)为每一个QObject子类提供了实现元对象特征的必要代码。
MOC工具读取C++源代码。如果它发现一个或者多个类的声明包括了宏Q_OBJECT,它产生另一个C++源代码文件,这个文件中包含了含有宏Q_OBJCET类的元对象代码。这个新产生的源文件或者被包含值类的源文件中或者,或者更通常的是被编译和链接到类的的实现中。
元对象系统除了提供信号和槽机制(介绍元对象系统的主要原因),还提供如下特征:
1 QObject::metaObject()返回了类关联的元对象;
2 QMetaObjcet::className()在运行时返回字符串形式的类名称,不需要通过C++编译器的原始运行时类型信息支持。
3 QObject::inherits()方法返回一个对象是否是QObject类或者QObject子类的实例。
4 QObject::tr()和Qobject::trUtf8()用来完成国际化;
5 QObject::SetProperty()和QObject::property()通过名称动态的设置和获取属性;
6 QMetaObject::newInstance()构造类的新实例。
对QObject类也可以使用动态转换qobject_cast(),qobject_cast()函数与标准C++dynamic_cast()的行为很像,优点是不需要RTTI支持,并且它可以跨动态库边界。qobject_cast()尝试将它的参数转换到特定的指针类型,如果对象是正确的类型(在运行时判断)返回非0指针,如果不兼容则返回0。
看下面的例子。我们假设MyWidget继承了Qwidget并且声明了宏Q_OBJECT:
QObject *obj = new MyWidget;
变量Obj是QObject类型,实际引用到一个MyWidget对象,所以我们可以转换:
QWidget *widget = qobject_cast<QWidget *>(obj);
从QObject到QWidget的转换是成功的,因为obj实际上就是一个MyWidget,是Qwidget的子类。现在我们知道obj是一个MyWidget对象,我们可以转换到MyWidget *:
MyWidget *myWidget = qobject_cast<MyWidget *>(obj);
到MyWidget的转换也是成功的,因为qobject_cast()对待QT内建类型和自定义类型之间没有区别的。
下面的转换则是失败的:
QLabel *label = qobject_cast<QLabel *>(obj);
obj到Qlabel的转换是失败的。label也被设置为0。
这种运行时类型信息机制可以在运行时处理不同类型的对象,比如:
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宏,这样的类就没有了元对象代码,前文提到的信号槽和其他特征也就都失效了。从元对象系统的观点来看,一个不使用元代码QObject子类等效于它最近的使用元对象代码的祖先。这就意味着,QMetaObject::className() 将会返回祖先的类名而不是实际类的名字。
因此强烈建议大家,所有QObject的子类都使用Q_OBJECT宏,无论是否使用了信号槽和动态属性。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。