编写可复用性更好的C++代码:Band对象和COMToys(5)
2006-07-21 11:46:08 来源:WEB开发网// 在 .cpp 文件中
BEGIN_INTERFACE_MAP(CMyComClass, CCmdTarget)
INTERFACE_PART(CMyComClass, IID_IPersistFile, PersistFile)
……
END_INTERFACE_MAP()
这个宏为你的类中每个COM接口产生一个细目表。每一个细目表的表项存储接口的IID以及在实现它的嵌套类主类中的偏移量
{ &IID_IPersistFile, offsetof(CMyComClass, m_xPersistFile) }
一旦你声明并实现了这个接口映射,就必须实现它们的方法,包括AddRef,Release,和QueryInterface。因为这个类是嵌套的,必须为COM对象支持的每一个接口编写IUnknown,即使实现都相同。例如:
// AdRef 和Release的代码相同
STDMETHODIMP
CMyComClass::XPersistFile::QueryInterface(...)
{
METHOD_PROLOGUE(CMyComClass, PersistFile)
return pThis->ExternalQueryInterface(...);
}
METHOD_PROLOGUE在MFC中是必不可少的,用以获得它们之间的关系(在任何DLL的入口点,AFX_MANAGE_STATE是不可缺少的)并设置pThis,它指向父类CMyComClass("this"指针地址减去嵌套的偏移量)。CCmdTarget::ExternalQueryInterface搜索你的接口映射查找与请求的接口匹配的IID条目。如果找到,则将偏移量添加到这个指针并返回结果——如果一切正常,它指向实现接口的嵌套对象。这种方法能行得通,但是极其麻烦。 首先,在实现每一个接口的IUnknown时就非常的别扭,它开启了出错的大门,而且因为复制不必要的代码而变得臃肿不堪。 第二,你不能从接口方法中直接访问你的类成员;而是必须通过pThis来存取它们——这简直就是故弄玄虚;从概念上讲,接口方法属于外部类,所以为什么不能像访问其它成员那样访问它的成员呢? 最后,也是最让人讨厌的一点,嵌套类方式无法让你在派生类中重载接口方法。假设你派生一个新类,CMyComClass2,并且你只想重载IPersistFile::SaveCompleted以便设置一个标志,ON_UPDATE_COMMAND_UI处理器将检查这个标志并显示"正在存储….",直到完成存储。你不用做什么。CMyComClass没有可重载的SaveCompleted函数。实现SaveCompleted的类被嵌套在其中,CMyComClass::XPersistFile,没有办法重载它的方法。除了要重载SaveCompleted以外,你还必须在派生类中重新实现整个IPersistFile接口,创建另一个嵌套类,其中包含什么也不做的方法。只是调用一下基类方法,CMyComClass::m_xPersistFile。 我在前面文章的代码中碰到了这个问题。当容器设置现场时,为了让CBandObj派生类做些事情,我不得不提供一个新的虚函数,CBandObj::OnSetSite,并从CBandObj::XDeskBand::SetSite中调用它。其它接口方法如何呢?我是不是要为CBandObj实现的每一个接口方法引入类似OnXxx的东西?我不想这样! 出于这些原因,许多C++程序员——包括ATL代码编写者——都采用多继承进行COM编程。从每个实现的接口派生多个自己的COM类。
class CMyComClass :
public IPersistFile,
public IContextMenu, ...
{
// IUnknown
STDMETHOD_(ULONG, AddRef)();
……
// IPersistFile
STDMETHODIMP GetClassID(LPCLSID pClsID);
……
// IContextMenu
STDMETHOD (QueryContextMenu)(...);
……
};
所有方法属于主类,因此你可以用通常的方式重载它们,而且所有的方法都能直接存取类成员;不需要使用pThis。然后利用C++的魔力,只要实现一次AddRef,Release和 QueryInterface即可,并且同样的实现适用于所有的IUnknown实例。这恰恰是因为C++的规则使然,"纯粹的虚函数总是通过实现它的任何子类进行重定义的。"所有虚表(vtbls)都将有IUnknown的位置,在这个位置指向相同的物理函数。这对于其它可能被多继承的接口也一样;例如,IPersist从IPersistFile 和IPersistStream继承而来。如图十七所示,我准备称它为魔力MI法则。
图十七 多继承
既然多继承如此之好,为什么MFC不用它呢?因为一碰到具体的类——那些有真实函数和数据的类时——多继承便引起混淆。如果你写x = m_foo,这里的 m_foo是个从A继承还是从B继承的呢?而且MFC从CObject派生了它的所有类,用多继承会导致可怕的菱形层次。虽然可以用虚基类来克服这种不足,但事情会更糟。所以创建MFC的那位哥们儿聪明地决定避开多继承。(待续)
更多精彩
赞助商链接