Freigeben über


Aggregation

Aggregation ist der Wiederverwendungsmechanismus des Objekts, bei dem das äußere Objekt Schnittstellen aus dem inneren Objekt verfügbar macht, als ob sie für das äußere Objekt selbst implementiert wurden. Dies ist nützlich, wenn das äußere Objekt jeden Aufruf an eine seiner Schnittstellen an dieselbe Schnittstelle im inneren Objekt delegiert. Aggregation ist als Komfort verfügbar, um zusätzlichen Implementierungsaufwand im äußeren Objekt in diesem Fall zu vermeiden. Aggregation ist tatsächlich ein spezieller Fall von Eindämmung/Delegierung.

Aggregation ist fast so einfach wie ein Einschluss zu implementieren, mit Ausnahme der drei IUnknown Funktionen: QueryInterface, AddRefund Release. Aus sicht des Clients muss jede IUnknown--Funktion des äußeren Objekts das äußere Objekt beeinflussen. Das heißt, AddRef und Release wirken sich auf das äußere Objekt aus und QueryInterface macht alle Schnittstellen verfügbar, die für das äußere Objekt verfügbar sind. Wenn das äußere Objekt jedoch einfach die Schnittstelle eines inneren Objekts als eigene verfügbar macht, verhält sich das innere Objekt IUnknown Elemente, die über diese Schnittstelle aufgerufen werden, anders als die IUnknown Member auf den Schnittstellen des äußeren Objekts, ein absoluter Verstoß gegen die Regeln und Eigenschaften für IUnknown.

Die Lösung besteht darin, dass aggregation eine explizite Implementierung von IUnknown- für das innere Objekt und die Delegierung der IUnknown Methoden einer anderen Schnittstelle für die IUnknown Methoden des äußeren Objekts erfordert.

Erstellen von Aggregable-Objekten

Das Erstellen von Objekten, die aggregiert werden können, ist optional; Es ist jedoch einfach zu tun und bietet erhebliche Vorteile. Die folgenden Regeln gelten für das Erstellen eines aggregablen Objekts:

  • Die Implementierung des QueryInterface, AddRefund Release für die IUnknown Schnittstelle steuert die Referenzanzahl des inneren Objekts, und diese Implementierung darf nicht an das unbekannte äußere Objekt delegieren (das Steuerelement IUnknown).
  • Die Implementierung des QueryInterface-,AddRef-und Release- für die anderen Schnittstellen muss an die steuerungsfähige IUnknown- delegiert werden und darf sich nicht direkt auf die Referenzanzahl des inneren Objekts auswirken.
  • Die innere IUnknown- muss QueryInterface- nur für das innere Objekt implementieren.
  • Das aggregable -Objekt darf AddRef- nicht aufrufen, wenn ein Verweis auf das Steuern IUnknown Zeiger gehalten wird.
  • Wenn das Objekt erstellt wird, wenn eine andere Schnittstelle als IUnknown- angefordert wird, muss die Erstellung mit E_NOINTERFACE fehlschlagen.

Das folgende Codefragment veranschaulicht eine korrekte Implementierung eines aggregablen Objekts mithilfe der geschachtelten Klassenmethode der Implementierung von Schnittstellen:

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

Aggregieren von Objekten

Beim Entwickeln eines aggregablen Objekts gelten die folgenden Regeln:

  • Beim Erstellen des inneren Objekts muss das äußere Objekt explizit die IUnknown-anfordern.

  • Das äußere Objekt muss seine Implementierung von Release vor der Reentranz mit einer künstlichen Referenzanzahl um seinen Zerstörungscode schützen.

  • Das äußere Objekt muss seine Steuerung IUnknownRelease-Methode aufrufen, wenn es einen Zeiger auf eine der Schnittstellen des inneren Objekts abfragt. Um diesen Zeiger freizugeben, ruft das äußere Objekt die Steuerung IUnknownAddRef--Methode auf, gefolgt von Release für den Zeiger des inneren Objekts.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • Das äußere Objekt darf eine Abfrage nicht blind für eine unbekannte Schnittstelle an das innere Objekt delegieren, es sei denn, dieses Verhalten ist ausdrücklich die Absicht des äußeren Objekts.

Eindämmung/Delegierung