Qt元对象系统

第二章Qt元对象系统

文章目录

  • 第二章Qt元对象系统
  • 1.什么是元对象?
  • 2.元对象系统组成
  • 3.信号与槽
    • 信号和槽的本质
    • 绑定信号与槽
    • 自定义槽
      • 定义槽函数必须遵循一下规则
      • 槽函数的类型
      • 自定义槽案例
    • 自定义信号
      • 自定义信号需要遵循以下规则
      • 信号和槽重载二义性问题
  • 4.内存管理
    • 1. 简介
    • 2.关联图
    • 3.详解
    • 4.智能指针
  • 5.属性系统
    • 获取/设置属性值
    • 自己声明属性值
    • 绑定属性
    • QObjectBindableProperty
  • 6.实时类型信息
  • 枚举
  • QMetaObject元信息

元对象系统是一个基于标准C++的扩展,为QT提供了新号与槽机制、实时类型信息、动态属性系统

1.什么是元对象?

在计算机科学中,元对象是这样一个东西:它可以操纵、创建、描述或执行其他对象。元对象描述的的对象称为基对象。元对象可能存在这样的信息:基础对象的类型、接口、类、方法、属性、变量、控制结构等。

2.元对象系统组成

  • QObject
    是QT对象模型的核心,绝大部分的QT类都是从这个类继承而来

  • Q_OBJECT
    Q_OBJECT宏必须出现在类定义的私有部分,用来开启信号和槽、动态属性系统,或QT元对象系统提供的其他服务

  • MOC
    MOC编译器为QObject子类提供了一些实现元对象特性所需要的一些代码。比如说信号,大家只是在类声明的时候声明了所需要的信息,在MOC编译时,会为信号添加函数定义。

3.信号与槽

所谓信号槽,实际就是观察者模式(发布-订阅模式)。当某个时间发生之后,比如按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽-slot)绑定来处理这个信号。也就是说,信号发出时,被连接的槽函数会自动被回调。

信号和槽的本质

信号是由对象发射出去的消息,信号实际上是一个特殊的函数,不需要由程序员实现,而是由QT的moc实现。
槽实际上就是普通的函数。
当我们把对象的信号和槽绑定在一起之后,当信号触发时,与之绑定的槽函数将会自动调用,并把信号参数传递给槽函数。

绑定信号与槽

使用QObject::connect()函数实现

#	Mainwindow.ui
# 创建一个PushButton
# 	Mainwindow.h 在下面加入代码
public slots:void onButtonClicked();
# Mainwindow.cpp
#include "MainWindow.h"
#include "./ui_MainWindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);auto con =connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(onButtonClicked()));QObject::connect(ui->pushButton_2,&QPushButton::clicked,this,&MainWindow::onButtonClicked);//lambda函数写法connect(ui->pushButton_3,&QPushButton::clicked,[=](){disconnect(con);qInfo()<<"断开1的连接";});
}
MainWindow::~MainWindow()
{delete ui;
}void MainWindow::onButtonClicked()
{qInfo()<<"我被点击了";
}

自定义槽

槽函数就是信号的处理动作,自定义槽函数和自定义的普通函数写法是一样的。只不过自定义的槽函数一般放在public slots:后面。

定义槽函数必须遵循一下规则

  1. 槽函数的返回类型必须是void
  2. 槽函数的参数必须等于或少于信号的参数
  3. 当信号与槽函数的参数数量相同时,它们参数类型要完全一致。

槽函数的类型

  • 成员函数
    • 普通成员函数
    • 静态成员函数
  • 全局函数
  • lambda表达式

自定义槽案例

