目录
  1. 1. 解读Q_D和Q_Q指针
    1. 1.0.0.0.0.1. D指针是在主类中使用,来获取私有子类成员指针
    2. 1.0.0.0.0.2. Q指针是在私有数据类中使用,来获取主类对象指针
  2. 1.0.0.0.1. D指针
  • 1.1. Q指针
  • 1.2. 三、使用的例子:
  • 解读Q_D指针和Q_Q指针

    解读Q_D和Q_Q指针

    见qglog.h定义:

    1
    #define Q_D(Class) Class##Private *const d = d_func()
    1
    #define Q_Q(Class) Class * const q = q_func()
    D指针是在主类中使用,来获取私有子类成员指针
    Q指针是在私有数据类中使用,来获取主类对象指针
    D指针

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

    从QObject开始看

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class QObject
    {
    Q_DECLARE_PRIVATE(QObject)
    public:
    Q_INVOLABLE explicit QObject(QObject *parent = 0);
    protected:
    QObject(QObjectPrivate &dd, QObject *parent = 0);
    QScopedPointer<QObjectData>d_ptr;
    }

    展开后

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class QObject
    {
    inline QObjectPrivate *d_func(){return reinterpret_cast(qGetOtrHelper(d_ptr));}
    inline const QObjectPrivate *d_func() const {return reinterpret_cast(qGetPtrHelp(d_ptr));}
    friend class QObjectPrivate;
    public:
    Q_INVOKABLE explicit QObject(QObject *parent = 0);
    protected:
    QObject(QObjectPrivate &dd, QObject *parent = 0);
    QScopedPointer<QObjectData> d_ptr;
    }

    QObject的构造函数如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    QObject::QObject(QObject *parent)
    :d_ptr(new QObjectPrivate)
    {
    //others
    }
    QObject::QObject(QObjectPrivate &dd, QObject *parent)
    :d_ptr(&dd)
    {
    //others
    }

    也就是QObjectData *d_ptr = new QObjectPrivate

    显然QObjectPrivate继承了QObjectData;

    如下

    1
    2
    3
    4
    5
    QObjectData{
    public:
    virtual ~QObjectData() = 0;
    //others
    };
    1
    2
    3
    4
    5
    6
    7
    8
    class Q_CORE_EXPORT QObjectPrivate:public QObjectData
    {
    Q_DECLARE_PUBLIC(QObject)
    public:
    QObjectPrivate(int version = QObjectPrivateVersion);
    virtual ~QObjectPrivate();
    //others
    }

    看看QOject的一个方法

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

    展开后

    1
    2
    3
    4
    5
    QString QObject::objectName() const
    {
    QObjectPrivate *const d = d_func();
    return d->objectName;
    }

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

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

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

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

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

    显然了QWidgetPrivate 继承了QObjectPrivate

    所以在QWidget中找不到dptr了,因为所有的dptr都已经在父类QObject中定义好了。展开QDECLAREPRIVATE宏你就能够发现,它实际上把父类的 QObjectPrivate 指针偷偷地转换成了 QWidgetPrivate 的指针。

    因此有如下结论:
    1、在基类中定义一个protected权限的基类私有类d_ptr指针;

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

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

    Q指针

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

    1
    2
    3
    4
    5
    6
    7

    class Q_CORE_EXPORT QObjectPrivate : public QObjectData
    {
    Q_DECLARE_PUBLIC(QObject)
    public:
    //others...
    };

    展开后

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

    QObjectData定义如下:

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

    三、使用的例子:

    在调色板中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    void QWidget::SetPalette(const QPalette &palette)
    {
    Q_D(QWidget);//得到私有成员QWidgetprivate指针d
    setAttribute(Qt::WA_SetPalette,palette.reolve()!=0);
    QPalette naturalPalette = d->naturalWidetPalette(d->inheritedPaletteResolveMask)
    QPalette resolvedPalette = palette.resolve(naturalPalette);
    d->setPalette_helper(resolvedPalette); //调用QWidgetPrivate::setPalette_helper()
    }
    void QWidgetPrivate::setPalette_helper(const QPalette &palette)
    {
    Q_Q(QWidget);
    if (data.pal == palette && data.pal.resolve() == palette.resolve())
    return;
    data.pal = palette;
    updateSystemBackground();
    propagatePaletteChange();
    updateIsOpaque();
    q->update(); //调用QWidget::update()
    updateIsOpaque();
    }
    文章作者: XyLan
    文章链接: https://blog.xylan.cn/2023/04/26/%E8%A7%A3%E8%AF%BBQ_D%E6%8C%87%E9%92%88%E5%92%8CQ_Q%E6%8C%87%E9%92%88/
    版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 XyLan
    打赏
    • 微信
    • 支付寶