QMetaObject::invokeMethod()
型别内省(type intropection):运行期间检查对象型别
基于内省机制,可以列出对象的方法和属性列表,并且能够获取有关对象的所有信息,如参数类型。如果没有内省机制,QtScript和 QML是难以实现的。
由于C++是静态类型语言,有关类的信息只在编译期被使用,编译后就不再保留,因此程序运行时无法获取类的信息。这时就需要使用「运行期类型信息」,即RTTI(Run-Time Type Information):程序运行时保存对象类型信息。
C++标准定义了dynamic_cast和typeid两个关键字用于支持RTTI机制。
GrandFather *p; Son *son = dynamic_cast(p)
https://zhuanlan.zhihu.com/p/66425414
https://zhuanlan.zhihu.com/p/61303678
使用该系统的基类QObject所创建的派生类对象,可以在运行期获取该对象的类名、父类名、枚举类型以及有哪些成员变量、有哪些成员函数等信息。基于这些信息,Qt实现了强大的信号槽机制。
Qt中的元对象系统全称Meta Object System,是一个基于标准C++的扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。元对象系统基于QObject类、Q_OBJECT宏、元对象编译器MOC实现。
Qt元对象系统的强大在于“即使编译器不支持RTTI,我们也能动态获取类型信息”。
QMetaObject::className() // 返回类的名称
qobject_cast()
相比dynamic_cast()强制转换安全得多,而且速度更快。因此,对于QObject派生类之间的转换,推荐使用qobject_cast()。
QObject *obj = new QWidget();
QWidget *widget = qobject_cast<Qwidget *>(obj);
元对象系统除了提供信号槽机制在对象间进行通讯的功能,还提供了如下功能:
QObject::metaObject()
方法
QMetaObject::className()
方法
QObject::inherits()
方法
QObject::tr()
and QObject::trUtf8()
QObject::setProperty()
and QObject::property()
qobject_cast()
方法在QObject类之间提供动态转换,qobject_cast()方法的功能类似于标准C++的dynamic_cast(),但qobject_cast()不需要RTTI的支持。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。
定义在每一个类的私有数据段,用来启用元对象功能,比如动态属性、信号和槽。
在一个QObject类或者其派生类中,如果没有声明Q_OBJECT宏,那么类的metaobject对象不会被生成,类实例调用metaObject()返回的就是其父类的metaobject对象,导致的后果是从类的实例获得的元数据其实是父类的数据。因此类所定义和声明的信号和槽都不能使用,所以,任何从QObject继承出来的类,无论是否定义声明了信号、槽和属性,都应该声明Q_OBJECT 宏。(如果 A 继承了 QObject 并且定义了 Q_OBJECT,B 继承了 A 但没有定义 Q_OBJECT,C 继承了 B,则 C 的 QMetaObject::className() 函数将返回 A,而不是本身的名字。)
在 qobjectdefs.h 里面,找到 Q_OBJECT 宏的定义:
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
Q_OBJECT_GETSTATICMETAOBJECT \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private:
在调用moc后生成的 moc_xxx.cpp文件中,包含了上述object中的属性和函数。
virtual const QMetaObject *metaObject() const;
函数:返回 QMetaObject 元对象类的实例。通过它,你就获得了 Qt 类的反射的能力:获取本对象的类型之类,而这一切,都不需要 C++ 编译器的 RTTI 支持。C++对象间的交互一般使用回调函数来实现。
使用回调函数有个弊端,当某个对象被多个对象通信时,需要一个容器来存放多个对象的回调函数。维护这个容器使得代码编写效率低、扩展性弱。
Qt属性系统是独立于编译器和平台的。
https://blog.csdn.net/u010168781/article/details/86562801
在继承QObject的类中,使用宏Q_PROPERTY()来注册属性。
Q_PROPERTY()
宏定义编译期的静态属性,使用setProperty()
函数动态添加属性。
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
Q_PROPERTY(
Priority priority
READ priority
WRITE setPriority
NOTIFY priorityChanged)
RESET:属性设置为默认值
可选,该RESET函数必须返回void并且不带参数。
NOTIFY:属性的值更改信号
可选,NOTIFY 后面跟该类中已经定义的一个信号函数,只要该属性的值发生更改,就会发出该信号。这个信号函数必须采用零个或一个参数,该参数必须与属性的类型相同。
REVISION int
可选,版本信息(通常用于QML)。
DESIGNABLE bool
可选,表示属性是否能在GUI设计工具的属性编辑器中可见(例如,Qt Designer)。大多数属性是DESIGNABLE(默认为true)。
SCRIPTABLE bool
可选,SCRIPTABLE属性表示脚本引擎是否应该可以访问此属性(默认为true)
STORED bool
可选,该属性是单独存在还是从其他值中获取的。大部分是true,一个反例是QWidget::minimumWidth()的值从QWidget::minimumSize()中获取,因此它的STORED为false。
USER bool
可选,表示是否可以被用户所编辑。
CONSTANT
可选,CONSTANT表明属性值是常量,不可更改,因此不能有WRITE方法或NOTIFY信号。对于给定的对象实例,常量属性的READ方法每次调用时都必须返回相同的值。对于对象的不同实例,该常数值可以是不同的。
FINAL
可选,FINAL表示属性不会被派生类覆盖,在某些情况下,这可用于性能优化。
Qt核心剖析: moc