集計
集計は、外部オブジェクト自体に実装されているかのように、外部オブジェクトが内部オブジェクトからインターフェイスを公開するオブジェクト再利用メカニズムです。 これは、外部オブジェクトが、そのインターフェイスの1つに対するすべての呼び出しを内部オブジェクトの同じインターフェイスに委任する場合に便利です。 集計は、この場合、外部オブジェクトでの追加の実装オーバーヘッドを回避するために便利です。 集計は、実際には包含/委任の特殊なケースです。
集計は、3つのIUnknown関数 (QueryInterface、AddRef、およびRelease)QueryInterface, AddRefおよびRelease. キャッチは、クライアントの観点から、外部オブジェクトのすべてのIUnknown関数が外部オブジェクトに影響を与える必要があることです。 つまり、AddRefとReleaseは外部オブジェクトに影響し、QueryInterfaceは外部オブジェクトで使用可能なすべてのインターフェイスを公開します。 ただし、外部オブジェクトが内部オブジェクトのインターフェイスを独自のインターフェイスとして公開するだけの場合、そのインターフェイスを介して呼び出される内部オブジェクトのIUnknownメンバーは、外部オブジェクトのインターフェイスのIUnknownメンバーとは異なる動作をします。これは、IUnknownを管理する規則とプロパティの絶対違反です。
このソリューションでは、集計には、内部オブジェクトのIUnknownの明示的な実装と、外部オブジェクトのIUnknownメソッドに対する他のインターフェイスのIUnknownメソッドの委任が必要です。
集計可能なオブジェクトの作成
集計可能なオブジェクトの作成は省略可能です。ただし、簡単に実行でき、大きな利点が得られます。 集計可能なオブジェクトの作成には、次の規則が適用されます。
- 集計可能 (または内部) オブジェクトのQueryInterface, AddRefおよびReleaseのIUnknownインターフェイスの実装は、内部オブジェクトの参照カウントを制御します。この実装は、外側のオブジェクトの不明な (制御IUnknown) に委任することはできません。
- 集計可能 (または内部) オブジェクトのQueryInterface, AddRefおよびReleaseの他のインターフェイスの実装は、制御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;
};
};
オブジェクトの集計
集計可能なオブジェクトを開発する場合は、次の規則が適用されます。
内部オブジェクトを作成する場合、外部オブジェクトは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();
外部オブジェクトは、認識されないインターフェイスのクエリを内部オブジェクトに盲目的に委任することはできません。ただし、その動作が特に外部オブジェクトの意図である場合は除きます。
関連トピック