当前位置:   article > 正文

Qt 的 d_ptr (d-pointer) 和 q_ptr (q-pointer)解析_qt中的d指针和

qt中的d指针和

通常情况下,与一个类密切相关的数据会被作为数据成员直接定义在该类中。然而,在某些场合下,我们会将这些数据从该类(被称为公类)分离出来,定义在一个单独的类中(被称为私类)。公类中会定义一个指针,指向私类的对象。在计算机的发展历史中,这种模式被称为pointer to implementation (pimpl)。

Qt常将其命名为d_ptr或者d。Qt文档将其称为d-pointer。与之对应还有q_ptr或者q。

所以,q,d指针并不是多么神秘,它只是运用了pimpl手法,将数据成员放到了另外一个类中。
那么接下来,在了解了pimpl手法后,可以看下面的d,q指针了。

Q_D和Q_Q指针

在一些项目中会遇到这两者,简称 D 指针和 Q 指针。Qt中大量使用Q_D和Q_Q。

Q_D 与 Q_Q宏定义

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

两个宏展开后分别是对 d_func() 和 q_func() 两个函数的调用,返回值分别赋值给 d 和 q 两个指针变量。

Q_DECLARE_PRIVATE与Q_DECLARE_PUBLIC

那么 d_func() 和 q_func()又是什么呢?那就需要看这两个宏的定义

#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;  

#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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里涉及到宏的一些技巧,比如##连接符,\续行符。如果现阶段看不懂没关系,可以看下面的简化版

ClassPrivate* d_func() { return reinterpret_cast<ClassPrivate *>(d_ptr)); } 
  • 1

大致意思,d_func()其实就是d_ptr,只是经过了转型等处理。那么,你就可以理解为d = f_func() = d_ptr(仅限于初学者的初期阶段理解)

然后,代码里面还有一行friend class Class##Private; ,这个就是用来声明友元的,目的是A类可以访问B类的私有成员。这也是实现pimpl的基础。

纸上得来终觉浅,且因笔者的水平,可能会理解不到位,或者不理解。接下来,直接放上代码来看看:


class MyClassPrivate;  //前置声明
class MyClass : public QObject {
 public:
    MyClass(QObject *parent = nullptr);
    virtual ~MyClass();
    void dummyFunc();
 signals : 
    void dummySignal();

 private:
    MyClassPrivate *const d_ptr;
    Q_DECLARE_PRIVATE(MyClass);//①
};

class MyClassPrivate {
 public:
    MyClassPrivate(MyClass *parent) : q_ptr(parent) {}

    void foobar() {
        Q_Q(MyClass);  //声明之后,即将q_func() = q,就可以直接使用q
        emit q->dummySignal();
    }

 private:
    MyClass *const q_ptr;
    Q_DECLARE_PUBLIC(MyClass);//②
};

MyClass::MyClass(QObject *parent)
          :QObject(parent),
          d_ptr(new MyClassPrivate(this)) {
}

MyClass::~MyClass() {
    Q_D(MyClass);  //同Q_Q
    delete d;
}

void MyClass::dummyFunc() {
    Q_D(MyClass);
    d->foobar();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

在这段代码中,公类MyClass定义了一个指针d_ptr来访问私类MyClassPrivate,而私类定义了一个指针q_ptr来访问公类。在这个简单的例子中,公类和私类本可以通过这两个指针非常方便地访问对方的数据。但是,在一些复杂的场合下,这两个指针并不是直接定义在公类、私类中的,而是被定义在它们的基类中。此时,就需要用到Qt定义的4个宏:Q_DECLARE_PRIVATE与Q_DECLARE_PUBLIC、Q_D和Q_Q

Q_DECLARE_PRIVATE(MyClass)作用是在公类中定义了一个成员函数d_func,返回一个指针,指向对应的私类。本例中,公类本身定义了一个指向私类的指针d_ptr,所以Qt应用程序可以直接使用这个指针,而不必调用d_func来获取这个指针。

总之,一方面,Qt在公类中定义了一个指针d_ptr指向私类,在宏Q_DECLARE_PRIVATE中定义了一个函数获取这个指针,用宏Q_D将这个指针重新命名为d,以便于访问私类对象。另一方面,Qt在私类中定义了一个指针q_ptr指向公类,在宏Q_DECLARE_PUBLIC中定义了一个函数获取这个指针,用宏Q_Q将这个指针重新命名为q,以便于访问公类对象。

参考资料
张波 《Qt中的C++技术》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/758814
推荐阅读
相关标签
  

闽ICP备14008679号