Qt的D與Q指針

庫的二進(jìn)制兼容

對于一個已經(jīng)發(fā)布的庫,如果在庫的某個接口類中增加了一個成員,并重新發(fā)布該庫,如果使用該庫的程序直接替換該庫后運(yùn)行時會導(dǎo)致程序崩潰,解決辦法就是重新編譯應(yīng)用程序,這是因?yàn)?,新庫中某些接口類及繼承于它的類內(nèi)存布局都已變化,但是舊的應(yīng)用程序是根據(jù)之前的類內(nèi)存布局,在編譯期間分配好了內(nèi)存,如果不重新編譯程序,那么就會導(dǎo)致訪問一個數(shù)據(jù)時,其可能已被覆蓋,從而導(dǎo)致崩潰。
所以二進(jìn)制兼容是指如果自己的程序使用了第三方模塊,二進(jìn)制兼容可以保證在第三方模塊修改之后,自己的程序可以不用重新編譯就能夠兼容修改后的第三方模塊。

實(shí)現(xiàn)方法

它其實(shí)就是pimpl設(shè)計(jì)模式的具體應(yīng)用。在了解二進(jìn)制兼容實(shí)現(xiàn)方法前,引入兩個概念:主類和私有類。我把二進(jìn)制庫提供給第三方程序的接口類稱為主類,把二進(jìn)制程序內(nèi)部使用的類稱作私有類。只要不修改主類的成員,無論怎么修改二進(jìn)制庫,都可以做到二進(jìn)制兼容。為直觀說明二進(jìn)制兼容的優(yōu)勢,舉個例子,假如我們寫了一個庫,提供了如下的接口:

class Interface{
public:
    Interface(){m_greeting="hello\n";}
    void say(){
        std::cout<<m_greeting;
    }
private:
    std::string m_greeting;
}

接下來我們因?yàn)樾碌男枨螅枰獙爝M(jìn)行修改,修改如下:

class Interface{
public:
    Interface(){m_greeting="hello\n";m_name="mike";}
    void say(){
        std::cout<<m_greeting+m_name;
    }
private:
    std::string m_greeting;
    std::string m_name;
}

這時候,由于主類成員發(fā)生了修改,我們不僅要重新編譯庫,還要重新編譯所有基于這個庫的程序。

接下來我們對上述代碼進(jìn)行重構(gòu),修改成二進(jìn)制兼容模式,二進(jìn)制兼容的實(shí)現(xiàn)方式類似于設(shè)計(jì)模式中的Pimpl,關(guān)鍵點(diǎn)就是在主類中定義一個指向私有類的指針,簡單實(shí)現(xiàn)方式如下:

//導(dǎo)出類 interface.h
class Interface{
public:
    Interface(){m_ptr = new ClassPrivate;}
    ~Interface(){delete m_ptr;}
    void say(){
        if(m_ptr!=nullptr){m_ptr->say();}
    }
private:
    ClassPrivate* m_ptr;
}
//私有類 classprivate.h
class ClassPrivate{
public:
    ClassPrivate(){m_greeting="hello\n";}
    void say(){std::cout<<m_greeting;}
private:
    std::string m_greeting;
}

當(dāng)需求發(fā)生變動,我們只需要作出如下修改:

class ClassPrivate{
public:
    ClassPrivate(){m_greeting="hello\n";m_name="mike";}
    void say(){std::cout<<m_greeting+m_name;}
private:
    std::string m_greeting;
    std::string m_name;
}

因?yàn)槲覀儧]有修改主類Interface,只是對私有類進(jìn)行了修改,因此不需要重新編譯基于該庫的程序,縮短了編譯流程。

好處

私有的結(jié)構(gòu)體可以隨意改變,而不需要重新編譯整個工程項(xiàng)目
隱藏實(shí)現(xiàn)細(xì)節(jié)
頭文件中沒有任何實(shí)現(xiàn)細(xì)節(jié),可以作為API使用
原本在頭文件的實(shí)現(xiàn)部分轉(zhuǎn)移到樂源文件,所以編譯速度有所提高

Qt中Q和D指針實(shí)現(xiàn)

  • d指針是在主類中使用的,主類獲取私有類或類中私有變量的指針
  • q指針是在私有數(shù)據(jù)類中使用的,來獲取主類對象指針
    Q_DECLARE_PRIVATE宏和Q_D宏配合用來在主類中訪問私有類對象
#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_D(Class) Class##Private * const d = d_func()

Q_DECLARE_PUBLIC宏和Q_Q宏配合用來在私有類中訪問主類對象

Q_DECLARE_PUBLIC宏和Q_Q宏
#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;
#define Q_Q(Class) Class * const q = q_func()

如下為一個完整例子:

// 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();
private:
  MyClassPrivate * const d_ptr;
  Q_DECLARE_PRIVATE(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();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文翻譯自 Policies/Binary Compatibility Issues With C++ 二進(jìn)制兼容...
    趙者也閱讀 2,467評論 0 3
  • QT的D指針 相信不少剛開始閱讀Qt源代碼的朋友在看到其中的Private類和諸如Q_D、Q_Q等宏時都會思考,為...
    yangpaul98閱讀 1,529評論 0 1
  • D-Pointer簡介 如果你經(jīng)常閱讀QT的源碼,你會看到大量的Q_D和Q_Q宏.這篇文章將會揭開這些宏的用處.這...
    托尼章閱讀 1,146評論 1 1
  • Qt/QML 編碼規(guī)范 一. 概述 良好的編碼規(guī)范可以大幅提高程序的可讀和可理解性,最終目標(biāo)是實(shí)現(xiàn)更友好的可維護(hù)性...
    趙者也閱讀 2,533評論 0 5
  • 本文翻譯自: https://wiki.qt.io/Coding_Conventions原作者: Qt原文發(fā)布時間...
    Qt君閱讀 408評論 0 0

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