QT的D指針
相信不少剛開(kāi)始閱讀Qt源代碼的朋友在看到其中的Private類和諸如Q_D、Q_Q等宏時(shí)都會(huì)思考,為什么Qt要用這樣一個(gè)設(shè)計(jì)模式呢?這樣一段增加了不少?gòu)?fù)雜度的代碼,到底有多大的好處呢?簡(jiǎn)單的說(shuō),這樣的好處在于保證代碼的二進(jìn)制兼容性。
什么是二進(jìn)制兼容性?大名鼎鼎的KDE項(xiàng)目是這樣介紹的:一個(gè)庫(kù)是二進(jìn)制兼容的,如果一個(gè)程序和某個(gè)庫(kù)的某個(gè)版本動(dòng)態(tài)鏈接,并且不需要重新編譯,即可在安裝有該庫(kù)較新版本的環(huán)境中運(yùn)行。為什么要保證二進(jìn)制兼容性?如果不能保證庫(kù)的二進(jìn)制兼容性,就意味著每次發(fā)布新版本時(shí),依賴該庫(kù)的所有程序都必須重新編譯才能正常運(yùn)行。顯然,這對(duì)于像Qt這樣被廣泛采用的庫(kù)而言是完全不可接受的。關(guān)于二進(jìn)制兼容性的更多信息,感興趣的朋友可以參考下KDE TechBase上的這篇文章,這里就不羅嗦了,僅僅和大家分享下具體的使用。
本質(zhì)上就是讓public的類只有public的方法。而把其成員定位為指針。指針的大小不變,這樣,對(duì)外公開(kāi)的class就是不變的。變化 的只是private的類。
如果不使用D指針,那我們可能會(huì)有如下的一個(gè)類聲明:
class MyClass
{
public:
MyClass();
~MyClass();
private:
int myVar;
};
顯然,這里的私有成員myVar是保證代碼二進(jìn)制兼容性的大敵,所以我們需要使用D指針,改寫(xiě)這個(gè)類:
class MyClassPrivate;
class MyClass
{
public:
MyClass();
~MyClass();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
};
這里,我們定義了一個(gè)指針d_ptr指向私有實(shí)現(xiàn)類,然后用Q_DECLARE_PRIVATE宏來(lái)定義一些輔助函數(shù)和聲明友元類:
#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;
然后這個(gè)私有類的實(shí)現(xiàn)如下所示:
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent);
private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
int myVar;
};
這里的q_ptr指針就是指向公開(kāi)的接口了,然后Q_DECLARE_PUBLIC宏則定義了輔助函數(shù)并聲明了友元類:
#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;
而我們還可以用Q_D和Q_Q兩個(gè)宏來(lái)進(jìn)一步簡(jiǎn)化訪問(wèn):
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
這就是Qt中D指針/私有實(shí)現(xiàn)的最基本使用方法。最后用一個(gè)比較完整的例子作為結(jié)尾
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QtCore/QObject>
class MyClassPrivate;
class MyClass: public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
virtual ~MyClass();
void dummyFunc();
signal:
void dummySignal();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
Q_DISABLE_COPY(MyClass);
};
#endif // MYCLASS_H
// myclass.cpp
#include "myclass.h"
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent)
: q_ptr(parent)
{
}
void foobar()
{
Q_Q(MyClass);
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);
delete d;
}
void MyClass::dummyFunc()
{
Q_D(MyClass);
d->foobar();
}
參考資料:
http://xizhizhu.blogspot.com/2010/11/beauty-of-qt-1-d-pointer-private.html
https://blog.csdn.net/zhu_xz/article/details/6035861