元对象系统
对象树
Qt提供了一种机制,能够自动、有效的组织和管理继承自QObject的Qt对象,这种机制就是对象树。
Qt对象树在用户界面编程上是非常有用的。它能够帮助程序员减轻内存泄露的压力。
比如说当应用程序创建了一个具有父窗口部件的对象时,该对象将被加入父窗口部件的孩子列表。当应用程序销毁父窗口部件时,其下的孩子列表中的对象将被一一删除。这让我们在编程时,能够将主要精力放在系统的业务上,提高编程效率,同时也保证了系统的稳健性。
QDialog *dlg = new QDialog(0);
QPushButton *btn = new QPushButton(dlg);
QTread* p = new xx;
delete dlg;元对象系统
Qt 的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
元对象系统由以下三个基础组成:
- QObject 类是所有使用元对象系统的类的基类。
- 在一个类的private部分声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
- MOC(元对象编译器)为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。构建项目时,MOC 工具读取 C++ 源文件,当它发现类的定义里有 Q_OBJECT 宏时,它就会为这个类生成另外一个包含有元对象支持代码的 C++ 源文件,这个生成的源文件连同类的实现文件一起被编译和连接。
Q_OBJECT
Q_OBJECT是Qt实现元编译系统的一个关键宏,这个宏展开后,里边包含了很多Qt自动生成的代码,包括了变量定义、函数声明等等。
示例:
- 变量
- static const qt_meta_stringdata_completerTst_t qt_meta_stringdata_completerTst:存储函数列表
- static const uint qt_meta_data_completerTst:类文件描述- Q_OBJECT展开后的函数声明
以下5个函数都是使用Q_OBJECT宏自动生成的
- void xxx::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
- const QMetaObject xxx::staticMetaObject
- const QMetaObject *xxx::metaObject()
- void *xxx::qt_metacast(const char *_clname)
- int xxx::qt_metacall(QMetaObject::Call _c, int _id, void **_a)为了更好的理解这5个函数,我们首先需要引入一个Qt元对象,也就是QMetaObject,这个类里边存储了父类的源对象、我们当前类描述、函数描述和qt_static_metacall函数地址。
qt_static_metacall
根据函数索引进行调用槽函数,这个回调中,信号和槽都是可以被回调的,自动生成代码如下
if (_c == QMetaObject::InvokeMetaMethod) {
completerTst *_t = static_cast<completerTst*>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0:_t->lanuch(); break;
case 1: _t->test(); break;
default: ;
}
}一个信号声明,但是却也可以被回调,信号是可以当槽函数一样使用的。
b、staticMetaObject 构造一个QMetaObject对象,传入当前moc文件的动态信息;
c、metaObject 返回当前QMetaObject,一般而言,虚函数 metaObject() 仅返回类的 staticMetaObject对象。
qt_metacast 是否可以进行类型转换,被QObject::inherits直接调用,用于判断是否是继承自某个类。判断时,需要传入父类的字符串名称。
qt_metacall 调用函数回调,内部还是调用了qt_static_metacall 函数,该函数被异步处理信号时调用,或者Qt规定的有一定格式的槽函数(on_xxx_clicked())触发,异步调用代码如下所示
void QMetaCallEvent::placeMetaCall(QObject *object)
{
if (slotObj_) {
slotObj_->call(object, args_);
} else if (callFunction_ && method_offset_ <= object->metaObject()->methodOffset()) {
callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
} else {
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
}
}除了信号与槽机制外,元对象还提供如下一些功能:
QObject::metaObject() 函数返回类关联的元对象,元对象类 QMetaObject 包含了访问元对象的一些接口函数,例如 QMetaObject::className() 函数可在运行时返回类的名称字符串。
QObject *obj = new QPushButton;
obj->metaObject()->className (); //返回"QPushButton"QMetaObject::newInstance() 函数创建类的一个新的实例。
QObject::inherits(const char *className) 函数判断一个对象实例是否是名称为 className 的类或 QObject 的子类的实例。例如:
QObject *timer = new QTimer; // QTimer 是 QObject 的子类
timer->inherits ("QTimer"); // 返回 true
timer->inherits ("QObject"); // 返回 true
timer->inherits ("QAbstractButton");//返回 false,不是 QAbstractButton 的子类QObject::tr()和QObject::trUtf8()函数可翻译字符串,用于多语言界面设计QObject::setProperty()和QObject::property()函数用于通过属性名称动态设置和获取属性值。
对于 QObject 及其子类,还可以使用 qobject_cast() 函数进行动态投射(dynamic cast)。 例如,假设 QMyWidget 是 QWidget 的子类并且在类定义中声明了 Q_OBJECT 宏。创建实例使用下面的语句: QObject *obj = new QMyWidget;
成功示例:
QWidget *widget = qobject_cast<QWidget*>(obj);
QMyWidget *myWidget = qobject_cast<QMyWidget*>(obj);失败示例:
QLabel *label = qobject_cast<QLabel*>(obj);
这样投射是失败的,返回指针 label 为 NULL,因为 QMyWidget 不是 QLabel 的子类。
使用动态投射,使得程序可以在运行时对不同的对象做不同的处理。属性系统
Qt 提供一个 Q_PROPERTY() 宏可以定义属性,它也是基于元对象系统实现的。 在 QObject 的子类中,用宏 Q_PROPERTY() 定义属性,其使用格式如下: Q_PROPERTY 宏定义一个返回值类型为 type,名称为 name 的属性,用 READ、WRITE 关键字定义属性的读取、写入函数,还有其他的一些关键字定义属性的一些操作特性。属性的类型可以是 QVariant 支持的任何类型,也可以用户自定义类型。
Q_PROPERTY 宏定义属性的一些主要关键字的意义如下:
- READ : required, 指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
- WRITE: optional,指定一个设定属性值的函数,只读属性没有 WRITE 设置。
- MEMBER: optional,指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
- RESET: optional,用于指定一个设置属性缺省值的函数。
- NOTIFY: optional,用于设置一个信号,当属性值变化时发射此信号。
- DESIGNABLE: optional,表示属性是否在 Qt Designer 里可见,缺省为 true。
- CONSTANT: optional,表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
- FINAL : optional, 表示所定义的属性不能被子类重载。
QWidget 类定义属性的一些例子如下:
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)属性的使用
QPushButton *button = new QPushButton;
QObject*object = button;
object->setProperty("flat", true);
bool isFlat= object->property ("flat");动态属性
QObject::setProperty() 函数可以在运行时为类定义一个新的属性,称之为动态属性。动态属性是针对类的实例定义的。
动态属性可以使用 QObject::property() 查询,就如在类定义里用 Q_PROPERTY 宏定义的属性一样。
例如,在数据表编辑界面上,一些字段是必填字段,就可以在初始化界面时为这些字段的关联显示组件定义一个新的 required 属性,并设置值为“true”,如:
editName->setProperty("required", "true");
editName->property("required");
comboSex->setProperty("required", "true");
checkAgree->setProperty("required", "true");然后,可以应用下面的样式定义将这种必填字段的背景颜色设置为亮绿色。
*[required="true"]{background-color:lime}类的附加信息
属性系统还有一个宏 Q_CLASSINFO(),可以为类的元对象定义“名称——值”信息,如:
class QMyClass:public QObject {
Q_OBJECT
Q_CLASSINFO("author", "Wang")
Q_CLASSINFO ("company", "UPC")
Q_CLASSINFO("version ", "3.0.1")
public:
...
};用 Q_CLASSINFO() 宏定义附加类信息后,可以通过元对象的一些函数获取类的附加信息,如 classlnfo(int) 获取某个附加信息,函数原型定义如下: QMetaClassInfo QMetaObject::classInfo(int index) const
返回值是 QMetaClassInfo 类型,有 name() 和 value() 两个函数,可获得类附加信息的名称和值。