Sdílet prostřednictvím


Agregace

Agregace je mechanismus opětovného použití objektu, ve kterém vnější objekt zveřejňuje rozhraní vnitřního objektu, jako by byly implementovány na samotném vnějším objektu. To je užitečné, když vnější objekt deleguje každé volání jednoho z jeho rozhraní na stejné rozhraní ve vnitřním objektu. Agregace je k dispozici jako pohodlí, abyste se vyhnuli nadbytečné režii implementace ve vnějším objektu v tomto případě. Agregace je ve skutečnosti specializovaný případ zahrnutí/delegování.

Agregace je téměř stejně jednoduchá jako zahrnutí, s výjimkou tří funkcí IUnknown: QueryInterface, AddRefa Release. Catch je, že z pohledu klienta musí všechny IUnknown funkce na vnějším objektu ovlivnit vnější objekt. To znamená, AddRef a release ovlivnit vnější objekt a QueryInterface zveřejňuje všechna rozhraní dostupná na vnějším objektu. Pokud však vnější objekt jednoduše zpřístupňuje rozhraní vnitřního objektu jako vlastní, IUnknown členy volaného prostřednictvím tohoto rozhraní se budou chovat jinak než ty, IUnknown členy na rozhraní vnějšího objektu, absolutní porušení pravidel a vlastností, které řídí IUnknown.

Řešení je, že agregace vyžaduje explicitní implementaci IUnknown na vnitřní objekt a delegování IUnknown metod jakéhokoli jiného rozhraní na metody IUnknown.

Vytváření agregovatelných objektů

Vytváření objektů, které lze agregovat, je volitelné; je však snadné to udělat a poskytuje významné výhody. Pro vytvoření agregovatelného objektu platí následující pravidla:

  • Agregovatelná (nebo vnitřní) implementace objektu QueryInterface, AddRefa Release pro jeho IUnknown rozhraní řídí počet odkazů vnitřního objektu, a tato implementace nesmí delegovat na neznámý vnější objekt (ovládací IUnknown).
  • Agregovatelná (nebo vnitřní) implementace objektu QueryInterface, AddRefa release pro ostatní rozhraní musí delegovat na řídicí IUnknown a nesmí přímo ovlivnit počet odkazů vnitřního objektu.
  • Vnitřní IUnknown musí implementovat QueryInterface pouze pro vnitřní objekt.
  • Agregovatelný objekt nesmí volat AddRef při podržení odkazu na řídicí IUnknown ukazatel.
  • Při vytvoření objektu, pokud je požadováno jakékoli jiné rozhraní než IUnknown, musí vytvoření selhat s E_NOINTERFACE.

Následující fragment kódu znázorňuje správnou implementaci agregovatelného objektu pomocí vnořené metody třídy implementace rozhraní:

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

Agregace objektů

Při vývoji agregovatelného objektu platí následující pravidla:

  • Při vytváření vnitřního objektu musí vnější objekt explicitně požádat o jeho IUnknown.

  • Vnější objekt musí chránit svou implementaci release před opakováním s použitím počtu umělých odkazů kolem kódu zničení.

  • Vnější objekt musí volat své řízení IUnknownRelease metoda, pokud se dotazuje na ukazatel na jakékoli rozhraní vnitřního objektu. Chcete-li uvolnit tento ukazatel, vnější objekt volá své řízení IUnknownAddRef metoda, následované Release ukazatele vnitřního objektu.

    // Obtaining inner object interface pointer 
    pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); 
    pUnkOuter->Release(); 
    
    // Releasing inner object interface pointer 
    pUnkOuter->AddRef(); 
    pISomeInterface->Release(); 
    
    
  • Vnější objekt nesmí nevidomě delegovat dotaz na jakékoli nerozpoznané rozhraní na vnitřní objekt, pokud toto chování není výslovně záměrem vnějšího objektu.

zahrnutí nebo delegování