设为首页 加入收藏

TOP

探讨C++中的Map映射机制(一)
2015-07-24 05:53:56 来源: 作者: 【 】 浏览:14
Tags:探讨 Map 映射 机制

概述

MFC到ATL,充斥着Map映射机制,似乎没有了这个Map机制,就玩不转啦。在WebBrower控件中,也存在着事件映射;在COM中,在IDispatch中也存在着自定义的函数映射。

以前,只要一谈到映射机制,总是让我闻风丧胆,退而求自保,暂且如此而已,记住就可以啦。现在想来,只要是跨不去过的坎,若没有认真面对和解决,那就永远无法逾越,成为心中永远的痛。最终,只能作茧自缚而唯唯诺诺。既然老天爷,又给了我一次机会,那我就好好抓住这次机会啦。

轰轰烈烈的开场白讲完了,让我们回归主题:“映射机制”

格式

Windows消息的Map格式

map代码,如下所示:

	BEGIN_MSG_MAP(CTestDialog)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		MESSAGE_HANDLER(WM_CLOSE, OnClose)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
	END_MSG_MAP()
其实是三个define所构成的,如下所示:

#define BEGIN_MSG_MAP(theClass) \
public: \
	BOOL ProcessWindowMessage(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam,\
		_In_ LPARAM lParam, _Inout_ LRESULT& lResult, _In_ DWORD dwMsgMapID = 0) \
	{ \
		BOOL bHandled = TRUE; \
		(hWnd); \
		(uMsg); \
		(wParam); \
		(lParam); \
		(lResult); \
		(bHandled); \
		switch(dwMsgMapID) \
		{ \
		case 0:

#define MESSAGE_HANDLER(msg, func) \
	if(uMsg == msg) \
	{ \
		bHandled = TRUE; \
		lResult = func(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}


#define END_MSG_MAP() \
			break; \
		default: \
			ATLASSERT(FALSE); \
			break; \
		} \
		return FALSE; \
	}

 
 
 
 
从中我们可以看到,头文件的map宏,确实有3个define所构成,其本质是定义了一个ProcessWindowMessage函数。在
#define MESSAGE_HANDLER(msg, func) \
中,msg和func由用户选择,所以就暴露这2个参数。若有3个参数有用户选择的话,则肯定会暴露3个参数啦。

一般来说,最后一个参数是函数名称或函数地址,而它之前的参数一般都是它的参数。这样就解决了,只用一个宏,就可以解决所有相同个数和类型的输入参数,但不同操作的一般化函数调用,即程序中的映射机制。

通常情况下,映射一词有照射的含义,是一个动词。在数学上,映射则是个术语,指两个元素集之间元素相互“对应”的关系,名词;也指“形成对应关系”这一个动作,动词。

(摘自百度百科)

说白了,就是一种对应关系嘛,就这么简单,没有啥可说的。

模板类的Map格式

在头文件中,我们定义如下格式的映射关系:

BEGIN_XXX_MAP(CClassName)

XXX_ENTRY(String1, Identify1, OnFunctionName1)

XXX_ENTRY(String2, Identify2, OnFunctionName2)

... ...

XXX_ENTRY(StringN, IdentifyN, OnFunctionNameN)

END_XXX_MAP()

那么,我们现在可以理解为OnFunctionName函数需要String和Identify这两个变量。由于有若个这样的XXX_ENTRY,那么就会有相应个函数,暂且成为函数容器。

这时我们就会想到两种情况来解释个函数容器:

一种是:上面所说的“Windows消息映射”,它只是将消息和函数进行一一对应,则程序更富有表现力,同时隐藏了不必要的代码。并且对应关系比较简单,就是一个类函数指针的代理。

另一种是:若个函数作为函数容器出现,以便在对应的模板类中对容器中的各个函数进行轮询,以便决定是否使用具有特定码的函数。它不再作为一个代理的角色出现,而更多地是扮演成员变量数组的角色出现。

这样做的好处是,让模板类可以更加灵活的处理这个数组,以便完成特定的处理效果。解放了数据和函数,分别进行了处理。咱们职责分明,秋毫无犯嘛,呵呵。

注意事项:

(1)定义GetMap()函数

它一般被const staic所修饰,其返回值为指向模板数组的一个指针;这样在函数中引用该GetMap时,只需要使用T::GetMap即可,因为它是静态函数啊!如下为保存和显示三个变量关系的结构体:

template
  
   
struct ST_XXX_ENTRY
{
	typedef void(T::*Function_Name)();
	LPSTR string;
	UINT identify;
	Function_Name func;

	static void ProcessFunc1()
	{
		//...
	}

	static void ProcessFunc2()
	{
		// ...
	}
};
  
结构体固然重要,但是这里更为重要的是展现三个变量关系之间的静态函数。


map的实例化代码如下所示:

#define BEGIN_XXX_MAP(theClassName)\
	static const ST_XXX_ENTRY
  
    * GetMap() \
	{ \
		static ST_XXX_ENTRY
   
     theMap[] = \ { #define XXX_ENTRY(string, identify, func) \ { string, identify, &theClassName::func},\ // 此处应用了类函数指针的获取方法 #define END_XXX_MAP() \ { NULL, 0, ST_XXX_ENTRY
    
     ::Function_Name(NULL)} \ } \ }
    
   
  

(2)调用GetMap函数

在父类模板中,必然定义了如何使用GetMap中的函数映射关系。那么此时的调用,必然是直接使用T::GetMap()来获得静态容器的指针,然后对它进行遍历和筛选,以期获得我们想要点对应函数或对应函数上的处理结果。


非常棒,到这里,我们已经基本讲完了如何关联map和实例化map,以及变量在结构体中的定义。呵呵,感觉越写越有感觉,越写越明白里面map机制的奥秘在哪里已经如何外化出这个奥秘。

客户(界面)代码

客户代码是使用map宏的代码,它会继承一个模板类,而模板类所需要的实例类便是客户类,为什么会是这个样子呢?

其实原因很简单,因为此处模板类就是将公共函数提取出来,并且统一处理map宏中的转换关系,从而精简客户代码。而客户类完全可以按照客户所想定义的方式定义,想如

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇hdu 4828 Grids(拓展欧几里得+卡.. 下一篇Nucleus PLUS系统架构和组件

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: