MOC:元对象编译器;
元对象(meta object)意思是描述另一个对象结构的对象,比如获得一个对象有多少成员函数,有哪些属性。在Qt中,我们将要用到的是QMetaObject这个类。
Qt的元对象系统系统建立在以下三个方面:
- QObject类是所有使用元对象系统类的基类
- 必须在一个类的开头插入宏Q_OBJECT,这样这个类才可以使用元对象系统的特性
- MOC为每个QObject的子类提供必要的代码来实现元对象系统的特性
1 QObject类
QObject类是所有使用元对象系统类的基类;
元对象系统的特性是通过QObject的一些函数实现的,主要包括以下几个部分:
- 元对象:函数metaObject()返回这个元对象指针。所以。获取一个对象的元对象有两种方式,示意代码如下:
QPushButton *btn=new QPushButton();
const QMetaObject *metaPtr=btn->metaObject(); //获取元对象指针
const QMetaObject metaObj=btn->staticMetaObject; //获取元对象
- 类型信息
- 动态翻译
- 对象树:表示对象间从属关系的树状结构;对象树中的某个对象被删除时,它的子对象就会被自动删除,因此,一个窗口被删除时,它上面的所有界面组件也会被自动删除;
- 信号与槽:在类定义中插入宏Q_OBJECT,就可以使用Qt扩展的c++语言特性编程;
- 属性系统:在类的定义中利用宏Q_PROPERTY定义属性,QObject的setProperty()函数会设置属性的值或定义动态属性;property()函数会返回属性的值;
QObject元对象核心类https://zhuanlan.zhihu.com/p/43598693
2 QMetaObject类
元对象是QMetaObject类型的实例。元对象存储了类的实例所属类的各种元数据,包括类信息元数据、方法元数据、属性元数据等。所以,元对象实质上是对类的描述;
QMetaObject类实际上是通过一些接口函数来获取所属类的各种元数据,包括类信息元数据、方法元数据、属性元数据等。
3 运行时类型信息(RTTI) P50
(1)函数QMetaObject::className():该函数运行时返回类名称的字符串
(2)函数QObjetc::inhetits()。可以判断一个对象是不是继承自某个类的实例。顶层的父类是QObject ;
(3)函数QMetaObject::superClass()。用来返回该元对象所描述类的父类的元对象,通过父类的元对象可以获取父类的一些元数据;
(4)函数qobject_cast(): 对于Object及其子类对象,可以使用函数qobject_cast()进行动态类型转换,此处可以理解为c++中的强制类型转换;如下:
QObject *btn=new QPushButton(); //创建QPushButton对象,但是使用QObject指针
const QMetaObject *meta=btn->metaObject();
QString str1=QString(meta->clasName()); //str1="QPushButton"
QPushButton *btnPush=qobject_cast<QPushButton*>(btn); //转换成功,将Object类型转换成QPushButton类型
const QMetaObject *meta2=btnPush->metaObject();
QString str2=QString(meta2->clasName()); //str1="QPushButton"
#此时的转换是成功的,因为btn就是QPushButton对象指针,但是,如果将btn转换成QCheckBox对象指针就会失败,因为QChechBox不是QPushButton的父类;
4 属性系统
在QObject的子类中,使用宏Q_PROPERTY定义属性;格式如下:
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])
MEMBER:指定一个成员变量与属性关联,使之成为可读可写的属性,指定后无需再设置READ和WRITE。
RESET:是可选的,用于将属性设置为上下文指定的默认值
Q_PROPERTY这个宏,简单用法如下:
Q_PROPERTY(type name READ getFunction WRITE setFunction)
Q_PROPERTY(参数类型 参数名称 READ 获得属性值函数 WRITE 设置属性值函数)
比如Q_PROPERTY(bool bIsDoubi READ getDoubi WRITE setDoubi),属性类型是bool类型,bIsDoubi是属性名称。除此之外还需要写两个函数,第一个是设置属性的函数void setDoui(bool),第二个是获得属性的函数bool getDoubi()。
QMetaObject类的一些函数可以提供元对象所描述类的属性元数据,属性元数据用QMetaProperty类描述,它有各种函数可反应属性的一些特性,例如下面的一段代码:
const QMetaObject *meta=ui->spinBoy->metaObject(); //获取一个SpinBox的元对象
int index=meta->indexOfProperty("value"); //获取属性value的序号
OMetaProperty prop=meta->property(index); //获取属性value的元数据
bool res=prop.isWritable(); //属性是否可写,值为true
res=prop.isDesignable(); //属性是否可设计,值为true
res=prop.hasNotifySignal(); //属性是否有反映属性值变化的信号,值为true
5 信号与槽
qt使用信号与槽机制实现对象之间的通信,它隐藏了复杂的底层实现;是Qt的核心特性;
5.1 connect函数不同的参数形式
connect(sender,SIGNAL(signal()),receiver,SLOT(slot())); 如下:
connect(ui->radioButton_red, SIGNAL(clicked()),this, SLOT(do_fontcolor()));
connect(ui->radioButton_blue, &QRadioButton::clicked,this, &do_fontcolor);
当槽函数是overload型时,需要使用模板函数qOverload()来明确参数类型;
//如果在窗口类中包含下列两个槽函数
void do_click(bool checked);
void do_click();
connect(ui->checkBox,&QcheckBox::clicked,this,qOverload<bool>(&Widget::do_click));
connect(ui->checkBox,&QcheckBox::clicked,this,qOverload<>(&Widget::do_click));
注意:遇到重载的槽函数一定要使用qOverload()模板函数来明确参数类型;
对于overload型信号,只要槽函数不是overload型,就可以使用传递函数指针的connect()来进行信号与槽的关联;Qt会根据槽函数的参数自动确定使用哪个信号;
QObject::connect(pushButton_ok, &QPushButton::clicked, Dialog, qOverload<>(&QDialog::accept));
QObject::connect(pushButton_close, &QPushButton::clicked, Dialog, qOverload<>(&QDialog::close));
5.2 disconnect()函数的使用
disconnect():用于解除信号与槽的连接;
使用方式如下:
(1)解除与一个发射者所有信号的连接,例如:
disconnect(myobject,nullptr,nullptr,nullptr); //静态函数形式
myobject->disconnect(); //成员函数形式
(2)解除与一个特定信号的所有连接,例如:
disconnect(myobject,SIGNAL(mysignal()),nullptr,nullptr); //静态函数形式
myobject->disconnect(SIGNAL(mysignal())); //成员函数的形式
(3)解除与一个特定接收者的所有连接,例如:
disconnect(myobject,nullptr,myreceiver,nullptr); //静态函数形式
myobject->disconnect(myreceiver); //成员函数的形式
(4)解除特定的一个信号与槽的连接,例如:
disconnect(lineEdit,&QLineEdit::textChanged,label,&QLabel::setText); //静态函数形式
6 对象树 (P57)
使用QObject及其子类创建的对象(统称为QObject对象)是以对象树的形式来组织的;父对象被删除,其全部子对象就会被自动删除;