Delen via


Aggregatie

Aggregatie is het mechanisme voor hergebruik van objecten waarin het buitenste object interfaces van het binnenste object beschikbaar maakt alsof ze zijn geïmplementeerd op het buitenste object zelf. Dit is handig wanneer het buitenste object elke aanroep naar een van de interfaces delegeert naar dezelfde interface in het binnenste object. Aggregatie is beschikbaar als gemak om extra implementatieoverhead in dit geval te voorkomen in het buitenste object. Aggregatie is eigenlijk een gespecialiseerd geval van insluiting/delegering.

Aggregatie is bijna net zo eenvoudig te implementeren als insluiting, met uitzondering van de drie IUnknown-functies: QueryInterface, AddRef-en release-. De catch is dat elke functie IUnknown op het buitenste object van invloed moet zijn op het buitenste object. Dat wil gezegd: AddRef en Release van invloed zijn op het buitenste object en QueryInterface alle interfaces beschikbaar maakt op het buitenste object. Als het buitenste object echter simpelweg de interface van een binnenste object als eigen blootstelt, gedragen de IUnknown leden die via die interface worden aangeroepen zich anders dan die IUnknown leden op de interfaces van het buitenste object, een absolute schending van de regels en eigenschappen die IUnknown.

De oplossing is dat aggregatie een expliciete implementatie vereist van IUnknown- op het binnenste object en delegering van de IUnknown methoden van elke andere interface naar de IUnknown methoden van het buitenste object.

Aggregable Objects maken

Het maken van objecten die kunnen worden samengevoegd, is optioneel; het is echter eenvoudig te doen en biedt aanzienlijke voordelen. De volgende regels zijn van toepassing op het maken van een aggregeerbaar object:

  • De implementatie van QueryInterface-, AddRef-en Release- voor de IUnknown interface bepaalt het referentieaantal van het binnenste object en deze implementatie mag niet delegeren aan het onbekende van het buitenste object (de controle IUnknown).
  • De implementatie van QueryInterface-, AddRef-en Release- voor de andere interfaces moet delegeren aan het beheer van IUnknown en mag niet rechtstreeks van invloed zijn op het aantal verwijzingen van het binnenste object.
  • De interne IUnknown- moet Query Interface alleen implementeren voor het binnenste object.
  • Het aggregable object mag geen AddRef- aanroepen bij het vasthouden van een verwijzing naar de besturingsfunctie IUnknown aanwijzer.
  • Wanneer het object wordt gemaakt, moet het maken mislukken met E_NOINTERFACE als er een andere interface dan IUnknown wordt aangevraagd.

Het volgende codefragment illustreert een juiste implementatie van een aggregable object met behulp van de geneste klassemethode voor het implementeren van interfaces:

// 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; 
        }; 
}; 
 

Objecten aggregeren

Bij het ontwikkelen van een aggregable object zijn de volgende regels van toepassing:

  • Bij het maken van het binnenste object moet het buitenste object expliciet om de IUnknownvragen.

  • Het buitenste object moet de implementatie van Release beschermen tegen reentrancy met een kunstmatig referentieaantal rond de vernietigingscode.

  • Het buitenste object moet de besturingsmethode IUnknownRelease aanroepen als er een query wordt uitgevoerd op een verwijzing naar een van de interfaces van het binnenste object. Als u deze aanwijzer wilt vrijmaken, roept het buitenste object de besturingsmethode IUnknownAddRef aan, gevolgd door Release- op de aanwijzer van het binnenste object.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • Het buitenste object mag een query niet blind delegeren voor een niet-herkende interface naar het binnenste object, tenzij dat gedrag specifiek de bedoeling is van het buitenste object.

insluitings-/delegatie-