Qt中的D指針

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容