彙總

匯總是物件重複使用機制,其中外部物件會公開內部物件的介面,就像是在外部物件本身上實作一樣。 當外部物件將其中一個介面的呼叫委派給內部物件中的相同介面時,這會很有用。 在此案例中,匯總是方便使用,以避免外部對象的額外實作額外負荷。 匯總實際上是內含專案/委派的特殊案例。

匯總幾乎就像實作內含項目一樣簡單,但三個 IUnknown 函式除外:QueryInterfaceAddRef Release。 catch 是從客戶端的觀點來看,外部物件上的任何 IUnknown 函式都必須影響外部物件。 也就是說, AddRefRelease 會影響外部物件, QueryInterface 會公開外部物件上可用的所有介面。 不過,如果外部物件只是將內部物件的介面公開為本身,則透過該介面呼叫的內部物件的 IUnknown 成員的行為會與外部物件介面上的 IUnknown 成員不同,絕對違反管理 IUnknown 的規則和屬性。

解決方案是匯總需要在內部物件上明確實作 IUnknown,並將任何其他介面的 IUnknown 方法委派給外部物件的 IUnknown 方法。

建立可匯總的物件

建立可匯總的對像是選擇性的;不過,這樣做很簡單,而且提供顯著的好處。 下列規則適用於建立可匯總的物件:

下列代碼段說明使用實作介面的巢狀類別方法,正確實作可匯總的物件:

// CSomeObject is an aggregable object that implements 
// IUnknown and ISomeInterface 
class CSomeObject : public IUnknown 
{ 
    private: 
        DWORD        m_cRef;         // Object reference count 
        IUnknown*    m_pUnkOuter;    // Controlling IUnknown, no AddRef 
 
        // Nested class to implement the ISomeInterface interface 
        class CImpSomeInterface : public ISomeInterface 
        { 
            friend class CSomeObject ; 
            private: 
                DWORD    m_cRef;    // Interface ref-count, for debugging 
                IUnknown*    m_pUnkOuter;    // Controlling IUnknown 
            public: 
                CImpSomeInterface() { m_cRef = 0;   }; 
                ~ CImpSomeInterface(void) {}; 
 
                // IUnknown members delegate to the outer unknown 
                // IUnknown members do not control lifetime of object 
                STDMETHODIMP     QueryInterface(REFIID riid, void** ppv) 
                {    return m_pUnkOuter->QueryInterface(riid,ppv);   }; 
 
                STDMETHODIMP_(DWORD) AddRef(void) 
                {    return m_pUnkOuter->AddRef();   }; 
 
                STDMETHODIMP_(DWORD) Release(void) 
                {    return m_pUnkOuter->Release();   }; 
 
                // ISomeInterface members 
                STDMETHODIMP SomeMethod(void) 
                {    return S_OK;   }; 
        } ; 
        CImpSomeInterface m_ImpSomeInterface ; 
    public: 
        CSomeObject(IUnknown * pUnkOuter) 
        { 
            m_cRef=0; 
            // No AddRef necessary if non-NULL as we're aggregated. 
            m_pUnkOuter=pUnkOuter; 
            m_ImpSomeInterface.m_pUnkOuter=pUnkOuter; 
        } ; 
        ~CSomeObject(void) {} ; 
 
        // Static member function for creating new instances (don't use 
        // new directly). Protects against outer objects asking for 
        // interfaces other than Iunknown. 
        static HRESULT Create(IUnknown* pUnkOuter, REFIID riid, void **ppv) 
        { 
            CSomeObject*        pObj; 
            if (pUnkOuter != NULL && riid != IID_IUnknown) 
                return CLASS_E_NOAGGREGATION; 
            pObj = new CSomeObject(pUnkOuter); 
            if (pObj == NULL) 
                return E_OUTOFMEMORY; 
            // Set up the right unknown for delegation (the non-
            // aggregation case) 
            if (pUnkOuter == NULL) 
            {
                pObj->m_pUnkOuter = (IUnknown*)pObj ; 
                pObj->m_ImpSomeInterface.m_pUnkOuter = (IUnknown*)pObj;
            }
            HRESULT hr; 
            if (FAILED(hr = pObj->QueryInterface(riid, (void**)ppv))) 
                delete pObj ; 
            return hr; 
        } 
 
        // Inner IUnknown members, non-delegating 
        // Inner QueryInterface only controls inner object 
        STDMETHODIMP QueryInterface(REFIID riid, void** ppv) 
        { 
            *ppv=NULL; 
            if (riid == IID_IUnknown) 
                *ppv=this; 
            if (riid == IID_ISomeInterface) 
                *ppv=&m_ImpSomeInterface; 
            if (NULL==*ppv) 
                return ResultFromScode(E_NOINTERFACE); 
            ((IUnknown*)*ppv)->AddRef(); 
            return NOERROR; 
        } ; 
        STDMETHODIMP_(DWORD) AddRef(void) 
        {    return ++m_cRef; }; 
        STDMETHODIMP_(DWORD) Release(void) 
        { 
            if (--m_cRef != 0) 
                return m_cRef; 
            delete this; 
            return 0; 
        }; 
}; 
 

匯總物件

開發可匯總物件時,會套用下列規則:

  • 建立內部物件時,外部對象必須明確要求其 IUnknown

  • 外部對象必須保護其 Release 的實作,避免使用其解構碼周圍的人工參考計數重新進入。

  • 如果外部對象查詢任何內部物件介面的指標,外部對象必須呼叫其控制 IUnknownRelease 方法。 為了釋放這個指標,外部物件會呼叫其控制 IUnknownAddRef 方法,後面接著內部物件的指標上的 Release。

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • 外部物件不得盲目地將任何無法辨識介面的查詢委派給內部物件,除非該行為是外部物件的意圖。

內含專案/委派