1.包容和聚合的概念
包容和聚合实际上是使一个组件使用另外一个组件的一种技术。
包容的情况下,客户只能看到外部组件的接口,而外部组件接口的实现是通过转发给内部组件实现的。
聚合的情况下,直接将内部组件的借口暴漏给客户,虽然客户不知道内部组件的存在。
2.包容的实现
包容的实现比较简单,例如外部组件CA(接口IX,IY,其中IY转发给内部组件),内部组件CB(接口IY)。
组件CA中添加一成员变量,用于保存内部组件CB的接口指针:
private:
IY* m_pIY;
CA中Fy函数的实现:
Virtual void __stdcall CA::Fy()
{
m_pIY->Fy();
}
CA析构函数中调用m_pIY->Release();将CB从内存中释放。
CA新增一成员函数 HRESULT Init(),用于获取内部组件对象的接口指针。
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
CA的类厂CFactory::CreateInstance函数中加入对Init函数的调用(PS:一个类厂只能创建一种COM对象,CreateInstance函数只接受接口IID,不接受CLSID):
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
以上实现了组件CA对组件CB的包容。CA自己没有实现Fy方法,而是通过转发给CB去实现,实现了组件的复用。
2.聚合的实现
因为客户应完全独立于组件的实现,也就是客户不应该知道所使用的组件使用了聚合,也就是永远看不到内部组件的IUnkown。由于两个不同的接口当且仅当返回相同的IUnkown指针时,才被认为是同一个组件实现的。所以外部组件要隐藏内部组件提供给客户,也就是只提供外部组件的IUnkown。
为了实现以上功能,支持被聚合CB接口要实现一组和IUnknown接口一致的非代理Unknown接口,这一组接口完成实际的QueryInterface, AddRef 和 Release的工作。而真正的IUnknow接口的工作是判断该对象是否是被聚合使用(通过一个指向IUnknown的指针成员m_pUnknownOuter),如果该对象被聚合使用(m_pUnknownOuter不等于Null),则将调用转向m_pUnknownOuter所指的外部对象CA,在CA中调用CB的非代理Unknown接口。
具体实现如下:
CA的类厂实现如下:
CA在初始化时,聚合使用了CB。如下:
HRESULT CA::Init()
{
IUnknown *pUnknownOuter = (IUnknown *)this;
HRESULT result = ::CoCreateInstance(CLSID_CB, pUnknownOuter, //传送给内部组件
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(void **)& m_pUnknownInner) ;//得到的是非代理IUnkown 接口
if (FAILED(result))
return E_FAIL;
result = m_pUnknownInner->QueryInterface(IID_IY, (void **)&m_pCAInterface);//外部组件的引用计数+1
if (FAILED(result))
{
m_pUnknownInner->Release();
return E_FAIL;
}
pUnknownOuter->Release(); //外部组件的引用计数-1
return S_OK;
}
CA的QueryInterface实现如下:
HRESULT CA::QueryInterface(const IID& iid, void **ppv)
{
if ( iid == IID_IUnknown )
{
*ppv = (IUnknown *) this ;
((IUnknown *)(*ppv))->AddRef() ;
}
else if ( iid == IID_IX )
{
*ppv = (IX*) this ;
((IX*)(*ppv))->AddRef() ;
}
else if ( iid == IID_IY)
{
return m_pUnknownInner->QueryInterface(iid, ppv) ;//调用非代理IUnkown接口的 QueryInterface
}
else
{
*ppv = NULL;
return E_NOINTERFACE ;
}
return S_OK;
}
CB的类厂实现为:
客户组件的非代理IUnknown的实现等同于非聚合时的实现,代理外部接口的实现如下:
HRESULT CB::QueryInterface(const IID& iid, void **ppv)
{
if ( m_pUnknownOuter != NULL ) //聚合情况
{
return m_pUnknownOuter->QueryInterface(iid, ppv);//转交给外部组件,由外部组件调用自己的非代理IUnknown接口
}
else //非聚合情况
{
return NondelegationQueryInterface(iid, ppv);
}
}
//非代理IUnknown接口实现
class INondelegatingUnknown
{
public:
virtual HRESULT __stdcall NondelegationQueryInterface(const IID& iid, void **ppv) = 0 ;
virtual ULONG __stdcall NondelegatingAddRef() = 0;
virtual ULONG __stdcall NondelegationRelease() = 0;
};
class CB : public IY, public INondelegatingUnknown
{
protected:
ULONG m_Ref;
public:
CA(IUnknown *pUnknownOuter);
~CA();
public :
// Delegating IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void **ppv) ;
virtual ULONG __stdcall AddRef() ;
virtual ULONG __stdcall Release() ;
// Nondelegating IUnknown
virtual HRESULT __stdcall NondelegationQueryInterface(const IID& iid, void **ppv);
virtual ULONG __stdcall NondelegatingAddRef();
virtual ULONG __stdcall NondelegationRelease();
private :
IUnknown *m_pUnknownOuter; // pointer to outer IUnknown
};
HRESULT CB::NondelegationQueryInterface(const IID& iid, void **ppv)
{
if ( iid == IID_IUnknown )
{
*ppv = (INondelegatingUnknown *) this ;
//当请求IUnknown接口时,强制转换成非代理接口。
//(INondelegatingUnknown *) this 表达式的结果就仅仅是INondelegatingUnknown虚表的指针。而*ppv需要的正好仅仅是这个虚表的指针。所以在在CA中调用m_pUnknownInner- >QueryInterface,程序去找虚表中的第一项。所以实际被调用的就是CA::NondelegationQueryInterface。
((IUnknown *)(*ppv))->AddRef() ;
//调用IUnkonwn:: AddRef,所以聚合时外部组件的引用计数+1;
} else if ( iid == IID_IY )
{
*ppv = (IY*) this ;
((IY*)(*ppv))->AddRef() ;
}
else
{
*ppv = NULL;
return E_NOINTERFACE ;
}
return S_OK;
}