编写可复用性更好的C++代码:Band对象和COMToys(7)
2006-07-21 11:46:03 来源:WEB开发网IMPLEMENT_IPersist(CMyComClass, CTPersist);
产生的GetClassID方法便替代IPersistStream 和 IPersistFile中的GetClassID。 最后正是在这些方法中我不得不针对语言规范编写一些代码。所幸的是它不算太麻烦。只是将前面用嵌套类实现的BandObj转换成本文中对应的CTXxx类,将必须的变量转换成数据成员并在构造函数中进行初始化。例如,CTPersist的代码如下:
class CTPersist {
public:
const CLSID& m_clsid;
CTPersist(const CLSID& clsid) : m_clsid(clsid) { }
STDMETHODIMP GetClassID(LPCLSID pClassID) {
return pClassID ?
(*pClassID=m_clsid, S_OK) : E_UNEXPECTED;
}
};
CTPersist保存着类ID的引用,并不是类ID本身。此乃COMToys的一般原则:执行类不存储实际数据,只有外部对象的指针或引用。对于CTPersist,它无关紧要,因为一旦运行中的对象类ID改变,它便无所适从。但一般来说,数据是可以改变的,所以让父类拥有数据是明智的,这样它就可以自由的处理这些数据。CTPersistFile 和 CTPersistStream都有一个m_bModified修改标志,但它是对BOOL类型的一个引用,而不是BOOL类型。如果主类改变了实际的标志,执行类自动改变,不用自己去调用诸如SetModified之类的函数。作为一个一般的编程规则,对状态的操作最好是用按需方式进行(demand-pull),而不要用先入方式(supply-push),并且整个系统的每个状态变量自始至终只应该有一个拷贝。 CTMfcContextMenu的情况类似,它保存着对仅仅一个CMenu的引用,主类必须在构造时提供。
// 在COMToys.h文件中
class CTMfcContextMenu {
public:
CCmdTarget* m_pCmdTarget;
CMenu& m_ctxMenu;
CTMfcContextMenu(CCmdTarget* pTarg, CMenu& menu)
: m_pCmdTarget(pTarg), m_ctxMenu(menu) { }
……
};
// 在BandObj.cpp文件中
CBandObj::CBandObj(REFCLSID clsid) :
CTMfcContextMenu(this, m_menu), ...
{
……
}
因为CTMfcContextMenu::m_ctxMenu是个引用,CBandObj可以任何方式改变菜单,不必显式通知CTMfcContextMenu。CBandObj依赖这个特性,因为当用户右键单击Band时,它产生其浮动菜单。如图十二)。CTMfcContextMenu的实现基本上照搬前面代码中相应的部分。
图十二 CBandObj 上下文菜单
当容器调用IContextMenu::QueryContextMenu获得菜单项时,CTMfcContextMenu用希望的CMenu项填写菜单,但不是在创建CcmdUI对象之前,并且通过CCmdTarget发送,从而ON_UPDATE_COMMAND_UI处理器都能挂上菜单。同样,当容器调用InvokeCommand,CTMfcContextMenu发送这个命令到ON_COMMAND处理器。CTMfcContextMenu甚至能通过查找串资源来处理提示串。简言之,CTMfcContextMenu将COM语言转换成MFC语言。剩下的事情只是给它一个菜单和命令目标(通常是COM类本身)。所有的命令处理与在MFC应用中处理一样,不用再次实现IContextMenu——用COMToys就能搞掂。(待续)
更多精彩
赞助商链接