探讨C++中的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<class T>
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<theClassName> * GetMap() 	{ 		static ST_XXX_ENTRY<theClassName> theMap[] = 		{

#define  XXX_ENTRY(string, identify, func) 			{ string, identify, &theClassName::func},\ // 此处应用了类函数指针的获取方法

#define  END_XXX_MAP() 			{ NULL, 0, ST_XXX_ENTRY<theClassName>::Function_Name(NULL)} 		} 	}

(2)调用GetMap函数

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

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

客户(界面)代码

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

其实原因很简单,因为此处模板类就是将公共函数提取出来,并且统一处理map宏中的转换关系,从而精简客户代码。而客户类完全可以按照客户所想定义的方式定义,想如何命名类名就如何命名类名,很自由。唯一要做的就是继承一下模板类,并且添加自己喜欢的对应关系即可,想用什么函数名就用什么函数名,想用什么id就用什么id,因为map的实例化只是引用函数指针,跟名字一点关系都没有。够爽了吧,一个“牛爽”。

可话又说回来,所有的模板类不正是可以容纳各中类而存在,并且统一化处理流程的嘛。原来,我们从实践中,再次感受到模板的优点,或者说它的使命:

(1)模板更有助于编写。我们只需创建类或函数的一个泛型版本,而不是手动创建专用化;

(2)模板是类型安全的。 由于模板操作的类型在编译时是已知的,因此编译器可以在发生错误之前执行类型检查;

(3)由于可通过模板直接提取信息,因此模板更易于理解。(当然是这样的,若仅仅查看模板的话,显得比较抽象,若通过模板来实例化一个对象后,则提取信息变得可视化,确实易于理解。)

这三个优点,我是从msdn上摘的,不过稍微润色了一下,使得主旨更加明晰(毕竟翻译e文,仁者见仁哦)。

到此,我已经讲完了映射机制,Windows的所有映射机制,大抵如此,照葫芦画瓢。

真没有想到,居然写了这么多。不过真心体会,写完这篇blog之后,感觉对映射机制如释重负,感觉从未有过的轻松自在。越发觉得,写blog是一个很不错的深入学习的体验。只有在写得过程中,才会感受到那种顺藤摸瓜的感觉。

探讨C++中的Map映射机制

时间: 2024-10-24 01:24:27

探讨C++中的Map映射机制的相关文章

KVm中EPT逆向映射机制分析

2017-05-30 前几天简要分析了linux remap机制,虽然还有些许瑕疵,但总算大致分析的比较清楚.今天分析下EPT下的逆向映射机制.EPT具体的工作流程可参考前面博文,本文对于EPT以及其工作流程不做过多介绍,重点介绍逆向映射机制.其实逆向映射机制在最主要的作用就是映射的逆向,说了等于白说,但也不无道理.linux下根据虚拟地址经过页表转换得到物理地址.怎么根据物理地址得到对应的虚拟地址呢?这里便用到了逆向映射.逆向映射有什么用呢?最重要的,在页面换出时,由于物理内存的管理由一套相对

Hibernate的映射机制

对象关系映射(Object Relation Mapping(ORM))是一种为了解决面向对象与面向关系数据库互不匹配现象的技术,简而言之 ORM是通过使用描述对象之间映射的元数据,将java程序中的对象自动持久化到关系数据库中,这种映射机制从本质上来说 其实就是将数据从一种形式转化为另一种形式 Hibernate的基本映射数据类型 Hibernate的基本映射数据类型是java基本类型与标准SQL类型相互转化的桥梁,其关系 java类型----------->Hibernate的映射数据类型-

linux 逆向映射机制浅析

2017-05-20 聚会回来一如既往的看了会羽毛球比赛,然后想到前几天和朋友讨论的逆向映射的问题,还是简要总结下,免得以后再忘记了!可是当我添加时间--这就有点尴尬了--520还在写技术博客-- 闲话不多说,之前一个问题是想要根据物理页框号得到映射的虚拟地址,一时间不知道如何下手了,在群里和一个朋友讨论了一番,记得之前看swap机制的交换缓存时,记载说系统当要换出一个页面时,可以很容易找到使用该页面的所有进程,然后撤销映射.这一点也就成了我的突破口.经过对源码的一番研究结合相关书籍,便有了今天

Java中的Map List Set等集合类

Map List Set等集合类: 一.概述 在JAVA的util包中有两个所有集合的父接口Collection和Map,它们的父子关系: +Collection 这个接口extends自 --java.lang.Iterable接口 ├+List(接口 代表有序,可重复的集合.列表) │├ ArreyList     (Class 数组,随机访问,没有同步,线程不安全) │├ Vector        (Class  数组                   同步        线程全) │

详解Java的MyBatis框架中SQL语句映射部分的编写

这篇文章主要介绍了Java的MyBatis框架中SQL语句映射部分的编写,文中分为resultMap和增删查改实现两个部分来讲解,需要的朋友可以参考下 1.resultMap SQL 映射XML 文件是所有sql语句放置的地方.需要定义一个workspace,一般定义为对应的接口类的路径.写好SQL语句映射文件后,需要在MyBAtis配置文件mappers标签中引用,例如: ? 1 2 3 4 5 6 <mappers>   <mapper resource="com/limi

Linux 内核中的 Device Mapper 机制

http://www.68idc.cn/help/server/linux/20141127133367.html 结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物 简介: 本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,

1.Relationship in Entity Framework Using Code First Approach With Fluent API【使用EF Code-First方式和Fluent API来探讨EF中的关系】

In this article, you will learn about relationships in Entity Framework using the Code First Approach with Fluent API. 在这篇文章中,你将会学习到使用EF Code-First方式和Fluent API来探讨EF中的关系(一对一,一对多,多对多). Introduction[介绍] A relationship, in the context of databases, is a

[转] Linux 内核中的 Device Mapper 机制

本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,当前比较流行的 Linux 下的逻辑卷管理器如 LVM2(Linux Volume Manager 2 version).EVMS(Enterprise Volume Management System).dmraid(Device M

PHP转Go系列:map映射

映射的定义 初识映射会很懵,因为在PHP中没有映射类型的定义.其实没那么复杂,任何复杂的类型在PHP中都可以用数组表示,映射也不例外. $array['name'] = '平也'; $array['sex'] = '1'; $array['age'] = '10'; //output Array ( [name] => 平也 [sex] => 1 [age] => 10 ) 映射其实就是有key有value的数组,在Go中的赋值也很类似,但需要提前声明该映射类型的键与值的类型,确保所有的