집합체
집계는 외부 개체가 외부 개체 자체에서 구현된 것처럼 내부 개체의 인터페이스를 노출하는 개체 재사용 메커니즘입니다. 이는 외부 개체가 해당 인터페이스 중 하나에 대한 모든 호출을 내부 개체의 동일한 인터페이스에 위임할 때 유용합니다. 이 경우 외부 개체에서 추가 구현 오버헤드를 방지하기 위해 집계를 편리하게 사용할 수 있습니다. 집계는 실제로 포함/위임특수한 경우입니다.
집계는 IUnknown 함수인 QueryInterface, AddRef및 릴리스제외하고 포함으로 구현하는 것이 거의 간단합니다. 클라이언트의 관점에서 볼 때 외부 개체의 IUnknown 함수는 외부 개체에 영향을 주어야 합니다. 즉, AddRef 및 Release 외부 개체에 영향을 QueryInterface 외부 개체에서 사용할 수 있는 모든 인터페이스를 노출할 있습니다. 그러나 외부 개체가 단순히 내부 개체의 인터페이스를 자체적으로 노출하는 경우 해당 인터페이스를 통해 호출된 내부 개체의 IUnknown 멤버는 외부 개체의 인터페이스에서 IUnknown 멤버를 것과 다르게 동작합니다. 이는 IUnknown 제어하는 규칙 및 속성을 절대 위반하는 것입니다.
이 솔루션은 집계를 수행하려면 내부 개체에 대한 IUnknown 명시적 구현과 외부 개체의 IUnknown 메서드에 대한 다른 인터페이스의 IUnknown 메서드 위임이 필요합니다.
집계 가능한 개체 만들기
집계할 수 있는 개체 만들기는 선택 사항입니다. 그러나 간단하게 수행할 수 있으며 상당한 이점을 제공합니다. 다음 규칙은 집계 가능한 개체를 만드는 데 적용됩니다.
- 집계 가능(또는 내부) 개체의 QueryInterface, AddRef및 ReleaseIUnknown 인터페이스의 구현은 내부 개체의 참조 수를 제어하며, 이 구현은 외부 개체의 알 수 없음(IUnknown제어)에 위임해서는 안 됩니다.
- 집계(또는 내부) 개체의 QueryInterface구현, AddRef및 다른 인터페이스에 대한 릴리스 제어 IUnknown 위임해야 하며 내부 개체의 참조 수에 직접적인 영향을 미치지 않아야 합니다.
- 내부 IUnknown 내부 개체에 대해서만 QueryInterface 구현해야 합니다.
- 집계 가능한 개체는 제어 IUnknown 포인터에 대한 참조를 보유할 때 AddRef 호출해서는 안 됩니다.
- 개체를 만들 때 IUnknown 이외의 인터페이스가 요청되면 E_NOINTERFACE 함께 생성이 실패해야 합니다.
다음 코드 조각에서는 인터페이스를 구현하는 중첩된 클래스 메서드를 사용하여 집계 가능한 개체를 올바르게 구현하는 방법을 보여 줍니다.
// 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;
};
};
개체 집계
집계 가능한 개체를 개발할 때 다음 규칙이 적용됩니다.
외부 개체는 Release 구현을 소멸 코드에 대한 인공 참조 횟수로 재진입하지 않도록 보호해야 합니다.
외부 개체는 내부 개체의 인터페이스에 대한 포인터를 쿼리하는 경우 IUnknown Release 메서드를 제어하는호출해야 합니다. 이 포인터를 해제하기 위해 외부 개체는 제어 IUnknownAddRef 메서드를 호출한 다음, 내부 개체의 포인터에서 Release 호출합니다.
// Obtaining inner object interface pointer pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); pUnkOuter->Release(); // Releasing inner object interface pointer pUnkOuter->AddRef(); pISomeInterface->Release();
외부 개체는 인식할 수 없는 인터페이스에 대한 쿼리를 내부 개체에 맹목적으로 위임해서는 안 되며, 이 동작이 특히 외부 개체의 의도인 경우가 아니면 안됩니다.
관련 항목