我們先來(lái)看一組接口定義:
struct IX { virtual void MethodX() = 0; }; struct IXA : public IX { virtual void MethodXA() = 0; }; struct IXB : public IX { virtual void MethodXB() = 0; }; struct IXAB : public IXA, public IXB { virtual void MethodXAB() = 0; }; |
然后我們來(lái)實(shí)現(xiàn) IX:
struct CX : public IX { virtual void MethodX() { } }; |
然后再來(lái)實(shí)現(xiàn) IXA:
struct CXA : public IXA { virtual void MethodX() { } virtual void MethodXA() { } }; |
這樣寫(xiě)當(dāng)然是沒(méi)問(wèn)題的。但是,有木有發(fā)現(xiàn),這里出現(xiàn)MethodX了。如果,我們需要之前CX中對(duì)MethodX的實(shí)現(xiàn),那怎么辦呢?
一開(kāi)始我傻傻地想,繼承CX不就好了么:
struct CXA : public IXA, public CX { virtual void MethodXA() { } }; |
結(jié)果實(shí)例化CXA的時(shí)候報(bào)錯(cuò):
error C2259: 'CXA' : cannot instantiate abstract class
due to following members:
void IX::MethodX(void)' : is abstract
see declaration of 'IX::MethodX'
(嘿嘿。我不知道是不是挺多人一開(kāi)始都會(huì)這么想的。)究其原因,是因?yàn)樘砑佣嗬^承不會(huì)產(chǎn)生覆蓋虛函數(shù)的效果。此時(shí)虛函數(shù)的狀態(tài)是:
MethodX | 來(lái)自于IXA | 來(lái)自于IX | 未實(shí)現(xiàn) |
MethodXA | 來(lái)自于IXA | | CXA實(shí)現(xiàn) |
MethodX | 來(lái)自于CX | 來(lái)自于IX | CX實(shí)現(xiàn) |
新添加的CX中的MethodX,只是覆蓋了CX多繼承的IX中的那個(gè)MethodX。退一步,即便CX是個(gè)獨(dú)立的類,沒(méi)有繼承于IX,CX中的MethoxX也無(wú)法為覆蓋IXA中的MethodX。只有處于同一繼承鏈中的各個(gè)類才會(huì)產(chǎn)生覆蓋虛函數(shù)的效果。
假如,CX就是繼承于IXA的,且只實(shí)現(xiàn)了MethodX,未實(shí)現(xiàn)MethodA:
struct CX : public IXA { virtual void MethodX() { } }; |
然后CXA不再繼承IXA,而是直接繼承CX,補(bǔ)充實(shí)現(xiàn)MethodA:
struct CXA : public CX { virtual void MethodXA() { } }; |
這樣,CXA是可以直接實(shí)例化的。但是,CX又無(wú)法單獨(dú)使用了!
CX面臨的問(wèn)題是,需要隨時(shí)被改變基類,也就是基類類型可配置。于是我們想到了模版:
template <typename T = IX> struct CX : public T { virtual void MethodX() { } }; struct CXA : public CX<IXA> { virtual void MethodXA() { } }; |
CX單獨(dú)使用的時(shí)候,使用默認(rèn)模版參數(shù),讓它繼承于IX;被CXA使用的時(shí)候,讓它繼承于IXA,問(wèn)題就解決了!同樣的方法,我們實(shí)現(xiàn)CXB:
struct CXB : public CX<IXB> { virtual void MethodXB() { } }; |
下面我們來(lái)實(shí)現(xiàn)IXAB:
struct CXAB : public IXAB { virtual void MethodX() { } virtual void MethodXA() { } virtual void MethodXB() { } virtual void MethodXAB() { } }; |
注意到這里只有一個(gè)MethodX。由于MethodX對(duì)IXA、IXB是同源的,IXAB里面雖然本應(yīng)該有兩個(gè)MethodX,但編譯器進(jìn)行合并處理,對(duì)其中一個(gè)做跳轉(zhuǎn),所以我們只需要實(shí)現(xiàn)一次就夠了。(目前我是這樣理解的。)不過(guò)這不是本文要探討的內(nèi)容,我們關(guān)注的是,MethodX、MethodXA、MethodXB又要重復(fù)寫(xiě)一遍了。
看來(lái)我們要對(duì)CXA、CXB也做模版處理:
template <typename T = IXA> struct CXA : public CX<T> { virtual void MethodXA() { } }; template <typename T = IXB> struct CXB : public CX<T> { virtual void MethodXB() { } }; struct CXAB : public CXA<IXAB>, public CXB<IXAB> { virtual void MethodXAB() { } }; |
可惜的是,還是有錯(cuò)誤:
error C2259: 'CXAB' : cannot instantiate abstract class
due to following members:
'void IXB::MethodXB(void)' : is abstract
see declaration of 'IXB::MethodXB'
'void IXA::MethodXA(void)' : is abstract
see declaration of 'IXA::MethodXA'
說(shuō)的是CXA<IXAB>中的MethodXB未實(shí)現(xiàn),CXB<IXAB>中的MethodXA未實(shí)現(xiàn)……所以,以上方案不適合接口中存在多繼承的情形。
恰好COM中貌似也沒(méi)有接口多繼承的情形。我們將CX改名為IXImpl,將CXA改名為IXAImpl,將CXB改名為IXBImpl:
struct IX { virtual void MethodX() = 0; }; struct IXA : public IX { virtual void MethodXA() = 0; }; struct IXB : public IX { virtual void MethodXB() = 0; }; template <typename T = IX> struct IXImpl : public T { virtual void MethodX() { } }; template <typename T = IXA> struct IXAImpl : public IXImpl<T> { virtual void MethodXA() { } }; template <typename T = IXB> struct IXBImpl : public IXImpl<T> { virtual void MethodXB() { } }; |
是不是跟ATL對(duì)上號(hào)了?這些*Impl類可以直接使用(如果是需要默認(rèn)實(shí)現(xiàn)就夠了),也可以被繼承,被繼承的時(shí)候也可以更換基類。如:
struct CXA : public IXAImpl<> { }; |
struct IY : public IXB { virtual void MethodY() = 0; }; struct CY : public IXBImpl<IY> { virtual void MethodY() { } }; |
第二個(gè)例子中,IXB是庫(kù)提供的接口,IY是應(yīng)用程序設(shè)計(jì)的接口,CY是應(yīng)用程序?qū)崿F(xiàn)的類。
好了,ATL中的*Impl類的做法就是如此,只是多了其他一些模版參數(shù),暫時(shí)不理它們。
posted on 2012-09-02 13:56
溪流 閱讀(2936)
評(píng)論(1) 編輯 收藏 引用 所屬分類:
C++ 、
Windows 、
COM