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中的句柄类,实体类相关推荐

  1. JPA 不在 persistence.xml 文件中配置每个Entity实体类的2种解决办法

    原文地址:http://www.cnblogs.com/taven/p/3351841.html JPA 不在 persistence.xml 文件中配置每个Entity实体类的2种解决办法 在Spr ...

  2. mybatis高级(2)_数据库中的列和实体类不匹配时的两种解决方法_模糊查询_智能标签...

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...

  3. 【Dart 教程系列第 28 篇】Dart中的 Map 和实体类的相互转换

    这是[Dart 教程系列第 28 篇],如果觉得有用的话,欢迎关注专栏. 首先声明一个实体类 class People {String name; // 姓名String area; // 地区Str ...

  4. java xml 实体类_java中的xml与实体类之间的映射

    实体类: package xml; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class User ...

  5. java实体类怎么写_Java中(entity)实体类的书写规范

    在日常的Java项目开发中,entity(实体类)是必不可少的,它们一般都有很多的属性,并有相应的setter和getter方法.entity(实体类)的作用一般是和数据表做映射.所以快速写出规范的e ...

  6. mysql中datetime如何用实体类封装_死磕了关于MyBatis10种超好用的写法

    用来循环容器的标签forEach,查看例子 foreach元素的属性主要有item,index,collection,open,separator,close. item:集合中元素迭代时的别名 in ...

  7. QT中Json的使用方法(类和json字符串相互转换的方法)

    在QT5.0版本以后支持对Json的使用,直接使用QT提供的json类进行数据的组织和解析. Json类 介绍 QJsonDoucument 它封装了一个完整的 JSON 文档,并且可以从 UTF-8 ...

  8. 浅谈Java项目中要不要使用实体类

    问题背景:   经过在学校的努力学习,2021届菜鸟毕业喽.终于踏上了接受社会毒打的历程,毕业后入职第一家公司,欢天喜地的打开项目准备写下毕业后的第一套增删改查,然后emmmmmmm   公司的项目中 ...

  9. Gentle中的数据表实体类相关自定义属性的设置和获得

    1.自定义 表名属性 TableNameAttribute 2.自定义 主键属性 PrimaryKeyAttribute 3.自定义 列名属性 TableColumnAttribute 4.数据表pe ...

  10. 软件框架SpringBoot-实现使用@Component@Data@Configuration@Bean(配置类控制类实体类)等方法实现将配置文件从8080端口显示在网页上

    一.前言 1.该程序代码是使用idea2021.12版本编写的,若使用其他软件请对照好配置: 2.这个程序具体的内容我忘了,只知道使用@Component@ConfigurationPropertie ...

最新文章

  1. Linux调试技术介绍
  2. 如何兼容自训练与预训练:更高效的半监督文本分类模型
  3. 《挖掘管理价值:企业软件项目管理实战》一2.4 软件设计过程
  4. c++ fmt 库安装和使用示例、clion配置
  5. springboot的基础面试题
  6. vs2015 mysql edmx_VS2015+MySql EF的配置问题
  7. 【Matlab学习笔记】【编程实例】二(将两幅灰度图片调整成相同的尺寸,然后左右拼接到一起)
  8. 浅谈javascript的运行机制
  9. 3种云桌面(VDI、IDV、VOI)技术解决方案简介
  10. RFID(Radio Frequency Identification)技术,又称无线射频识别
  11. 供应链金融运营研究 -----内容运营
  12. 微软修改 MIT 项目原作者版权声明引发争议;白宫为提高开源安全性邀请软件行业者座谈;Ruby 3.1.0 发布 | 开源日报
  13. 运维黄金4指标,构筑
  14. 虾皮面试真题:虾皮后端15连问!
  15. 【体系-微服务架构】23-Spring Cloud Spring生态链(Alibaba)
  16. 西门子——好用的通讯仿真通讯工具NetToPLCsim
  17. Top 7大开源数据可视化分析工具!
  18. 更换ruby源----RubyGems 镜像 - 淘宝网
  19. Android TextView 文字两端对齐
  20. EtherCAT运动控制卡小线段前瞻的连续插补运动

热门文章

  1. java程序用maven打包的命令
  2. uniapp开发微信小程序--实现电子签名功能
  3. 字符串的编码格式转换
  4. UDS诊断服务(0x10)
  5. 使用Altium Designer与Ansys Q3D提取PCB寄生参数
  6. 周志华最新论文 | 深度森林:探索深度神经网络以外的方法
  7. python爬虫框架论文开题报告范文_论文开题报告范文
  8. php分组统计求和,数据需求统计常用shell命令---AWK分组求和,分组统计次数
  9. 各版本opencv官网下载路径
  10. 视频教程-思科网络工程师CCNP高级路由技术-路由协议