见 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开始看

展开后

  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的构造函数如下:

  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  ;

如下

  1. QObjectData {
  2. public:
  3. virtual ~QObjectData() = 0;
  4. // others
  5. };
  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的一个方法

  1. QString QObject::objectName() const
  2. {
  3. Q_D(const QObject);
  4. return d->objectName;
  5. }

展开后

  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 的代码:

  1. class QWidget : public QObject, public QPaintDevice
  2. {
  3. Q_OBJECT
  4. Q_DECLARE_PRIVATE(QWidget)
  5. // others
  6. }

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

  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指针是在私有数据类中使用的,来获取主类指针。

  1. class Q_CORE_EXPORT QObjectPrivate : public QObjectData
  2. {
  3. Q_DECLARE_PUBLIC(QObject)
  4. public:
  5. //others...
  6. };

展开后:

  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定义如下:

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

三、使用的例子:

在使用调色板中

    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. }

解读 Q_D, Q_Q 指针的更多相关文章

  1. [C/C++]如何解读返回函数指针的函数声明

    今天在看<深入理解C++11>的时候,看到一段有意思的代码: int (*(*pf())())() { return nullptr; } 我立刻就懵了——从来没有见过这样的函数声明.那么 ...

  2. Qt信息隐藏(Q_D/Q_Q)介绍——从二进制兼容讲起

    http://www.cnblogs.com/SkylineSoft/articles/2046404.html

  3. C++指针的概念解读

    C++指针的概念解读 超详细 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区 ...

  4. Netty之缓冲区ByteBuf解读(二)

    上篇介绍了 ByteBuf 的简单读写操作以及读写指针的基本介绍,本文继续对 ByteBuf 的基本操作进行解读. 读写指针回滚 这里的 demo 例子还是使用上节使用的. ByteBuf buf = ...

  5. C/C++中的 if(指针变量) 和 if(!指针变量)

    目录 if(指针变量) 代码演示 if(指针变量) 解读代码 if(!指针变量) 解读代码 总结 替代方案.推荐写法!!!!! if(指针变量) 当把一个指针作为条件表达式时,所要判断的条件实际上就是 ...

  6. Qt之二进制兼容

    一.回顾 使用qt2年多了,但是还是觉得很陌生,总是会被qt搞的很紧张,有时候当我自信满满的打开帮助文档,搜索某个已知的类时,由于笔误敲错了一个字母而出现了另外一个类,不过奇怪的是还真有这么一个类,哎 ...

  7. 30.C++复习篇

    本章学习内容: 1.const 2.指针const 3.inline内联函数 4.函数重载 5.extern “C” 6.new/delete声明与释放 7.namespace命名空间 8.C++中的 ...

  8. Qt中的Q_D宏和d指针

    _ZTS7QObject 一.Q_D的在文件中的提法 Q_D的设置意在方便地获取私有类指针,文件为qglobal.h.下面的##是宏定义的连字符.假设类名是A,那么A##Private翻译过来就是AP ...

  9. 《征服 C 指针》摘录6:解读 C 的声明

    一.混乱的声明——如何自然地理解 C 的声明? 通常,C 的声明 int hoge; 这样,使用“类型 变量名;”的形式进行书写. 可是,像“指向 int 的指针”类型的变量,却要像下面这样进行声明: ...

随机推荐

  1. [EF]vs15+ef6+mysql code first方式

    写在前面 前面有篇文章,尝试了db first方式,但不知道是什么原因一直没有成功,到最后也没解决,今天就尝试下code first的方式. [EF]vs15+ef6+mysql这个问题,你遇到过么? ...

  2. 2.2RNN

    RNN RNN无法回忆起长久的记忆 LSTM (long short Term memory长短期记忆)解决梯度消失或弥散vanishing 和梯度爆炸explosion  0.9*n-->0 ...

  3. TensorFlow softmax的互熵损失

    函数:tf.nn.softmax_cross_entropy_with_logits(logits, labels, name=None) 功能:这个函数的作用是计算 logits 经 softmax ...

  4. Help Jimmy POJ - 1661 数字三角DP

    题意:中文 https://vjudge.net/problem/POJ-1661 题解:设两个dp数组,dpl[i]存 从第i块板左边到地上所花的最短时间,dpr[i]存右边的. 将所有板按高度排序 ...

  5. 5839Special Tetrahedron---hdu5839(计算几何,求特殊四面体个数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5839 给你n个三维的点,然后求这n各点可以构成多少个特殊四面体,特殊四面体满足一下两点: 1.至少有四 ...

  6. java 原子类

    一.基本类原子操作 AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference<V>对boolean,Integer,long,refer ...

  7. Loadrnner 参数化策略

    参数化策略 关键:类型+数据+策略 1.Select next row ( 如何取) 选择下一行 1)Sequential:顺序的 每个VU都从第一行开始,顺序依次向下取值:数据可以循环重复使用:-- ...

  8. git使用简单指南

    参考: https://www.fengerzh.com/git-reset/?utm_source=tool.lu git建库小结 (一)远端:1.在git网站上建设一个远程仓库复制git远程仓库地 ...

  9. mysql中的日志

    关键词:mysql日志,mysql四种日志 一.mysql日志的种类 (1)一般来说,日志有四种,分别为: 1.错误日志:log-err (记录启动,运行,停止mysql时出现的信息) 2.二进制日志 ...

  10. R中双表操作学习[转载]

    转自:https://www.jianshu.com/p/a7af4f6e50c3 1.原始数据 以上是原有的一个,再生成一个新的: > gene_exp_tidy2 <- data.fr ...