包容和聚合這個(gè)概念困惑了有一段時(shí)間了,而且經(jīng)常出現的pUnkOuter也是昏昏然,昨天看了一下《Inside COM》,對這個(gè)東西總算有了一個(gè)比較清晰的了解。
假設某個(gè)manager手下有2個(gè)員工X和Y,X經(jīng)驗豐富,manager就讓X出去直接和客戶(hù)接觸,完成工作后直接把結果提交給客戶(hù),這就是聚合。而Y是新人,manager在接到客戶(hù)任務(wù)后必須指導Y工作并驗證結果后再提交給客戶(hù),此為包容。
pUnkOuter的問(wèn)題出在聚合上,根據規則,queryinterface方法必須滿(mǎn)足以下條件:
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是通過(guò)聚合組件CB實(shí)現。
那么IX->queryinterface(IY)成功后,如何如何再從IY中再query接口IX呢?
所以對于被聚合的組件CB傳入pUnkOuter(及組建CA的IUnknown接口)初始化其成員變量m_pUnkOuter,如果不是被聚合的話(huà)m_pUnkOuter則被初始化為CB自身的IUnknown接口。這樣queryinterface即可正常工作,即第一條符合,總是指向相同的IUnknown,其他也就依次符合了。
在具體實(shí)現上需要一些技巧(該死的C++多繼承),以《Inside COM》中的例子做一下解釋?zhuān)?/div>
struct INondelegatingUnknown
{
virtual HRESULT __stdcall NondelegatingQueryInterface(const IID&, void**) = 0;
virtual ULONG __stdcall NondelegatingAddRef() = 0;
virtual ULONG __stdcall NondelegatingRelease() = 0;
}
該接口的定義的結構與IUnknown完全相同(目的是為了騙過(guò)編譯器,因為C++的虛函數調用并不是通過(guò)函數名,然是通過(guò)查vtable實(shí)現,這樣INondelegatingUnknown的虛函數表與IUnknown相同,后面會(huì )提到),關(guān)鍵代碼如下:
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實(shí)際上使用的是NondelegatingQueryInterface。
}
else
{
m_pUnkOuter = pUnkOuter; //指向聚合組件CA的IUnknown。
}
}
以下這段代碼做了一個(gè)小小的演示,應該有助于理解以上問(wèn)題:
#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;
}