关于COM的包容和聚合以及pUnkOuter......包容和聚合这个概念困惑了有一段时间了,而且经常出现的pUnkOuter也是昏昏然,昨天看了一下《Inside COM》,对这个东西总算有了一个比较清晰的了解。
假设某个manager手下有2个员工X和Y,X经验丰富,manager就让X出去直接和客户接触,完成工作后直接把结果提交给客户,这就是聚合。而Y是新人,manager在接到客户任务后必须指导Y工作并验证结果后再提交给客户,此为包容。
pUnkOuter的问题出在聚合上,根据规则,queryinterface方法必须满足以下条件:
1. You always get the same IUnknown.
2. You can get an interface if you get it before.
3. You can get the interface you have.
4. You can always get back to where you started.
5. You can get there from anywhere if you can get there from somewhere.
如果组件CA提供接口IX和IY,其中IY是通过聚合组件CB实现。
那么IX->queryinterface(IY)成功后,如何如何再从IY中再query接口IX呢?
所以对于被聚合的组件CB传入pUnkOuter(及组建CA的IUnknown接口)初始化其成员变量m_pUnkOuter,如果不是被聚合的话m_pUnkOuter则被初始化为CB自身的IUnknown接口。这样queryinterface即可正常工作,即第一条符合,总是指向相同的IUnknown,其他也就依次符合了。
在具体实现上需要一些技巧(该死的C++多继承),以《Inside COM》中的例子做一下解释:
struct INondelegatingUnknown
{
virtual HRESULT __stdcall NondelegatingQueryInterface(const IID&, void**) = 0;
virtual ULONG __stdcall NondelegatingAddRef() = 0;
virtual ULONG __stdcall NondelegatingRelease() = 0;
}
该接口的定义的结构与IUnknown完全相同(目的是为了骗过编译器,因为C++的虚函数调用并不是通过函数名,然是通过查vtable实现,这样INondelegatingUnknown的虚函数表与IUnknown相同,后面会提到),关键代码如下:
class CB : public IY, public INondelegatingUnknown
{
IUnknown* m_pUnkOuter;
......
}
CB::CB(IUnknown* pUnkOuter)
{
if (NULL == pUnkOuter)
{
m_pUnkOuter = reinterpret_case<IUnknown*>(static_cast<INondelegatingUnknown>(this));
//在这种情况下,m_pUnkOuter调用queryinterface实际上使用的是NondelegatingQueryInterface。
}
else
{
m_pUnkOuter = pUnkOuter; //指向聚合组件CA的IUnknown。
}
}
以下这段代码做了一个小小的演示,应该有助于理解以上问题:
#include <stdio.h>
struct Base
{ virtual void func(void) { printf("Base\n"); } }; struct BaseNondel
{ virtual void funcNondel(void) { printf ("BaseNondel\n"); }; }; class Component : public BaseNondel
{ public: Component(Base* p); virtual void func(void); private:
Base* m_p; }; Component::Component(Base* p)
{ if (NULL == p) { m_p = reinterpret_cast<Base*>(static_cast<BaseNondel*>(this)); } else m_p = p; } void Component::func(void)
{ m_p->func(); } int main(void)
{ Component* pComponent = new Component(NULL); pComponent->func(); return 0;
} |
|