赞
踩
通常情况下,与一个类密切相关的数据会被作为数据成员直接定义在该类中。然而,在某些场合下,我们会将这些数据从该类(被称为公类)分离出来,定义在一个单独的类中(被称为私类)。公类中会定义一个指针,指向私类的对象。在计算机的发展历史中,这种模式被称为pointer to implementation (pimpl)。
Qt常将其命名为d_ptr或者d。Qt文档将其称为d-pointer。与之对应还有q_ptr或者q。
所以,q,d指针并不是多么神秘,它只是运用了pimpl手法,将数据成员放到了另外一个类中。
那么接下来,在了解了pimpl手法后,可以看下面的d,q指针了。
在一些项目中会遇到这两者,简称 D 指针和 Q 指针。Qt中大量使用Q_D和Q_Q。
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
两个宏展开后分别是对 d_func() 和 q_func() 两个函数的调用,返回值分别赋值给 d 和 q 两个指针变量。
那么 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;
这里涉及到宏的一些技巧,比如##
连接符,\
续行符。如果现阶段看不懂没关系,可以看下面的简化版
ClassPrivate* d_func() { return reinterpret_cast<ClassPrivate *>(d_ptr)); }
大致意思,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(); }
在这段代码中,公类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++技术》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。