#include <QApplication>
#include <QWidget>
#include<QPushButton>class MyWindow:public QWidget
{Q_OBJECT
public:MyWindow():QWidget(NULL){}
public slots:void showWindow(){this->show();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn = new QPushButton("打开");btn->show();MyWindow *mw=new MyWindow();auto con = QObject::connect(btn,&QPushButton::clicked,mw,&MyWindow::showWindow);return a.exec();
}#include "main.moc"

自定义信号

Qt框架提供的信号在某些特定场景下是无法满足我们的项目需求的,因此我们还设计自己需要的信号,同样还是使用connect()对接

自定义信号需要遵循以下规则

  • 信号是类的成员函数,并且返回类型必须是void
  • 信号函数只需要声明,不需要定义
  • 参数可以随意指定,信号也支持重载
  • 信号需要使用signals关键字进行声明
  • 在程序中发送自定义信号:发送信号的本质就是调用信号函数emit mysignals()
  • emit是个空宏,没有特殊含义,仅用来表示这个语句是发射一个信号,不写可以,但不推荐
#include <QApplication>
#include <QWidget>
#include<QPushButton>class MyWindow:public QWidget
{Q_OBJECT
public:MyWindow():QWidget(NULL){}
signals:void isShow(QString text);
public slots:void showWindow(){this->show();emit isShow("我出来了");}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn = new QPushButton("打开");btn->show();MyWindow *mw=new MyWindow();auto con = QObject::connect(btn,&QPushButton::clicked,mw,&MyWindow::showWindow);QObject::connect(mw,&MyWindow::isShow,btn,&QPushButton::setText);return a.exec();
}#include "main.moc"

信号和槽重载二义性问题

解决方法


#include <QApplication>
#include <QWidget>
#include<QPushButton>class MyWindow:public QWidget
{Q_OBJECT
public:MyWindow():QWidget(NULL){}
signals:void isShow();void isShow(QString text);public slots:void showWindow(){this->show();emit isShow("我出来了");}void showWindow(bool t){this->close();emit isShow();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn = new QPushButton("打开");btn->move(10,10);btn->show();QPushButton* btn1 = new QPushButton("关闭");btn1->move(10,100);MyWindow *mw=new MyWindow();//用overload重载QObject::connect(btn,&QPushButton::clicked,mw,QOverload<>::of(&MyWindow::showWindow));QObject::connect(mw,QOverload<QString>::of(&MyWindow::isShow),btn,&QPushButton::setText);//用qt4方式SIGNAL和SLOT宏QObject::connect(btn1,SIGNAL(clicked(bool)),mw,SLOT(showWindow(bool)));//SIGNAL和SLOT要前后一致QObject::connect(mw,SIGNAL(isShow()),btn1,SLOT(close()));QObject::connect(mw,SIGNAL(isShow(QString)),btn1,SLOT(show()));return a.exec();
}#include "main.moc"

4.内存管理

1. 简介

C++中new 和delete必须配对使用,防止内存泄露。Qt中使用了new却很少delete,因为Qt实现了其独特的内存管理机制。
QObject以对象树的形式组织起来。当为一个对象创建子对象时,子对象会自动地添加到父对象的children()列表中。父对象拥有子对象的所有权,比如父对象可以在自己的析构函数中删除它的子对象。使用findChild()或findChildren()通过名字和类型查询孩子对象。

QObject(QObject *parent=nullptr)
  1. QObject及其派生类的对象,如果其parent非nullptr,那么其parent析构时会析构该对象。
  2. 父子关系:父对象、子对象、父子关系。这是Qt中所特有的,与类的继承关系无关,传递参数是与parent有关

2.关联图

在Qt中,最基础和核心的类是QObject,QObject内部有一个名为children的QObjectList列表,会保存所有子对象,还有一个指针parent,用来指向父对象,当自己析构时,会先把自己从parent列表中删除,并且析构所有的child
在这里插入图片描述

3.详解

  1. 指定父对象释放,自己也释放

#include <QApplication>
#include <QWidget>
#include<QPushButton>class MyWindow:public QWidget
{Q_OBJECT
public:MyWindow(QWidget* parent=nullptr):QWidget(parent){}~MyWindow(){qInfo()<<__FUNCTION__<<"释放了";}
signals:void isShow();void isShow(QString text);public slots:void showWindow(){this->show();emit isShow("我出来了");}void showWindow(bool t){this->close();emit isShow();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn = new QPushButton("打开");btn->move(10,10);btn->show();QPushButton* btn1 = new QPushButton("关闭");btn1->move(10,100);MyWindow *mw=new MyWindow(btn1);//用overload重载QObject::connect(btn,&QPushButton::clicked,mw,QOverload<>::of(&MyWindow::showWindow));QObject::connect(mw,QOverload<QString>::of(&MyWindow::isShow),btn,&QPushButton::setText);//用qt4方式SIGNAL和SLOT宏QObject::connect(btn1,SIGNAL(clicked(bool)),mw,SLOT(showWindow(bool)));//SIGNAL和SLOT要前后一致QObject::connect(mw,SIGNAL(isShow()),btn1,SLOT(close()));QObject::connect(mw,SIGNAL(isShow(QString)),btn1,SLOT(show()));btn1->deleteLater();return a.exec();
}#include "main.moc"
  1. 查找子对象
	mw->setObjectName("window");auto wd =btn1->findChild<MyWindow*>("window"); //找子对象if(wd)qInfo()<<"存在";

4.智能指针

智能指针描述
QPointerQObject或子类对象释放时会自动指向nullpter
QScopedPointer【独享指针】超出作用域自动释放管理的对象
QSharedPoiner【共享指针】
QWeakPointer【监视指针】
QScopedArrayPointer【独享数组指针】超出作用域自动释放管理的对象数组
QSharedDataPointer【隐式共享指针】读时共享,写时拷贝
QExplicitlySharedDataPointer【显式共享指针】读时共享,写时需要手动拷贝(通过detach()函数)
  • 使用方法
	#include<QPointer>QPointer<QPushButton> btn1 = new QPushButton("关闭");btn1->move(10,100);MyWindow *mw=new MyWindow(btn1.data());
	#include<QSharedPointer>QSharedPointer<QPushButton> btn1(new QPushButton("关闭"));QObject::connect(btn1.data(),SIGNAL(clicked(bool)),mw,SLOT(showWindow(bool)));//SIGNAL和SLOT要前后一致QObject::connect(mw,SIGNAL(isShow()),btn1.data(),SLOT(close()));QObject::connect(mw,SIGNAL(isShow(QString)),btn1.data(),SLOT(show()));
	#include<QScopedArrayPointer>QScopedArrayPointer<int> arrptr(new int[10]);arrptr[1] = 666;

5.属性系统

属性的行为类似于数据成员,但它具有通过元对象系统访问的附加特性
在这里插入图片描述

获取/设置属性值

获取

    MyWindow *mw=new MyWindow();mw->show();qInfo()<< mw->property("pos");//结果QVariant(QPoint, QPoint(639,249))

设置

    QObject::connect(btn,&QPushButton::clicked,[&](){auto pos = mw->property("pos").toPoint();mw->setProperty("pos",QPoint(pos.x()-2,pos.y()-2));});

自己声明属性值

要声明属性,请在继承QObject的类中使用Q_PROPERTY()宏

Q_PROPRETY(type name(READ getFunction [WRITE setFunction]|MEMBER memberName [(READ getFunction|WRITE setFunction)])[RESET restFunction][NOTIFY notifySignal][REVISION int | REVISION(int[,int])][DESIGNABLE bool][SCRIPTABLE bool][STORED bool][USER bool][BINDABLE bindableProperty][CONSTANT][FINAL][REQUIRED])

属性的行为类似于数据成员,但它具有通过元对象系统访问的附加特性。

  • 如果未指定MEMBER变量,则需要READ访问器函数。它用于读取属性值。理想情况下,const函数用于此目的,它必须返回属性的类型或对该类型的const引用。例如,QWidget::focus是一个带有READ函数的只读属性,QWidget::hasFocus()。
  • WRITE访问器函数是可选的。它用于设置属性值。它必须返回void并且必须只接受一个参数,该参数可以是属性类型,也可以是指向该类型的指针或引用。例如,QWidget::enabled具有WRITE函数QWidget::setEnabled()。只读属性不需要WRITE函数。例如,QWidget::focus没有Write功能。
  • 如果未指定READ访问器函数,则需要MEMBER变量关联。这使得给定的成员变量可读可写,而无需创建READ和WRITE访问器函数。如果您需要控制变量访问,除了MEMBER变量关联之外,仍然可以使用READ或WRITE访问器函数。
  • RESET功能是可选的。它用于将属性设置回特定于上下文的默认值。
  • NOTIFY信号是可选的。如果已定义,则应指定该类中的一个现有信号,该信号在属性值更改时发出。MEMBER变量的NOTIFY信号必须采用零个或一个参数,该参数必须与属性的类型相同。该参数将采用属性的新值。NOTIFY信号只应在属性真正被更改时发出,以避免在QML中不必要地重新评估绑定。
  • REVISION编号或REVISION()宏是可选的。如果包含,它定义了要在API的特定修订版中使用的属性及其通知信号(通常用于暴露于QML)。如果不包含,则默认为0.
  • DESIGNABLE属性指示该属性是否应在GUI设计工具(如Qt Designer)的属性编辑器中可见。大多数属性是可设计的(默认为true)。有效值为真和假。
  • SCRIPTABLE属性指示脚本引擎是否可以访问此属性(默认为true)。有效值为真和假。
  • STORED属性指示该属性是否应该被认为是独立存在的,或取决于其他值。它还指示在存储对象的状态时是否必须保存属性值。大多数属性值是STORED的(默认为true)。
  • USER属性指示该属性是被指定为该类的面向用户的属性还是用户可编辑的属性。通常,每个类只有一个USER属性(默认false)。
  • BINDABLE bindableProperty属性表明该属性支持绑定,并且可以通过对元对象系统设置和检查该属性的绑定。bindableProperty命名QBindable类型的类成员,其中T是属性类型。
  • CONSTANT属性的存在表明属性值是常量。对于给定的对象实例,常量属性的READ方法在每次调用时都必须返回相同的值。对于对象的不同实例,该常数值可能不同。常量属性不能有WRITE方法或NOTIFY信号。
  • FINAL属性的存在表明该属性不会被派生类覆盖。这在某些情况下可用于性能优化,但并非由moc强制执行。必须注意永远不要覆盖FINAL属性。
  • REQUIRED属性的存在表明该属性应该由该类的用户设置。这不是由moc强制执行的,并且对于暴露给QML的类最有用。在QML中除非设置了所有的REQUIRED属性,否则无法实例化具有REQUIRED属性的类。

#include <QApplication>
#include <QWidget>
#include<QPushButton>
#include<QPointer>class MyWindow:public QWidget
{Q_OBJECTQ_PROPERTY(QString name READ getname WRITE setname NOTIFY nameChanged FINAL)QString m_name;
public:MyWindow(QWidget* parent=nullptr):QWidget(parent){}~MyWindow(){qInfo()<<__FUNCTION__<<"释放了";}
signals:void isShow();void isShow( QString text);
signals:void nameChanged();
public slots:QString getname(){qInfo()<<__FUNCTION__;return m_name;}void setname(QString name){qInfo()<<__FUNCTION__;m_name = name;emit nameChanged();}
public slots:void showWindow(){this->show();emit isShow("我出来了");}void showWindow(bool t){this->close();emit isShow();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn = new QPushButton("打开");btn->move(10,10);btn->show();MyWindow *mw=new MyWindow();mw->showWindow();QObject::connect(btn,&QPushButton::clicked,[=](){auto pos = mw->property("pos").toPoint();mw->setProperty("pos",QPoint(pos.x()-2,pos.y()-2));mw->setProperty("name","abc");//自定义的name属性改变});QObject::connect(mw,&MyWindow::nameChanged,[&,btn](){auto windowName = mw->property("name");btn->setText(windowName.toString());});return a.exec();
}#include "main.moc"

直接关联也可以

 Q_PROPERTY(QString name  MEMBER m_name NOTIFY nameChanged FINAL)

绑定属性


#include <QApplication>
#include<QProperty>struct Rectangle
{Rectangle() {}int w,h,area;Rectangle(int w,int h):w(w),h(h){area = this->w*this->h;}
};
struct Rect
{Rect() {}QProperty<int> w,h,area;Rect(int w,int h):w(w),h(h){//设置绑定属性area.setBinding([&]{return this->w*this->h;});}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);//没设置绑定属性Rectangle rl(10,10);qInfo()<<rl.area;rl.h =20;qInfo()<<rl.area;//2个结果一样,都是100,没改变//设置绑定属性后Rect r(10,10);qInfo()<<r.area;r.h = 20;qInfo()<<r.area;//结果分别是100,和200return a.exec();
}

QObjectBindableProperty


#include <QApplication>
#include<QProperty>
#include<QWidget>
#include <QPushButton>class MyWindow:public QWidget
{Q_OBJECT
public:MyWindow(QWidget* parent=nullptr):QWidget(parent){}signals:void myxChanged();public:Q_OBJECT_BINDABLE_PROPERTY(MyWindow,int,myx,&MyWindow::myxChanged);};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn = new QPushButton("打开");btn->move(10,10);btn->show();MyWindow* mw = new MyWindow();QObject::connect(btn,&QPushButton::clicked,[=]{static int a=0;mw->show();mw->myx = ++a;});QObject::connect(mw,&MyWindow::myxChanged,[&]{qInfo()<<"x修改了";});return a.exec();
}#include "main.moc"

6.实时类型信息

判断继承自XX类

qInfo()<<mw->inherits("QObject");//true

枚举

#include <QMetaEnum>namespace Yerennuo {
//命名空间
Q_NAMESPACE
enum Type
{Player,Enemy,Bullet
};
//注册枚举
Q_ENUM_NS(Type)
}qInfo()<< Yerennuo::Type::Bullet;//Yerennuo::Bullet

也可以在类里面

class Test:public QObject
{Q_OBJECT
public:enum Type{Player,Enemy,Bullet};Q_ENUM(Type)
};qInfo()<<Test::Type::Player;//Test::Player

QMetaObject元信息


#include "qmetaobject.h"
#include <QApplication>
#include<QWidget>
#include <QMetaObject>class MyWindow:public QWidget
{Q_OBJECTQ_CLASSINFO("author","yerennuo")
public:MyWindow(QWidget* parent=nullptr):QWidget(parent){}};int main(int argc, char *argv[])
{QApplication a(argc, argv);MyWindow* mw = new MyWindow();auto metaobj = mw->metaObject();qInfo()<<"key="<<metaobj->classInfo(0).name()<<"value="<<metaobj->classInfo(0).value();//key= author value= yerennuoreturn a.exec();
}#include "main.moc"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/585863.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ | Leetcode C++题解之第6题Z字形变换

题目&#xff1a; 题解&#xff1a; class Solution { public:string convert(string s, int numRows) {int n s.length(), r numRows;if (r 1 || r > n) {return s;}string ans;int t r * 2 - 2;for (int i 0; i < r; i) { // 枚举矩阵的行for (int j 0; j i &l…

GDC回顾与MAU前瞻丨Flat Ads开启开发者流量变现新篇章

3月18日-22日,全球游戏行业最具规模、最有影响力的盛会——GDC 2024 在美国旧金山 Moscone Convention Center 成功举办,Flat Ads作为参展商亮相GDC大会,向全球游戏开发者展示我们的最新技术与服务。此次Flat Ads团队不仅洞察了行业最前沿的技术和发展趋势,同时也与诸多一线开发…

GWO-CNN-BiLSTM多输入回归预测|灰狼群算法优化的卷积-双向长短期神经网络|Matlab

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&…

电梯四种事故检测YOLOV8

电梯四种事故检测&#xff0c;采用YOLOV8训练得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV调用&#xff0c;支持C/PYTHON/ANDORID开发 电梯四种事故检测YOLOV8

海康威视(老版本)录像机+(新版本)摄像头 不兼容的解决方案

一、适用场景 1、海康威视的硬盘录像机使用多年&#xff0c;增加新版本的摄像头&#xff0c;原投资沿用&#xff1b; 2、监控网络第一期工程与第二期工程相隔的时间长&#xff0c;摄像头更新换代快&#xff1b; 3、企业或单位自己动手建监控网络&#xff1b; 4、上级主管部门要…

高性能威廉希尔产品特点低代码开发平台

高性能低代码是唯一一种使您能够构建复杂的、战略性的、任务关键型的消费者和内部软件的低代码类别。 它在提供端到端应用程序开发和更新方面没有限制&#xff0c;没有额外的许可&#xff0c;没有可扩展性问题&#xff0c;也没有更新的数据“重做”&#xff0c;当你从部门应用程…

vue3源码解析——ref和reactive定义响应式的区别

ref 和 reactive 是 Vue 3.0 中用于定义响应式数据的两个新 API。它们有以下区别&#xff1a; ref 定义单个响应式数据 数据类型可以是任意类型。它通常用于定义原始数据类型为响应式数据。返回一个响应式对象&#xff0c;该对象包含一个 .value 属性&#xff0c;可用于获取和设…

Python学习从0到1 day20 第二阶段 面向对象 ② 封装

缘分 朝生暮死犹如露水 —— 24.4.1 学习目标&#xff1a; 1.理解封装的概念 2.掌握私有成员的使用 一、面向对象三大特性&#xff1a; 面向对象编程&#xff0c;是许多编程语言都支持的一种编程思想 简单理解是&#xff1a;基于模板&#xff08;类&#xff09;去创建实体&…

leetcode刷题-字符串

目录 1、Reverse String 反转字符串 2、Reverse String II 反转字符串II 3、Reverse Words in a String 翻转字符串里的单词 4、Find the Index of the FirstOccurrence in a String 实现 strStr() KMP算法 next数组如何建立 模式串和字符串匹配 5、Repeated Substring…

数据结构与算法实验6——队的应用

一、实验目的 队列的应用&#xff0c;结合应用实例&#xff0c;深入理解和掌握队列。 二、实验软硬件要求 1、VC 6.0 三、实验预习 队列基本操作 四、实验内容&#xff08;实验步骤、测试数据等&#xff09; 1、队列基本操作。请选择循环队列结构或链式队列结构实现队列…

nginx | nginx反向代理/负载均衡/缓存

文章目录 一、Nginx 反向代理1.1 nginx 文件结构1.2 默认的nginx配置文件1.3 实践中的 nginx.conf 二、Nginx 负载均衡2.1 热备负载均衡2.2 轮询负责均衡2.3 加权轮询负载规则2.4 ip_hash 负载均衡2.5 对特定资源实现负载均衡2.6 对不同域名实现负载均衡2.7 实现带有URL重写的负…

Java NIO是New IO还是Non-blocking IO

文章目录 前言NIO到底叫啥通过对比理解NIO传统IO网络编程NIO引入的新概念NIO网络编程两者区别NIO的事件驱动 总结 前言 很多小伙伴对Java NIO的一些概念和编程不是很理解&#xff0c;希望通过本文对Java NIO与传统IO的对比&#xff0c;可以帮助大家更好地理解和掌握Java NIO。…