见 qglog.h文件定义:

#define Q_D(Class) Class##Private * const d = d_func()
    #define Q_Q(Class) Class * const q = q_func()

 d指针是在主类中使用的,来获取私有子类成员指针

  q指针是在私有数据类中使用的,来获取主类对象指针

D-指针 
   私有成员总是不可见的,Qt中私有成员不仅仅是简单封装一下,将访问权限改为private,它将所有私有数据封装在私有类里(命名就是 classname##private), 这样一来连用户都不知道他到底封装了什么,程序中只有这个私有类成员指针,这个指针就是D-指针。

从QObject开始看

[c-sharp] view plain copy
  1. class QObject
  2. {
  3. Q_DECLARE_PRIVATE(QObject)
  4. public:
  5. Q_INVOKABLE explicit QObject(QObject *parent=0);
  6. protected:
  7. QObject(QObjectPrivate &dd, QObject *parent = 0);
  8. QScopedPointer<QObjectData> d_ptr;
  9. // others
  10. }

展开后

[c-sharp] view plain copy
  1. class QObject
  2. {
  3. inline QObjectPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
  4. inline const QObjectPrivate* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
  5. friend class QObjectPrivate;
  6. public:
  7. Q_INVOKABLE explicit QObject(QObject *parent=0);
  8. protected:
  9. QObject(QObjectPrivate &dd, QObject *parent = 0);
  10. QScopedPointer<QObjectData> d_ptr;
  11. // others
  12. }

QObject的构造函数如下:

[c-sharp] view plain copy
  1. QObject::QObject(QObject *parent)
  2. : d_ptr(new QObjectPrivate)
  3. {
  4. // others
  5. }
  6. QObject::QObject(QObjectPrivate &dd, QObject *parent)
  7. : d_ptr(&dd)
  8. {
  9. // others
  10. }

也就是QObjectData *d_ptr = new QObjectPrivate

显然QObjectPrivate 继承了 QObjectData  ;

如下

[c-sharp] view plain copy
  1. QObjectData {
  2. public:
  3. virtual ~QObjectData() = 0;
  4. // others
  5. };
[c-sharp] view plain copy
  1. class Q_CORE_EXPORT QObjectPrivate : public QObjectData
  2. {
  3. Q_DECLARE_PUBLIC(QObject)
  4. public:
  5. QObjectPrivate(int version = QObjectPrivateVersion);
  6. virtual ~QObjectPrivate();
  7. // others
  8. }

看看QObject的一个方法

[c-sharp] view plain copy
  1. QString QObject::objectName() const
  2. {
  3. Q_D(const QObject);
  4. return d->objectName;
  5. }

展开后

[c-sharp] view plain copy
  1. QString QObject::objectName() const
  2. {
  3. QObjectPrivate * const d = d_func()
  4. return d->objectName;
  5. }

所以Qt 为我们把从 d_func() 获取 QObjectPrivate 指针的代码给封装起来了,之后就可以直接使用d->

QObject的第二个构造函数使用传入的 QObjectPrivate 对象,但它是 protected 的,也就是说,你不能在外部类中使用这个构造函数。那么这个构造函数有什么用呢?我们来看一下 QWidget 的代码:

[c-sharp] view plain copy
  1. class QWidget : public QObject, public QPaintDevice
  2. {
  3. Q_OBJECT
  4. Q_DECLARE_PRIVATE(QWidget)
  5. // others
  6. }

QWidget 是 QObject 的子类,然后看它的构造函数:

[c-sharp] view plain copy
  1. QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
  2. : QObject(dd, 0), QPaintDevice()
  3. {
  4. Q_D(QWidget);
  5. QT_TRY {
  6. d->init(parent, f);
  7. } QT_CATCH(...) {
  8. QWidgetExceptionCleaner::cleanup(this, d_func());
  9. QT_RETHROW;
  10. }
  11. }

显然了QWidgetPrivate 继承了QObjectPrivate

于是我们已经明白,为什么 QWidget 中找不到 d_ptr 了,因为所有的 d_ptr 都已经在父类 QObject 中定义好了!尝试展开一下 Q_DECLARE_PRIVATE 宏,你就能够发现,它实际上把父类的 QObjectPrivate 指针偷偷地转换成了 QWidgetPrivate 的指针。

因此有如下结论:

1、在基类中定义一个protected权限的基类私有类d_ptr指针;

2、在每个派生类中用本类私有类初始化d_ptr(该私有类需要继承基类私有类),并定义d_func(),获取基类d_ptr,这个d_func()是由     Q_DECLARE_PRIVATE展开得来的 ,并将其转换为当前私有类指针;

3、在函数中使用Q_D,这样就可以使用d了;
4、在私有数据继承体系中,不要忘记将析构函数定义为虚函数,基类析构函数中释放d_ptr,以防内存泄露!!!

============================================================

Q-指针 
   q指针是在私有数据类中使用的,来获取主类指针。

[cpp] view plain copy
  1. class Q_CORE_EXPORT QObjectPrivate : public QObjectData
  2. {
  3. Q_DECLARE_PUBLIC(QObject)
  4. public:
  5. //others...
  6. };

展开后:

[cpp] view plain copy
  1. class Q_CORE_EXPORT QObjectPrivate : public QObjectData
  2. {
  3. inline QObject* q_func() { return static_cast<QObject *>(q_ptr); } /
  4. inline const QObject* q_func() const { return static_cast<const QObject *>(q_ptr); } /
  5. friend class QObject;
  6. //others
  7. }

QObjectData定义如下:

[cpp] view plain copy
  1. QObjectData {
  2. public:
  3. QObject *q_ptr;
  4. //others
  5. }
  6. #define Q_Q(QObject) QObject * const q = q_func()

三、使用的例子:

在使用调色板中

[cpp] view plain copy
  1. void QWidget::setPalette(const QPalette &palette)
  2. {
  3. Q_D(QWidget); //得到私有成员 QWidgetPrivate指针 d
  4. setAttribute(Qt::WA_SetPalette, palette.resolve() != 0);
  5. QPalette naturalPalette = d->naturalWidgetPalette(d->inheritedPaletteResolveMask);
  6. QPalette resolvedPalette = palette.resolve(naturalPalette);
  7. d->setPalette_helper(resolvedPalette); //调用QWidgetPrivate::setPalette_helper()
  8. }
  9. void QWidgetPrivate::setPalette_helper(const QPalette &palette)
  10. {
  11. Q_Q(QWidget);
  12. if (data.pal == palette && data.pal.resolve() == palette.resolve())
  13. return;
  14. data.pal = palette;
  15. updateSystemBackground();
  16. propagatePaletteChange();
  17. updateIsOpaque();
  18. q->update();  //调用QWidget::update()
  19. updateIsOpaque();
  20. }

转载于:https://www.cnblogs.com/senior-engineer/p/8065370.html

解读 Q_D, Q_Q 指针相关推荐

  1. Q_D指针和Q_Q指针

    作用: 信息隐藏:把类头文件中的私有数据成员和方法隐藏起来,只暴露用户需要的接口 二进制兼容:如果程序从一个以前版本的库动态链接到新版本的库之后,能够继续正常运行,而不需要重新编译,那么我们就说这个库 ...

  2. 【Qt】通过QtCreator源码学习Qt(十二):Q_D和Q_Q指针(简称“d指针”)详解

    1.Q_D和Q_Q指针(简称"d指针")简介 参考博客: https://www.devbean.net/2016/11/qt-creator-source-study-07/ h ...

  3. Q_D指针(d指针)和Q_Q指针(q指针)简介

    文章目录 1. Q_D指针 2. Q_Q指针 1. Q_D指针 在class中配合使用 Q_DECLARE_PRIVATE 和 Q_D ,方便获取d指针,d指针指向Class##Private: 2. ...

  4. Qt信息隐藏(Q_D/Q_Q)介绍

    目录: 1:基本介绍与二进制兼容 2:二进制兼容的设计原则 3:常见c++/qt信息隐藏 4:Q_Q,Q_D介绍 5:定制可编辑treewidget与如何访问基类的Private 6:总结 1:基本介 ...

  5. 关于Qt中的Q_D,Q_Q

    扒了下QComboBox的源码,里面使用了大量的Q_D,Q_Q宏,记录一下: #define Q_D(Class) Class##Private * const d = d_func() #defin ...

  6. Q_D以及Q_Q指针理解

    1. d指针 QT wiki D_Pointer 作用 隐藏接口具体实现细节 提高程序编译速度 最大程度实现二进制兼容 二进制兼容动态库:动态链接到库的前一个版本的程序继续与库的新版本一起运行而不需要 ...

  7. 【C 语言】二级指针作为输入 ( 指针数组 | 复杂指针解读 )

    文章目录 一.指针数组 二.复杂指针解读 三.数组指针代码示例 一.指针数组 定义一个数组指针 : 数组中的 元素 是 指向 字符串的指针 , 即 每个数组元素 只有 4 字节 ; char *arr ...

  8. 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  9. C++学习(一五零)qt的公有类、私有类、Q_Q、Q_D、二进制兼容

    1.二进制兼容 如果程序从一个以前版本的库动态链接到新版本的库之后,能够继续正常运行,而不需要重新编译,那么我们就说这个库是二进制兼容的. 如果不能保证库的二进制兼容性,就意味着每次发布库的新版本时, ...

最新文章

  1. php随机生成器加密,php生成随机密码的几种方法
  2. Docker无法修改devicemapper(四)
  3. 【leetcode】3 minstack
  4. 社交搜索Graph Search技术解析
  5. SAP UI5 应用开发教程之四十一 - Chrome 扩展 UI5 Inspector 的离线安装和使用方法试读版
  6. GDI绘图基本步骤总结(经典)
  7. Java 反射机制和动态代理是基于什么原理,了解过吗?
  8. ssh登陆慢/xhell访问主机慢
  9. dos进入mysql不记得密码_windos mysql 忘记密码,无密码登录,重新登录
  10. Swift 获取字符的个数 、字符串的遍历
  11. 鼠标显示效果的形状设置
  12. Linux下安装gcc5.5
  13. TortoiseSVN 安装中文语言包,SVN中文语言包
  14. 【手把手】教你MySQL调优
  15. android反编译修改教程,Android逆向反编译代码注入
  16. 坚持分享的魅力,我超越了当年的榜样
  17. 学计算机要数学吗,学习计算机真的需要数学能力超强吗?
  18. 深度学习神经网络训练调参技巧
  19. python发送图片邮件exchangelib_Python3.5 执行发邮件Exchangelib(=)
  20. 怎样修改游戏服务器里的数据库,修改自己游戏服务器中的数据库

热门文章

  1. 数字在数组中出现的次数
  2. 详解RMQ LCA
  3. shell实例第23讲:每天定时备份nginx日志
  4. STM32外部中断与各通道对应关系
  5. 嵌入式处理器的体系架构与内核详解
  6. jvm:运行时数据区
  7. linux:用户和组
  8. 6、Gerrit插件
  9. 阿里云云计算服务 备案期间服务器免费
  10. 站长们都会,但是都会写错的robots!