Agregação

Agregação é o mecanismo de reutilização de objetos no qual o objeto externo expõe interfaces do objeto interno como se fossem implementadas no próprio objeto externo. Isso é útil quando o objeto externo delega cada chamada a uma de suas interfaces para a mesma interface no objeto interno. A agregação está disponível como uma conveniência para evitar sobrecarga de implementação extra no objeto externo nesse caso. A agregação é, na verdade, um caso especializado de contenção/delegação.

A agregação é quase tão simples de implementar quanto a contenção, exceto para as três funções IUnknown: QueryInterface, AddRef e Release. O problema é que, da perspectiva do cliente, qualquer função IUnknown no objeto externo deve afetar o objeto externo. Ou seja, AddRef e Release afetam o objeto externo e QueryInterface expõe todas as interfaces disponíveis no objeto externo. No entanto, se o objeto externo simplesmente expõe a interface de um objeto interno como sua, os membros IUnknown desse objeto interno chamados por meio dessa interface se comportarão de forma diferente daqueles membros IUnknown nas interfaces do objeto externo, uma violação absoluta das regras e propriedades que regem IUnknown.

A solução é que a agregação requer uma implementação explícita de IUnknown no objeto interno e delegação dos métodos IUnknown de qualquer outra interface para os métodos IUnknown do objeto externo.

Criando objetos agregáveis

Criar objetos que podem ser agregados é opcional; No entanto, é simples de fazer e proporciona benefícios significativos. As regras a seguir se aplicam à criação de um objeto agregável:

O fragmento de código a seguir ilustra uma implementação correta de um objeto agregável usando o método de classe aninhada de implementação de 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; 
        }; 
}; 
 

Agregando objetos

Ao desenvolver um objeto agregável, as seguintes regras se aplicam:

  • Ao criar o objeto interno, o objeto externo deve solicitar explicitamente seu IUnknown.

  • O objeto externo deve proteger sua implementação de liberação da reentrância com uma contagem de referência artificial em torno de seu código de destruição.

  • O objeto externo deve chamar seu método de liberação IUnknownde controle se ele consultar um ponteiro para qualquer uma das interfaces do objeto interno. Para liberar esse ponteiro, o objeto externo chama seu método AddRef IUnknownde controle, seguido por Release no ponteiro do objeto interno.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • O objeto externo não deve delegar cegamente uma consulta para qualquer interface não reconhecida ao objeto interno, a menos que esse comportamento seja especificamente a intenção do objeto externo.

Contenção/Delegação