Dela via


Aggregering

Sammansättning är mekanismen för återanvändning av objekt där det yttre objektet exponerar gränssnitt från det inre objektet som om de implementerades på själva det yttre objektet. Detta är användbart när det yttre objektet delegerar varje anrop till ett av dess gränssnitt till samma gränssnitt i det inre objektet. Sammansättning är tillgängligt som en bekvämlighet för att undvika extra implementeringskostnader i det yttre objektet i det här fallet. Sammansättning är i själva verket ett specialiserat fall av inneslutning/delegering.

Sammansättning är nästan lika enkelt att implementera som inneslutning är, förutom de tre IUnknown-funktionerna: QueryInterface, AddRefoch Release. Fångsten är att från klientens perspektiv måste alla IUnknown funktion på det yttre objektet påverka det yttre objektet. Det vill AddRef och Release påverka det yttre objektet och QueryInterface exponerar alla gränssnitt som är tillgängliga på det yttre objektet. Men om det yttre objektet bara exponerar ett inre objekts gränssnitt som sitt eget, kommer det inre objektets IUnknown medlemmar som anropas via gränssnittet att bete sig annorlunda än de IUnknown medlemmar i det yttre objektets gränssnitt, en absolut överträdelse av de regler och egenskaper som styr IUnknown.

Lösningen är att aggregering kräver en explicit implementering av IUnknown på det inre objektet och delegering av IUnknown metoder för andra gränssnitt till det yttre objektets IUnknown metoder.

Skapa aggregerbara objekt

Det är valfritt att skapa objekt som kan aggregeras. Det är dock enkelt att göra och ger betydande fördelar. Följande regler gäller för att skapa ett aggregerbart objekt:

  • Det aggregable (eller inre) objektets implementering av QueryInterface, AddRefoch Release för dess IUnknown-gränssnittet styr det inre objektets referensantal och den här implementeringen får inte delegeras till det yttre objektets okända (det kontrollerande IUnknown).
  • Det aggregerbara (eller inre) objektets implementering av QueryInterface, AddRefoch Release för dess andra gränssnitt måste delegeras till den kontrollerande IUnknown och får inte direkt påverka det inre objektets referensantal.
  • Den inre IUnknown måste endast implementera QueryInterface- för det inre objektet.
  • Det aggregerbara objektet får inte anropa AddRef- när du håller en referens till den kontrollerande IUnknown pekaren.
  • När objektet skapas, om något annat gränssnitt än IUnknown begärs, måste skapandet misslyckas med E_NOINTERFACE.

Följande kodfragment illustrerar en korrekt implementering av ett aggregerbart objekt med hjälp av den kapslade klassmetoden för att implementera gränssnitt:

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

Aggregera objekt

När du utvecklar ett aggregerbart objekt gäller följande regler:

  • När du skapar det inre objektet måste det yttre objektet uttryckligen be om dess IUnknown-.

  • Det yttre objektet måste skydda sin implementering av Release från återaktivering med ett artificiellt referensantal runt dess destruktionskod.

  • Det yttre objektet måste anropa dess kontrollmetod IUnknownRelease om den frågar efter en pekare till något av det inre objektets gränssnitt. För att frigöra den här pekaren anropar det yttre objektet dess kontrollmetod IUnknownAddRef följt av Release på det inre objektets pekare.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • Det yttre objektet får inte blint delegera en fråga för något okänt gränssnitt till det inre objektet, såvida inte det beteendet specifikt är avsikten med det yttre objektet.

inneslutning/delegering