我在今年2月份寫了篇《C++中介面與實現分離的技術》的文章,用一個很簡單的例子說明了在C++中介面與實現分離的好處及實現方法。很榮幸,這篇文章被推薦到了CSDN的首頁並被多家網站轉載。
可是當時寫那篇文章的時候,沒有考慮到類與類之間的繼承關係。下面我就來具體的談談這個方面。
還是以上面提到的那篇文章中的例子來說明。
執行類:
lxImplement.h檔案內容:
#include "lxTest.h"
class ClxImplement
{
public:
ClxImplement();
~ClxImplement();
void DoSomething();
private:
ClxTest m_lxTest;
void lxTest();
};
lxImplement.cpp檔案內容:
#include "lxImplement.h"
ClxImplement::ClxImplement()
{
}
ClxImplement::~ClxImplement()
{
}
void ClxImplement::lxTest()
{
m_lxTest.DoSomething();
}
void ClxImplement::DoSomething()
{
lxTest();
}
介面類:
lxExp.h檔案內容:
// 前置聲明
class ClxImplement;
class ClxExp
{
public:
ClxExp();
virtual ~ClxExp();
void DoSomething();
private:
// 聲明一個類ClxImplement的指標,不需要知道類ClxImplement的定義
ClxImplement *m_pImpl;
};
lxExp.cpp檔案內容:
// 在這裡包含類ClxImplement的定義標頭檔
#include "lxImplement.h"
ClxExp::ClxExp()
{
m_pImpl = new ClxImplement;
}
ClxExp::~ClxExp()
{
if (m_pImpl)
delete m_pImpl;
}
void ClxExp::DoSomething()
{
m_pImpl->DoSomething();
}
但是,如果類ClxExp是另一個類的子類,而在類ClxExp中要調用基類的方法,那上面的方案就不行了。比如說,類ClxExp的基類是下面的樣子:
class ClxInF
{
public:
ClxInF();
virtual ~ClxInF();
bool InitSet();
virtual void DoSomething();
};
相應的類ClxExp的聲明變成了如下的形式:
class ClxExp : public ClxInF
{
public:
ClxExp();
virtual ~ClxExp();
void DoSomething();
private:
ClxImplement *m_pImpl;
};
現在, 假設我們必須在類ClxExp的DoSomething()方法中根據InitSet()的傳回值來確定是否執行操作。最簡單的實現方法是把類ClxExp的DoSomething()方法改成下面的樣子:
void ClxExp::DoSomething()
{
if (InitSet())
m_pImpl->DoSomething();
}
可是如果這樣的話,介面與實現就沒有徹底的分離,因為實現細節被暴露到了介面類中。為了避免這種情況發生,我們就必須把對基類ClxInF的方法InitSet()調用放到執行類ClxImplement當中。可是怎麼在執行類ClxImplement當中調用介面類ClxExp的基類ClxInF的方法呢?其實很簡單,因為類ClxExp是類ClxInF的子類,那麼它也就繼承了類ClxInF的方法,只要把類ClxExp的this指標傳給類ClxImplement,就可以通過這個指標來調用類ClxExp的方法,當然也可以調用類ClxExp從基類ClxInF繼承來的方法。下面是修改後的代碼:
lxImplement.h檔案內容:
#include "lxTest.h"
// 包含聲明類ClxExp的標頭檔
#include "lxExp.h"
class ClxImplement
{
public:
// 建構函式,傳入類的ClxExp的指標
ClxImplement(ClxExp *plxExp);
~ClxImplement();
void DoSomething();
private:
ClxTest m_lxTest;
// 定義一個類ClxExp的指標,可以通過該指標調用類ClxExp從基類繼承下來的方法
ClxExp *m_plxExp;
void lxTest();
};
lxImplement.cpp檔案內容:
#include "lxImplement.h"
ClxImplement::ClxImplement(ClxExp *plxExp)
{
m_plxExp = plxExp;
}
ClxImplement::~ClxImplement()
{
}
void ClxImplement::lxTest()
{
m_lxTest.DoSomething();
}
void ClxImplement::DoSomething()
{
if (m_plxExp->InitSet())
lxTest();
}
對於類ClxExp來說,只要修改一下它的建構函式就行了,其他都不用修改。
ClxExp::ClxExp()
{
m_pImpl = new ClxImplement(this);
}
這樣,我們就解決了前面所提到的問題。
當然,也許有人會說,讓類ClxImplement也從類ClxInF繼承不是更簡單嗎?那樣就可以在類ClxImplement中直接調用類ClxInF的方法,也不用添加什麼代碼。可是我們知道公有繼承是的子類與基類是IS-A的關係。也就是說子類是一種基類,就像說轎車是一種汽車一樣。可是,在我們例子中,類ClxImplement只是類ClxExp的一個執行類而已,跟類ClxExp的基類ClxInF沒有一點兒關係,更不要說是一種ClxInF了。所以不能讓類ClxImplement從類ClxInF繼承。