簡體   English   中英

C ++ COM設計。 合成與多重繼承

[英]C++ COM design. Composition vs multiple inheritance

我試圖在我的應用程序(IWebBrowser2)中嵌入瀏覽器控件。 我需要實現IDispatch,IDocHostShowUI,IDocHostUIHandler等以使其工作。 我在純C ++ / Win32 api中執行此操作。 我沒有使用ATL,MFC或任何其他框架。

我有一個名為TWebf的主類,它創建一個Win32窗口來放置瀏覽器控件,並進行使其工作所需的所有OLE調用。 它還通過Refresh(),Back(),Forward()等方法用於控制瀏覽器控件。

現在,這是通過組合實現的。 TWebf具有將所有不同接口(IDispatch,IDocHostShowUI ...)實現為(堆棧分配的)成員的類。 TWebf在其構造函數中所做的第一件事是為所有這些成員提供一個指向其自身的指針( dispatch.webf = this;等等)。 QueryInterface,AddRef和Release在所有接口實現中都作為對TWebf中這些方法的調用來實現(例如,通過調用return webf->QueryInterface(riid, ppv);

我不喜歡TWebf與實現接口的類之間的這種循環依賴關系。 TWebf的TDispatch成員的TWebf成員的...

所以我當時正在考慮使用多重繼承來解決這個問題。 這也將簡化QueryInterface使其始終能夠僅返回this

我想要的UMLish草圖如下所示:(單擊查看大圖)

從uml中可以看出,我想提供所有接口的最小實現,因此我只需要重寫接口中的那些方法,而實際上我想在TWebf中做一些實質性的事情。

我的“多繼承實現”是否可能? 這是個好主意嗎? 這是最好的解決方案嗎?

編輯:

為了將來的討論,這是TWebf中QueryInterface的當前實現。

HRESULT STDMETHODCALLTYPE TWebf::QueryInterface(REFIID riid, void **ppv)
{
    *ppv = NULL;

    if (riid == IID_IUnknown) {
        *ppv = this;
    } else if (riid == IID_IOleClientSite) {
        *ppv = &clientsite;
    } else if (riid == IID_IOleWindow || riid == IID_IOleInPlaceSite) {
        *ppv = &site;
    } else if (riid == IID_IOleInPlaceUIWindow || riid == IID_IOleInPlaceFrame) {
        *ppv = &frame;
    } else if (riid == IID_IDispatch) {
        *ppv = &dispatch;
    } else if (riid == IID_IDocHostUIHandler) {
        *ppv = &uihandler;
    }

    if (*ppv != NULL) {
        AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

編輯2:

我嘗試僅通過幾個接口來實現。 從IUnknown和TOleClientSite繼承TWebf似乎很好,但是當我將TDispatch添加到繼承列表時,它停止工作。

除了warning C4584: 'TWebf' : base-class 'IUnknown' is already a base-class of 'TDispatch'警告warning C4584: 'TWebf' : base-class 'IUnknown' is already a base-class of 'TDispatch'我也遇到運行時錯誤。 運行時錯誤為“訪問沖突讀取位置0x00000000”

由於某些原因,運行時錯誤發生在處理IOleClientSite的行上,而不是IDispatch。 我不知道為什么會這樣,或者是否確實與多重繼承有關。 有任何線索嗎?

編輯3:

QueryInterface的錯誤實現似乎是運行時異常的原因。 正如Mark Ransom正確指出的那樣,在將此指針分配給* ppv之前,必須強制轉換此指針,並且在請求IUnknown時需要特別注意。 請閱讀為什么在具有多重繼承的對象中實現QueryInterface時我到底需要顯式的上位轉換,以得到很好的解釋。

為什么我仍然不知道確切地得到了那個特定的運行時錯誤。

多重繼承是建立COM接口的一種非常常見的方式,因此是可能的。

但是QueryInterface仍必須為每個接口強制轉換指針。 多重繼承的一個有趣的特性是,可以針對每種類類型對指針進行調整-指向IDispatch的指針與指向IDocHostUIHandler的指針不會具有相同的值,即使它們都指向同一對象也是如此。 另外,請確保IUnknown的QueryInterface始終返回相同的指針; 由於所有接口都是從IUnknown派生的,因此,如果嘗試直接將其強制轉換為IUnknown,則會得到模棱兩可的轉換,但這也意味着您可以將任何接口用作IUnknown,只需在父列表中選擇第一個即可。

多重繼承有兩個限制

  1. 如果兩個接口要求實現具有相同名稱/簽名的功能,則不可能使用多重繼承來提供兩種不同的行為。 在某些情況下,您需要相同的實現,但在其他情況下,您不需要。

  2. 您的類的虛擬表上將有多個IUnknown接口,這些接口可能會增加額外的內存使用量。 它們確實共享相同的實現,這很好。

保留組成將是非常容易的。 MI有很多陷阱,例如虛擬繼承,並且遭受了可維護性的極大困擾。 如果您必須將此作為數據成員傳遞給組成的類,那么您做錯了。 如果他們需要訪問其他提供的方法,則應該將其傳遞給方法調用。 由於控制了對組成對象的所有方法調用,因此插入額外的指針應該沒有問題。 這使使用壽命大大縮短,維護和其他操作也變得更加容易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM