包 容 与 聚 合

330 篇文章 4 订阅 ¥19.90 ¥99.00
本文详细介绍了COM中的包容和聚合概念,包括两者的工作原理和区别。通过示例解释了如何实现聚合,并探讨了IUnknown在聚合过程中的角色。同时提到了ATL Wizard在创建可聚合组件时的作用,以及实现包容和聚合所需的步骤。
摘要由CSDN通过智能技术生成

包 容 与 聚 合

lostall

    包容和聚合是COM中比较重要的概念,不过它们的原理还是比较简单的。

    一、包容

    如图所示:

图1

    从上图可看出组件A自己实现了组件B的接口IB,只不过在它实现接口IB的时侯,其内部可能重用了组件B的一些代码。组件B的内部实现对client是完全隐藏的,client看见的只是IB。对client来说,接口IB就是组件A提供的接口。可见包容的概念是相当简单自然的,它类似于设计模式中的“委托”模式。

    二、聚合 NondelegationQueryInterface()。如图所示:

图2

    从上图可看出组件A自己并没有实现组件B的接口IB。当client向组件A请求接口IB的指针时,组件A返回它内部聚合(已经用 CoCreateInstance创建好了)的组件B的接口IB。与包容相同的,对client来说,接口IB就是组件A提供的接口。但它们不同之处在于,聚合中组件A自己没有实现IB,client得到的是原封不同的组件B对IB的实现。而包容中client得到的是组件A自己对IB 的实现,它可能与组件B对IB的实现大相径庭。

    聚合中令人感兴趣的一个问题是:IUnknown是怎么实现的。组件A并没有实现IB,那么如何才能使IB就象是组件A自身具有的呢?具体地说就是如何根据pIA查询出pIB,又如何根据pIB查询出pIA呢?而且别忘了组件B并不只是一个被聚合对象,它可以独立存在也可以去聚合别的组件。

    其实解决方法也很简单。COM给出的解决方法是这样的:组件B实现两个IUnknown接口(这只是逻辑上的),分别叫委托IUnknown和非委托IUnknown,分别实现QueryInterface、AddRef、Release和NondelegationQueryInterface、NondelegationAddRef、 NondelegationRelease。它的查询过程如下图所示:

图3

    注解如下:

    (1)client通过pIB->QueryInterface()查询接口。

    (2)若查询的是组件A实现的接口,则查询转到组件A实现的IUnknown接口,即调用组件A的QueryInterface()。

    (3)若查询的是组件B自己的接口,则查询转到组件B实现的非代理IUnknown接口,即调用组件B的NondelegationQueryInterface()。

    (4)client通过pIA->QueryInterface()查询接口。

    (5)若查询的是组件B的接口,则查询直接转到组件B的非代理IUnknown接口,即调用组件B的NondelegationQueryInterface()。

    就这么简单,还有一点要补充的是。组件A也可能被其他组件聚合,所以它也会有一个代理的IUnknown和一个非代理的IUnknown。另外组件A除了聚合组件B之外,也可能聚合组件C,所以在图3中组件A的IUnknown显然也应该是代理的IUnknown。

    下面说说聚合一个组件需要哪些步骤:

    (1)生成一个可聚合组件。在用ATL Wizard创建组件时,默认的选择是允许聚合。

    (2)再生成一个组件,它是准备聚合前面生成的组件的。

    (3)加入以下代码:

	CComPtr
 
  m_pUnkAgg ;	//指向被聚合组件的非代理IUnknown指针
	......
	HRESULT FinalConstruct() ;	//这两个函数必须是公有的
	void FinalRelease() ;
	......
	DECLARE_GET_CONTROLLING_UNKNOWN()
(4)在BEGIN_COM_MAP和END_COM_MAP之间加入:
	COM_INTERFACE_ENTRY_AGGREGATE(IID_IDemoAgg, m_pUnkAgg.p)

    正如图3中的(5)所示,若组件A发现要查询的接口是IID_IDemoAgg(组件B的接口)时,就会交给m_pUnkAgg.p(组件B的非代理IUnknown)处理。如果组件B实现的接口不止一个,比如还有IDemoAgg2,则同样的,上面这条语句也要对IID_IDemoAgg2再写一次。

    (5)创建聚合组件:

	HRESULT CDemoOuter::FinalConstruct()
	{
		IUnknown *pUnkOuter = GetControllingUnknown() ;
		HRESULT hr ;
		hr = CoCreateInstance(CLSID_DemoAgg, pUnkOuter, CLSCTX_ALL, IID_IUnknown, (void**)&m_pUnkAgg) ;
		return hr ;
	}

	void CDemoOuter::FinalRelease()
	{
		m_pUnkAgg.Release() ;
	}

    在ATL的帮助上,实现包容和聚合是非常容易的,只需要少量的几行代码就可以了。但这种方便是建立在ATL为我们隐藏了大量细节的基础上的。如果想对聚合和ATL的类库结构有更深一步的了解,可参阅另一篇文章ATL接口映射宏详解

    下载示例代码(43KB)  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值