Condividi tramite


Aggregazione

L'aggregazione è il meccanismo di riutilizzo degli oggetti in cui l'oggetto esterno espone interfacce dall'oggetto interno come se fossero implementate sull'oggetto esterno stesso. Ciò è utile quando l'oggetto esterno delega ogni chiamata a una delle relative interfacce alla stessa interfaccia nell'oggetto interno. L'aggregazione è disponibile per praticità per evitare un sovraccarico di implementazione aggiuntivo nell'oggetto esterno in questo caso. L'aggregazione è in realtà un caso specializzato di contenimento/delega.

L'aggregazione è quasi semplice da implementare come contenimento, ad eccezione delle tre funzioni IUnknown: QueryInterface, AddRef e Release. Il catch è che dal punto di vista del client, qualsiasi funzione IUnknown sull'oggetto esterno deve influire sull'oggetto esterno. Ovvero, AddRef e Release influiscono sull'oggetto esterno e QueryInterface espone tutte le interfacce disponibili nell'oggetto esterno. Tuttavia, se l'oggetto esterno espone semplicemente l'interfaccia di un oggetto interno come propria, i membri IUnknown dell'oggetto interno chiamati tramite tale interfaccia si comportano in modo diverso rispetto ai membri IUnknown sulle interfacce dell'oggetto esterno, una violazione assoluta delle regole e delle proprietà che regolano IUnknown.

La soluzione consiste nel fatto che l'aggregazione richiede un'implementazione esplicita di IUnknown sull'oggetto interno e la delega dei metodi IUnknown di qualsiasi altra interfaccia ai metodi IUnknown dell'oggetto esterno.

Creazione di oggetti aggregabili

La creazione di oggetti che possono essere aggregati è facoltativa; tuttavia, è semplice da fare e offre vantaggi significativi. Le regole seguenti si applicano alla creazione di un oggetto aggregabile:

Il frammento di codice seguente illustra un'implementazione corretta di un oggetto aggregabile usando il metodo della classe annidata di implementazione delle interfacce:

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

Aggregazione di oggetti

Quando si sviluppa un oggetto aggregabile, si applicano le regole seguenti:

  • Quando si crea l'oggetto interno, l'oggetto esterno deve richiedere esplicitamente il relativo oggetto IUnknown.

  • L'oggetto esterno deve proteggere l'implementazione di Release dalla reentrancy con un conteggio dei riferimenti artificiali intorno al codice di distruzione.

  • L'oggetto esterno deve chiamare il relativo metodo IUnknownRelease di controllo se esegue una query per un puntatore a una qualsiasi delle interfacce dell'oggetto interno. Per liberare questo puntatore, l'oggetto esterno chiama il relativo metodo IUnknownAddRef, seguito da Release sul puntatore dell'oggetto interno.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • L'oggetto esterno non deve delegare in modo cieco una query per qualsiasi interfaccia non riconosciuta all'oggetto interno, a meno che tale comportamento non sia specificamente l'intenzione dell'oggetto esterno.

Contenimento/delega