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.
Související témata