集合体
集計は、外側のオブジェクトが外側のオブジェクト自体に実装されているかのように内部オブジェクトからインターフェイスを公開するオブジェクト再利用メカニズムです。 これは、外側のオブジェクトが、そのインターフェイスの 1 つに対するすべての呼び出しを内部オブジェクト内の同じインターフェイスに委任する場合に便利です。 この場合、外部オブジェクトでの追加の実装オーバーヘッドを回避するために、集計を便利に使用できます。 集計は、実際には 包含/委任の特殊なケースです。
集約は、QueryInterface、AddRef、Releaseの 3 つの IUnknown 関数を除いて、包含と同じくらい簡単に実装できます。 catch は、クライアントの観点から、外部オブジェクトの IUnknown 関数 外部オブジェクトに影響を与える必要があるということです。 つまり、AddRef と Release 外部オブジェクトに影響し、QueryInterface 外部オブジェクトで使用可能なすべてのインターフェイスが公開されます。 ただし、外側のオブジェクトが単に内部オブジェクトのインターフェイスをそれ自体として公開している場合、そのインターフェイスを介して呼び出された内部オブジェクトの IUnknown メンバーは、外部オブジェクトのインターフェイス上の IUnknown メンバー とは異なる動作をします。IUnknown を制御する規則とプロパティ絶対違反になります。
この解決策は、集計には、内部オブジェクト IUnknown を明示的に実装し、他のインターフェイスの IUnknown メソッドを外部オブジェクトの IUnknown メソッドに委任する必要があるということです。
Aggregable オブジェクトの作成
集計できるオブジェクトの作成は省略可能です。ただし、簡単に実行でき、大きな利点があります。 次の規則は、aggregable オブジェクトの作成に適用されます。
- IUnknown インターフェイスの QueryInterface、AddRef、および Release の aggregable (または内部) オブジェクトの実装は、内部オブジェクトの参照カウントを制御します。この実装は、外部オブジェクトの不明 (制御 IUnknown) に委任してはなりません。
- aggregable (または内部) オブジェクトの QueryInterface、AddRef、および他のインターフェイスの Release の実装は、制御 IUnknown に委任する必要があり、内部オブジェクトの参照カウントに直接影響を与えてはなりません。
- 内部 IUnknown は、内部オブジェクト 対してのみ QueryInterface を実装する必要があります。
- 制御 IUnknown ポインターへの参照を保持している場合、aggregable オブジェクト AddRef を呼び出してはなりません。
- オブジェクトが作成されるときに、IUnknown 以外のインターフェイスが要求された場合、作成はE_NOINTERFACEで失敗する必要があります。
次のコード フラグメントは、インターフェイスを実装する入れ子になったクラス メソッドを使用して、aggregable オブジェクトの正しい実装を示しています。
// 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;
};
};
オブジェクトの集計
aggregable オブジェクトを開発するときは、次の規則が適用されます。
内部オブジェクトを作成する場合、外側のオブジェクトは、IUnknownを明示的に要求する必要があります。
外側のオブジェクトは、Release の実装を、その破棄コードに関する人工参照カウントで再入から保護する必要があります。
外側のオブジェクトは、内部オブジェクトのいずれかのインターフェイスへのポインターを照会する場合、その制御 IUnknownRelease メソッドを呼び出す必要があります。 このポインターを解放するために、外側のオブジェクトは、その制御 IUnknownAddRef メソッドを呼び出し、その後、内部オブジェクトのポインターに Release を呼び出します。
// Obtaining inner object interface pointer pUnkInner->QueryInterface(IID_ISomeInterface, &pISomeInterface); pUnkOuter->Release(); // Releasing inner object interface pointer pUnkOuter->AddRef(); pISomeInterface->Release();
外側のオブジェクトは、認識できないインターフェイスのクエリを内部オブジェクトに対して盲目的に委任してはなりません。ただし、その動作が外側のオブジェクトの意図である場合を除きます。
関連トピック