元对象系统(Meta-Object System)是Qt框架中一个非常重要的机制,它提供了一种在运行时动态获取对象信息的方法,并且能够实现对象之间的通信。
元对象系统的核心是QObject类提供的QMetaObject对象。每个QObject对象都有一个对应的QMetaObject成员,它包含了该对象的类型信息、信号和槽信息、属性信息、枚举信息等。这些信息在运行时可以被动态获取,从而实现了许多高级特性,如信号和槽、元数据反射等。
在使用元对象系统时,要在QObject的子类中添加Q_OBJECT宏来启用元对象特性。这个宏会自动生成一些元对象信息,并且会将这些信息注册到Qt的元对象系统中。当对象被实例化时,它的元对象信息也会被创建并注册到元对象系统中。
元对象编译器( Meta-Object Compile, Moc)为QObject的子类提供了实现元对象特性所必须得代码。
我们还可以使用qobject_cast来检查一个对象是否是某个特定类型或其派生类的实例。这是通过比较对象的元对象来实现的,如果元对象的名称与目标类型相同或目标类型是元对象所对应类型的基类,那么就认为这个对象是目标类型的实例。这种方法比C++中的dynamic_cast更高效,因为它只涉及元对象的比较,而不需要进行运行时类型检查。
元对象系统涉及的主要类有下面几个:
Qt提供了一个复杂的属性系统,作为一个独立于编译器和平台的库,它不依赖于任何非标准编译器功能。Qt可与其支持的每个平台上的C++编译器配合使用。属性系统是基于元对象系统实现的。下面是属性声明的通用公式:
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(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
...
signals:
void colorChanged();
void spacingChanged();
void textChanged(const QString &newText);
private:
QColor m_color;
qreal m_spacing;
QString m_text;
属性行为类似数据成员,但是可以通过元对象系统访问属性的附加特性,如属性名称,属性值等。
下面详细解释属性声明中关键字的含义:
通过元对象系统访问属性:
QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) {
QMetaProperty metaproperty = metaobject->property(i);
const char *name = metaproperty.name();
QVariant value = object->property(name);
...
}
通过QObject子类对象的setProperty函数可以设置一个不存在的属性,该属性将存储在QObject 子类对象中,而不是QMetaObject中,不能通过元对象系统访问。这类属性称为动态属性。如果绑定动态属性的数据类型是自定义类型,则需要通过Q_DECLARE_METATYPE()宏声明,这样属性值才能通过QVariant包装存储。
信号和槽是Qt框架的重要特性之一,用来在对象之间通信。元对象系统为信号槽的实现提供了底层支持。
在GUI编程中,当触发某个事件时,我们通常希望通知另一个对象。更普遍地说,我们希望所有对象之间都能够相互交流。例如,用户单击Close按钮,我们可能希望调用窗口的Close函数。其他框架使用回调来实现这种通信,回调是指向函数的指针。虽然使用这种方法的成功框架确实存在,但回调可能是不直观的,并且在确保回调参数的类型正确性方面可能会遇到问题。
在Qt中,使用信号和槽在对象间通信,当特定事件发生时,会发出一个信号。QWidget 有许多预定义的信号,我们也可以继承QWidget实现自定义UI组件,并在子类中添加我们自己的信号。槽是响应特定信号而调用的函数。QWidget有许多预定义的槽函数,但通常的做法是子类化QWidget,并添加自己的槽函数,以便处理相应的信号。
信号槽机制是类型安全的:信号的签名必须与接收插槽的签名匹配。基于字符串的SIGNAL和SLOT语法将在运行时检测类型不匹配。信号和槽是松散耦合的:发出信号的类既不知道也不关心哪个插槽接收信号。Qt的信号槽机制确保,如果将信号连接到槽函数,槽函数将在正确的时间使用信号的参数进行调用。信号槽可以采用任何类型的任意数量的参数。它们是完全类型安全的。
所有继承自QObject或其子类之一(例如,QWidget)的类都可以包含信号槽。当对象状态改变时,它们会发出信号。这就是对象所做的所有通信。它不知道也不关心是否有任何对象正在接收它发出的信号。这是真正的信息封装,并确保对象可以用作软件组件。
槽函数可以用于接收信号,但它们也是正常的成员函数。就像一个对象不知道是否有任何对象接收它的信号一样,一个槽函数也不知道是否有任何信号连接到它。这确保了可以用Qt创建真正独立的组件。
我们可以将任意数量的信号连接到单个槽函数,也可以将信号连接到任意数量的槽函数。甚至可以将一个信号直接连接到另一个信号。(无论何时发出第一个信号,都会立即发出第二个信号。)信号和槽共同构成了强大的组件编程机制。下图是信号槽的连接示例:
本篇介绍了元对象系统及其应用的部分内容,下一篇会继续这个话题,并给出一个反射的应用示例,感谢大家的支持。
以上就是本篇全部内容了,欢迎吐槽评论!