atl中常見的模板使用手法

1、傳入基類,繼承實現
在設計com接口時,經常會遇到這樣的情況:設計一個基接口,其他多個接口繼承該接口。一個典型的例子是IUnknown接口,所有的com接口必須從IUnknown接口繼承,而這些接口的實現都是相同的,我們不可能爲每一個com接口寫一個IUnknown接口的實現。IUnknown接口的實現比較複雜,分佈在幾個類中(可參考《深入解析atl》)。比較直觀的一個例子是IDispatch接口的實現,通常需要實現IDispatch接口的類都從IDispatchImpl繼承。這個結構是這樣的:

class ATL_NO_VTABLE Cbbb : 
 public IDispatchImpl<Ibbb, &IID_Ibbb, &LIBID_AAAALib>

template <class T, ...>
class ATL_NO_VTABLE IDispatchImpl : public T
                
這樣,每個需要實現IDispatch接口的類都只需要從IDispatchImpl繼承即可。
IDispatchImpl的做法其實相當簡單,僅僅是引入了一個實現類而已。
另一種常見的情況是分離接口和實現。比如有以下的接口繼承結構:
interface IBase
{
    virtual void A()=0;
};
interface IDerive1 : public IBase
{
    virtual void D1()=0;
};
interface IDerive2 : public IBase
{
    virtual void D2()=0;
};
IBase對應的實現類如下:
class CBase : public IBase
{
    virtual void A(){...}
};
這裏假設有一個類CDerive1需要繼承接口IDerive1和實現類CBase,如果直接從兩個類繼承,象這樣:
class CDerive1 : public CBase,public IDerive1
{
public:
    void D1(){}
};
當使用CDerive1類時,編譯器會抱怨模棱兩可。很自然的,考慮到用虛擬繼承解決:
interface IDerive1 : virtual public IBase
{
    virtual void D1()=0;
};
class CBase : virtual public IBase
{

public:
    virtual void A(){...}
};
class CDerive1 : public CBase,public IDerive1
{
public:
    void D1(){}
};
一切看起來都很完美,virtual繼承解決了模棱兩可問題。但是虛擬繼承是很多產生複雜性問題的根源,難以擴展和維護.
看來我們還是隻有老老實實加一箇中間層吧。最終解決方案如下:

template<typename TBase>
class CBase : public TBase
{
public:
    virtual void A(){...}
};
class CDerive1 : public CBase<IDerive1>
{
public:
    void D1(){}
};

2、傳入子類
在atl中,有大量的以下用法:
template<typename T>
class CB
{
public:
    void Fun1()
    {
        T* pT = static_cast<T*>(this);
        pT->FunD(); //調用T的函數
    }
};
這裏傳進來的是CB的子類
class CD : public CB<CD>
{
public:
    void FunD(){...}
};

這種技術實際上有點象虛函數.

以上語句T* pT = static_cast<T*>(this)之所以能編譯通過是因爲模板類CB在編譯期間已被實例化爲CB<CD>,編譯器已經知道T就是CD,而CD繼承自CB,所以從CB向CD轉型是安全的.這種手法的一個好處是不需要一個T類型的對象,直接可以使用this指針安全轉型.其2,避免了虛函數調用的開銷,雖然虛函數的開銷是很小的,單繼承情況下幾乎可以忽略不計,但是虛函數的存在不利於編譯器的優化,而且static和inline虛函數也要出問題.

發佈了10 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章