庫的二進(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();
}