1、14、包含与聚合、包含与聚合如果一个构件如果一个构件B使用另外一个构件使用另外一个构件A我们称使用者我们称使用者B为:外部构件为:外部构件 被使用者被使用者A为:内部构件为:内部构件则:外部构件则:外部构件B包含(聚合)内部构件包含(聚合)内部构件A包含与聚合是包含与聚合是COM的两种复用方式(模型)的两种复用方式(模型)为系统升级、扩展提供支持为系统升级、扩展提供支持2(1)包含与聚合简介)包含与聚合简介(2)包含的实现)包含的实现(3)聚合的实现)聚合的实现(4)例子)例子3(1)包含与聚合简介)包含与聚合简介包含(Containment)对象A对象BIYIXIX4对象对象 B 的成员函数
2、在调用对象的成员函数在调用对象A的接口之前或之后的接口之前或之后可以进行其他一些操作可以进行其他一些操作IX提供的功能可以超过提供的功能可以超过IX对象对象B是对象是对象A 的客户的客户而对象而对象B的客户看到的只是对象的客户看到的只是对象B显露出的接口(显露出的接口(IY、IX)对象对象A的创建与释放完全在对象的创建与释放完全在对象B内部进行内部进行对象对象A的生命期包含在对象的生命期包含在对象B的生命期内的生命期内5对象A对象B聚合(Aggregation)IYIX6可以将聚合看作是包含的一个特例可以将聚合看作是包含的一个特例更好地体现了复用更好地体现了复用 虽然对象虽然对象A直接向对象直
3、接向对象B的客户提供服务的客户提供服务 但但 对象对象B的客户感觉不到对象的客户感觉不到对象A的存在的存在对象对象A的生存期受到对象的生存期受到对象B的控制的控制实现聚合的关键在于实现聚合的关键在于 QueryInterface:当客户向对象当客户向对象B请求请求IX时时对象对象B的的QueryInterface返回对象返回对象A的的IX指针指针存在问题:存在问题: (1)从)从IX如何得到如何得到IY?(2)从)从IX得到的得到的Iunknown如何与从如何与从IY得到的得到的IUnknown 相同?相同?7包含的复用性建立在客户包含的复用性建立在客户/服务器模式上服务器模式上聚合的复用性需
4、要内部构件提供支持聚合的复用性需要内部构件提供支持复用方式的选择:复用方式的选择:当内部构件提供的接口完全满足要求时当内部构件提供的接口完全满足要求时使用聚合合适使用聚合合适当内部构件提供的接口与需求类似时当内部构件提供的接口与需求类似时使用包含合适使用包含合适在一个对象中可以同时使用两种复用方式在一个对象中可以同时使用两种复用方式8(2)包含的实现包含的实现假定假定IX、IY的定义分别为:的定义分别为:class IX:public IUnknown public : virtual HRESULT _stdcall FX() =0;class IY:public IUnknown publ
5、ic : virtual HRESULT _stdcall FY() =0;9对象对象B的定义为:的定义为:class CB: public IX, public IY protected: ULONG m_ref;public: CB(); CB(); / IUnknownvirtual HRESULT _stdcall QueryInterface(const IID& iid, void* ppv) ;virtual ULONG _stdcall AddRef() ;virtual ULONG _stdcall Release() ;/IXHRESULT _stdcall FX();/I
6、YHRESULT _stdcall FY(); HRESULT Init();private:IX *m_pIX;10类类B的实现:的实现: CB:CB()m_pIX = null;m_Ref = 0; CB:CB()if (m_pIX != null) m_pIX-Release();HRESULT CB:Init() HRESULT result = :CoCreateInstance(CLSID_ComponentA,null, CLSCTX_INPROC_SERVER,IID_ISomeInterface,(void *)&m_pIX);if (FAILED(result) retur
7、n E_FAIL;else return S_OK;HRESULT _stdcall CB:IX() return m_pIX-FX();11包含的主要用途:扩展接口包含的主要用途:扩展接口 假定想把一个假定想把一个 Iairplane接口扩展为接口扩展为 IFloatPlane:定义:定义:interface Iairplane: Iunknownvoid Takeoff();void fly();void Land();interface IFloatPlane: Iairplane void Float();void Sink();void Rust();void DrainBankAc
8、count();12(3)聚合的实现聚合的实现对象对象B的定义为:的定义为:class CB: public IY protected: ULONG m_ref;public: CB(); CB(); / IUnknownvirtual HRESULT _stdcall QueryInterface(const IID& iid, void* ppv) ;virtual ULONG _stdcall AddRef() ;virtual ULONG _stdcall Release() ;/IYHRESULT _stdcall FY(); HRESULT Init();private:IUnkn
9、own *m_pUnknownInner;13HRESULT _stdcall CB:QueryInterface(const IID& iid, void* ppv)if (iid = IID_IUnknown)*ppv = static_cast(this) ; else if (iid = IID_IY)*ppv = static_cast(this) ;else if (iid = IID_IX)return m_pUnknownInner-QueryInterface(iid,ppv);else*ppv = NULL ;return E_NOINTERFACE ;reinterpre
10、t_cast(*ppv)-AddRef() ;return S_OK ;14问题:QueryInterfaceAddRefReleaseFyQueryInterfaceAddRefReleaseFxIYIX内部构件的IUnknown实现外部构件的IUnknown实现内部构件外部构件15解决方法:将内部构件的解决方法:将内部构件的IUnknown接口进行隐藏接口进行隐藏使内部构件的接口使内部构件的接口调用外部接口的调用外部接口的IUnknown接口接口这样,内部构件将实现两个这样,内部构件将实现两个IUnknown接口接口一个是一个是“非代理未知接口非代理未知接口”按一般方式实现按一般方式实现I
11、Unknown接口接口一个是一个是“代理未知接口代理未知接口”如果内部构件未被聚合如果内部构件未被聚合则代理未知接口将调用则代理未知接口将调用转发给非代理未知接口转发给非代理未知接口如果内部构件被聚合如果内部构件被聚合代理未知接口将调用代理未知接口将调用转发给外部构件实现的未知接口转发给外部构件实现的未知接口16CoCreateInstance与IClassFactory中的pIUnknownOuter:HRESULT_stdcall CoCreateInstance(const CLSID& clsid,Iunkown * pIUnknownOuter,DWORD dwClsContext,
12、const IID& iid,void * ppv);HRESULT _stdcallCreateInstance( IUnknown * pUnknownOuter,const IID & iid,void * ppv );17QueryInterfaceAddRefReleaseFxIX代理Iunknown实现非聚合构件非代理Iunknown实现18QueryInterfaceAddRefReleaseFyQueryInterfaceAddRefReleaseFxIYIX代理Iunknown的实现外部构件的IUnknown实现内部构件外部构件非代理Iunknown的实现19内部构件的创建内
13、部构件的创建 涉及三个函数:涉及三个函数: 外部构件的外部构件的 Init 函数函数内部构件类厂的内部构件类厂的CreateInstance内部构件的构造函数内部构件的构造函数外部构件的外部构件的 Init 函数函数:HRESULT _stdcall CB:Init() IUnknown * pUnknownOuter = this;HRESULT hr = CoCreateInstance(CLSID_ComponentA, pIUnknownOuter,CLSCTX_INPROC_SERVER,IID_IUnknown, (void * ) &m_pUnknownInner);if (FA
14、ILED(hr) return E_FAIL;else return S_OK;20内部构件类厂的内部构件类厂的CreateInstance:HRESULT _stdcallCfactory:CreateInstance( IUnknown * pUnknownOuter,const IID & iid,void * ppv )/ To aggregate, iid must be IID_IUnknown. If (pUnknownOuter != NULL)&(iid != IID_IUnknown) return CLASS_E_NOAGGREGATION; ;/Create compo
15、nent.CB *pB = new CB(pUnknownOuter); if( pB = NULL) return E_OUTOFMEMORY;HRESULT hr= pB-NondelegationQueryInterface(iid,ppv);pB-NondelegatingRelease();return hr;21内部构件的构造函数内部构件的构造函数:CB:CB(IUnknown * pUnknownOuter):m_cRef(1) :InterlockedIncrement(&g_cComponents); if(pUnknownOuter = NULL) /Not being a
16、ggregated, use nondelegating unknown.m_pUnknownOuter = reinterpret_cast(static_cast(this); else /Being aggregated, use outer unknown.m_pUnknownOuter = pUnknownOuter; 22对外部构件Init的补充HRESULT _stdcall CB:Init() IUnknown * pUnknownOuter = this;HRESULT hr = CoCreateInstance(CLSID_ComponentA, pIUnknownOuter,CLSCTX_INPROC_SERVER,IID_IUnknown, (void * ) &m_pUnknownInner);if (FAILED(hr) return E_FAIL;result = m_ pUnknownInner -QueryInterface(IID_IX,(void *) &m_pIX);if (FAILED(result) m_ pUnknownInner - Release();return E_FAIL; m_ pUnknownOuter -Release();return S_OK;