qt中的句柄类,实体类
1.应用程序封装成so动态库时,需要提供头文件,此时为了把私有数据封装起来,就需要把私有数据封装成private的类,把它放在cpp文件而非h文件中,
这样就把私有数据封装起来不给用户看了。
2.在私有数据修改时,不用重新编译所有文件,只需要编译这个动态库就可以了,因为头文件并没有改变。
D指针/私有实现
使用D指针:
class MyClass
{
public:
MyClass();
~MyClass();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
};
class MyClassPrivate;
定义了一个指针d_ptr指向私有实现类,然后用Q_DECLARE_PRIVATE宏来定义一些辅助函数和声明友元类:
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;
只要在类的头文件中使用这两个宏,就可以通过函数直接得到实体类和句柄类的实际类型了,而且这里还声明了友元,使得数据类和句柄
类连访问权限也不用顾忌了
然后这个私有类的实现如下所示:
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent);
private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
int myVar;
};
这里的q_ptr指针就是指向公开的接口了,然后Q_DECLARE_PUBLIC宏则定义了辅助函数并声明了友元类:
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
friend class Class;
而我们还可以用Q_D和Q_Q两个宏来进一步简化访问:
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
以后局部变量可千万不能声明为d和q了。
qt的 Q_PRIVATE_SLOT是用来干什么的:
每个qobject, qwidget这样的类中 ,把内部数据和内部函数都写在private类里面了 ,然后用这个宏把private类里面的函数声明为主类的slot
QApplication构造过程
QApplication::QApplication(int &argc, char **argv)
: QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient) //第一步) //第二步
{
Q_D(QApplication); //第三步
d->construct(); //第四步
}
第一步构造了QApplicationPrivate的实例,其中GuiClient是一个枚举常量,表示应用程序的类型,这里我们不用关心。说道QApplicationPrivate,
如果你看过Qt的源码,你会发现很多类都配对有相应的Private类,这是Qt在封装底层实现时经常用的一种模式,Handle-Body Design Pattern,
也就是说你在xx类中看不到具体的实现,具体的实现都封装在xxPrivate类中,而在xx类的方法只是调用xxPrivate中相应的方法。
第二步用QApplicationPrivate的实例构造了QCoreApplication,这里调用的是QCoreApplication的QCoreApplication::QCoreApplication
(QCoreApplicationPrivate &p)构造函数,代码如下:
QCoreApplication::QCoreApplication(QCoreApplicationPrivate &p)
: QObject(p, 0)
{
init();
// note: it is the subclasses' job to call
// QCoreApplicationPrivate::eventDispatcher->startingUp();
}[/code]上面代码中的init初始化了一些资源,其中包括事件分发器。
第三步定义了d,这里是通过宏Q_D来实现的,其中宏Q_D的定义如下:]#define Q_D(Class) Class##Private * const d = d_func()所以这里的d即
是调用父类构造函数QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)时创建的QApplicationPrivate实例。
第四步调用d的construct函数,也就是QApplicationPrivate的实例的construct函数。
QObject解析
QObject是Qt类体系的唯一基类
QObject的大小是8,除了虚函数表指针需要的4个字节以外,另外的4个字节是:
QObjectData *d_ptr;
QObject中的数据被封装在QObjectData类中了。Qt中有一个很重要的设计模式就是句柄实体模式,以QObject为基类的类一般都是句柄类,只有一个指针指
向一个实体类,在实体类中保存全部的数据,而且这个指针还是私有的。Qt的基类其实有两个,一个是QObject,这是句柄类的唯一基类,另一个是QObjectData,
这是实体类的基类。
QObjectData类定义如下:
class QObjectData {
public:
QObject *q_ptr;
};
QObject *q_ptr;指针指向实体类对应的句柄类,这和上面的代码QObjectData *d_ptr;遥相呼应,使得句柄类和实体类可以双向的引用。q指的是Qt接口类,d指的
是Data数据类。
d_func和q_func函数是非常常用的函数,可以理解为一个是得到数据类,一个是得到Qt接口类
实体类派生关系中多了一个QObjectPrivate,这个类封装了线程处理,信号和槽机制等具体的实现,可以说它才是Qt实体类中真正起作用的基类,而QObjectData不过是一层浅浅的数据封装而已
看看在Qt中句柄类和实体类这两条体系是如何构造的?
QPushButton* quit = new QPushButton("Quit");
创建一个Qt的按钮,背后大有玄机
QPushButton::QPushButton(const QString &text, QWidget *parent)
: QAbstractButton(*new QPushButtonPrivate, parent)
首先QPushButton的构造函数中调用了QAbstractButton的构造函数,同时马上new出来一个QPushButtonPrivate实体类,然后把指针转换为引用传递给QAbstractButton
QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
: QWidget(dd, parent, 0)
QAbstractButton的构造函数中继续调用基类QWidget的构造函数,同时把QPushButtonPrivate实体类指针继续传给基类
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)
: QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice()
QWidget继续做着同样的事情
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
终于到了基类QObject,这里就直接把QPushButtonPrivate的指针赋值给了d_ptr(还记得这个变量名称吧)
最终在QPushButton构造时同时产生的new QPushButtonPrivate被写到了QObject中的d_ptr中
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
Q_D(QObject);
::qt_addObject(d_ptr->q_ptr = this);
QThread *currentThread = QThread::currentThread();
d->thread = currentThread ? QThreadData::get(currentThread)->id : -1;
Q_ASSERT_X(!parent || parent->d_func()->thread == d->thread, "QObject::QObject()",
"Cannot create children for a parent that is in a different thread.");
if (parent && parent->d_func()->thread != d->thread)
parent = 0;
if (d->isWidget) {
if (parent) {
d->parent = parent;
d->parent->d_func()->children.append(this);
}
// no events sent here, this is done at the end of the QWidget constructor
} else {
setParent(parent);
}
}
然后执行QObject的构造函数,这里主要是一些线程的处理,先不理它
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)
: QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice()
{
d_func()->init((parent && parent->windowType() == Qt::Desktop ? parent : 0), f);
}
然后是QWidget的构造函数,这里调用了数据类QWidgetPrivate的init函数,这个函数不是虚函数,因此静态解析成QWidgetPrivate的init函数调用
QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
: QWidget(dd, parent, 0)
{
Q_D(QAbstractButton);
d->init();
}
然后是QAbstractButton的构造函数,这里调用了数据类QAbstractButton的init函数,这个函数不是虚函数,因此静态解析成QAbstractButton的init函数调用
QPushButton::QPushButton(const QString &text, QWidget *parent)
: QAbstractButton(*new QPushButtonPrivate, parent)
{
Q_D(QPushButton);
d->init();
setText(text);
}
然后是QPushButton的构造函数,这里调用了数据类QPushButton的init函数,这个函数不是虚函数,因此静态解析成QPushButton的init函数调用
现在的事情很清楚了,总结一下:
QPushButton在构造的时候同时生成了QPushButtonPrivate指针,QPushButtonPrivate创建时依次调用数据类基类的构造函数
QPushButton的构造函数中显示的调用了基类的构造函数并把QPushButtonPrivate指针传递过去,QPushButton创建时依次调用接口类基类的构造函数
在接口类的构造函数中调用了平行数据类的init函数,因为这个函数不是虚函数,因此就就是此次调用了数据类的init函数
需要指出的是,为什么QPushButtonPrivate实体类指针要转换为引用呢?为什么不是直接传递指针?结论是人家喜欢这样写,就是不传指针传引用,而且要用一个*new之类的怪异语法,
真叫人没有办法,其实这里用指针是一样的,代码看起来也自然一些.
Qt对象有自己的内存管理策略,当父对象被析构时,会将它的所有子对象同时析构。因此,只要一个Qt对象的父对象设置适当,它就会在父对象析构时自动析构,不需要做多余的delete操作。
注意: 由于窗口内的部件会在窗口析构时自动用delete操作符析构,因此这些部件不能定义为成员变量,只能由new操作符动态生成。
各种Qt对象类的构造函数一般都可以接受一个Qt对象指针作为参数,用于设置父对象,这样在Qt对象生成时就确定了它的父对象。另外,有些函数可以改变Qt对象的父对象,如布局类
addWidget函数,它会使加入布局的窗口部件成为布局所在窗口的子对象。窗口内的布局和部件是分开管理的,只有最顶级的布局的父对象是窗口,其他布局的父对象都是包含它的布局,
而窗口部件不管在哪个布局内,其父对象都是窗口本身。
使用Qt进行编程必须对 Qt 中常用的类有一定的了解。这些类可以分成两种:一种不是从 QObject 类派生出来的,用来表示各种基本的数据对象,如字符串、图像、字体等,这里将它们通
称为基本类;另一种都是从 QWidget 类派生出来的,它们表示一个顶级窗口或者窗口部件,这里将它们统称为窗口类
qt中的句柄类,实体类相关推荐
- JPA 不在 persistence.xml 文件中配置每个Entity实体类的2种解决办法
原文地址:http://www.cnblogs.com/taven/p/3351841.html JPA 不在 persistence.xml 文件中配置每个Entity实体类的2种解决办法 在Spr ...
- mybatis高级(2)_数据库中的列和实体类不匹配时的两种解决方法_模糊查询_智能标签...
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...
- 【Dart 教程系列第 28 篇】Dart中的 Map 和实体类的相互转换
这是[Dart 教程系列第 28 篇],如果觉得有用的话,欢迎关注专栏. 首先声明一个实体类 class People {String name; // 姓名String area; // 地区Str ...
- java xml 实体类_java中的xml与实体类之间的映射
实体类: package xml; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class User ...
- java实体类怎么写_Java中(entity)实体类的书写规范
在日常的Java项目开发中,entity(实体类)是必不可少的,它们一般都有很多的属性,并有相应的setter和getter方法.entity(实体类)的作用一般是和数据表做映射.所以快速写出规范的e ...
- mysql中datetime如何用实体类封装_死磕了关于MyBatis10种超好用的写法
用来循环容器的标签forEach,查看例子 foreach元素的属性主要有item,index,collection,open,separator,close. item:集合中元素迭代时的别名 in ...
- QT中Json的使用方法(类和json字符串相互转换的方法)
在QT5.0版本以后支持对Json的使用,直接使用QT提供的json类进行数据的组织和解析. Json类 介绍 QJsonDoucument 它封装了一个完整的 JSON 文档,并且可以从 UTF-8 ...
- 浅谈Java项目中要不要使用实体类
问题背景: 经过在学校的努力学习,2021届菜鸟毕业喽.终于踏上了接受社会毒打的历程,毕业后入职第一家公司,欢天喜地的打开项目准备写下毕业后的第一套增删改查,然后emmmmmmm 公司的项目中 ...
- Gentle中的数据表实体类相关自定义属性的设置和获得
1.自定义 表名属性 TableNameAttribute 2.自定义 主键属性 PrimaryKeyAttribute 3.自定义 列名属性 TableColumnAttribute 4.数据表pe ...
- 软件框架SpringBoot-实现使用@Component@Data@Configuration@Bean(配置类控制类实体类)等方法实现将配置文件从8080端口显示在网页上
一.前言 1.该程序代码是使用idea2021.12版本编写的,若使用其他软件请对照好配置: 2.这个程序具体的内容我忘了,只知道使用@Component@ConfigurationPropertie ...
最新文章
- Linux调试技术介绍
- 如何兼容自训练与预训练:更高效的半监督文本分类模型
- 《挖掘管理价值:企业软件项目管理实战》一2.4 软件设计过程
- c++ fmt 库安装和使用示例、clion配置
- springboot的基础面试题
- vs2015 mysql edmx_VS2015+MySql EF的配置问题
- 【Matlab学习笔记】【编程实例】二(将两幅灰度图片调整成相同的尺寸,然后左右拼接到一起)
- 浅谈javascript的运行机制
- 3种云桌面(VDI、IDV、VOI)技术解决方案简介
- RFID(Radio Frequency Identification)技术,又称无线射频识别
- 供应链金融运营研究 -----内容运营
- 微软修改 MIT 项目原作者版权声明引发争议;白宫为提高开源安全性邀请软件行业者座谈;Ruby 3.1.0 发布 | 开源日报
- 运维黄金4指标,构筑
- 虾皮面试真题:虾皮后端15连问!
- 【体系-微服务架构】23-Spring Cloud Spring生态链(Alibaba)
- 西门子——好用的通讯仿真通讯工具NetToPLCsim
- Top 7大开源数据可视化分析工具!
- 更换ruby源----RubyGems 镜像 - 淘宝网
- Android TextView 文字两端对齐
- EtherCAT运动控制卡小线段前瞻的连续插补运动