Compartir a través de


Agregación

La agregación es el mecanismo de reutilización de objetos en el que el objeto externo expone interfaces del objeto interno como si se implementaran en el propio objeto externo. Esto resulta útil cuando el objeto externo delega cada llamada a una de sus interfaces a la misma interfaz del objeto interno. La agregación está disponible como comodidad para evitar sobrecargas de implementación adicionales en el objeto externo en este caso. La agregación es en realidad un caso especializado de contención o delegación.

La agregación es casi tan sencilla de implementar como la contención, excepto las tres funciones IUnknown : QueryInterface, AddRef y Release. La captura es que, desde la perspectiva del cliente, cualquier función IUnknown del objeto externo debe afectar al objeto externo. Es decir, AddRef y Release afectan al objeto externo y QueryInterface expone todas las interfaces disponibles en el objeto externo. Sin embargo, si el objeto externo simplemente expone la interfaz de un objeto interno como su propio, los miembros IUnknown del objeto interno llamados a través de esa interfaz se comportarán de forma diferente a los miembros IUnknown en las interfaces del objeto externo, una infracción absoluta de las reglas y propiedades que rigen IUnknown.

La solución es que la agregación requiere una implementación explícita de IUnknown en el objeto interno y la delegación de los métodos IUnknown de cualquier otra interfaz a los métodos IUnknown del objeto externo.

Creación de objetos agregados

La creación de objetos que se pueden agregar es opcional; sin embargo, es fácil de hacer y proporciona importantes ventajas. Las reglas siguientes se aplican a la creación de un objeto agregado:

  • La implementación del objeto agregado (o interno) de QueryInterface, AddRef y Release para su interfaz IUnknown controla el recuento de referencias del objeto interno, y esta implementación no debe delegar en el desconocido del objeto externo (el control IUnknown).
  • La implementación del objeto agregado (o interno) de QueryInterface, AddRef y Release para sus otras interfaces debe delegar en el control IUnknown y no debe afectar directamente al recuento de referencias del objeto interno.
  • El IUnknown interno debe implementar QueryInterface solo para el objeto interno.
  • El objeto agregado no debe llamar a AddRef al mantener una referencia al puntero IUnknown de control.
  • Cuando se crea el objeto, si se solicita alguna interfaz distinta de IUnknown , se debe producir un error en la creación con E_NOINTERFACE.

En el fragmento de código siguiente se muestra una implementación correcta de un objeto agregado mediante el método de clase anidada para implementar 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; 
        }; 
}; 
 

Agregar objetos

Al desarrollar un objeto agregado, se aplican las reglas siguientes:

  • Al crear el objeto interno, el objeto externo debe pedir explícitamente su IUnknown.

  • El objeto externo debe proteger su implementación de Release de la reentrada con un recuento de referencias artificial alrededor de su código de destrucción.

  • El objeto externo debe llamar a su método IUnknownRelease controlling si consulta un puntero a cualquiera de las interfaces del objeto interno. Para liberar este puntero, el objeto externo llama a su método IUnknownAddRef de control, seguido de Release en el puntero del objeto interno.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • El objeto externo no debe delegar ciegamente una consulta para cualquier interfaz no reconocida al objeto interno, a menos que ese comportamiento sea específicamente la intención del objeto externo.

Contención/delegación