CMap在用CString做key类型时,ARG_KEY要选LPCTSTR 
文章来源:http://blog.csdn.net/flyingxu/archive/2005/12/26/562852.aspx

遇到好几个人说CMap在用CString做key类型时有问题,说用int和DWORD就可以,用CString就不行。因此很多人推荐使用MFC中的CMapStringToPtr之类。

看下面的代码:
//.h
    CMap<CString, LPCTSTR, int, int> typeMap;
//.cpp
    typeMap.SetAt(_T("ONE"),1);
    typeMap.SetAt(_T("TWO"),2);

int nValue = 0;
    BOOL ret = typeMap.Lookup(_T("ONE"), nValue);
    ret = typeMap.Lookup(_T("THREE"), nValue);
    ret = typeMap.Lookup(_T("TWO"), nValue);
我的代码运行的很好,我觉得关键是ARG_KEY要选LPCTSTR

原因:  referencehttp://www.codeproject.com/KB/architecture/cmap_howto.aspx

Introduction

Programmers like me, who learnt STL::map before CMap, always think CMap is difficult to use, and always try to useCMap in the way as a STL::map. In this article, I will explain about CMap and what you should do to use it for your own custom classes. And at the end of this article, I will show an example of how to use CMap correctly withCString* (note, I mean CString pointer and not CString :>)

CMap Internal

The first thing to be noted is that CMap is actually a hash map, and not a tree map (and usually a Red-black tree) asSTL::map. Shown below is the internal structure of a CMap.

How to declare a CMap

Many people get confused about CMap's declaration CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>, why not justCMap<KEY, VALUE>?

In fact, the ultimate data container in CMap is CPair, and the internal of CPair is {KEY, VALUE}. Therefore, CMapwill really store a KEY, and not ARG_KEY. However, if you check with the MFC source code, almost all the internal parameters passing within CMap itself is called with ARG_KEY and ARG_VALUE, therefore, using KEY& as ARG_KEYseems always a correct thing, except when:

  1. You are using primitive date types like intchar, where pass-by-value makes no difference (may be even faster) with pass-by-reference.
  2. If you use CString as KEY, you should use LPCTSTR as ARG_KEY and not CString&, we will talk more about this later.

So what should I do to make CMap work with my ClassX

Well, as I mentioned earlier, CMap is a hash map, a hash map will try to get the "hash value" -- a UINT -- from the key, and use that hash value as the index in the hash table (well, actually it is hash value % hash table size). If more then one key have the same hash value, they will be linked in a linked list. Therefore, the first thing you have to do is to provide a hash function.

CMap will call a templated function HashKey() to do the hashing. The default implementation and specialized version for LPCSTR and LPCWSTR are listed as follows:

 Collapse| Copy Code
// inside <afxtemp.h>

template<class ARG_KEY>
AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)
{// default identity hash - works for most primitive values
return (DWORD)(((DWORD_PTR)key)>>4);
}// inside <strcore.cpp>

// specialized implementation for LPCWSTR

#if _MSC_VER >= 1100
template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key)
#else
UINT AFXAPI HashKey(LPCWSTR key)
#endif
{UINT nHash = 0;while (*key)nHash = (nHash<<5) + nHash + *key++;return nHash;
}// specialized implementation for LPCSTR

#if _MSC_VER >= 1100
template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key)
#else
UINT AFXAPI HashKey(LPCSTR key)
#endif
{UINT nHash = 0;while (*key)nHash = (nHash<<5) + nHash + *key++;return nHash;
}

As you can see, the default behavior is to "assume" that the key is a pointer, and convert it to DWORD, and that's why you will get "error C2440: 'type cast': cannot convert from 'ClassXXX' to 'DWORD_PTR'" if you don't provide a specialized HashKey() for your ClassX.

And because MFC only has specialized implementations for the LPCSTR and LPCWSTR, and not for CStringA norCStringW, if you want to use CString in CMap, you have to declare CMap<CString, LPCTSTR....>.

OK, now you know how CMap calculates the hash value, but since more than one key may have the same hash value,CMap needs to traverse the whole linked list to find the one with exactly the same key "content", not only with the same "hash value". And when CMap does the matching, it will call CompareElements(), another templated function.

 Collapse| Copy Code
// inside <afxtemp.h>

// noted: when called from CMap,

//        TYPE=KEY, ARG_TYPE=ARG_TYPE

// and note pElement1 is TYPE*, not TYPE

template<class TYPE, class ARG_TYPE>
BOOL AFXAPI CompareElements(const TYPE* pElement1, const ARG_TYPE* pElement2)
{ASSERT(AfxIsValidAddress(pElement1, sizeof(TYPE), FALSE));ASSERT(AfxIsValidAddress(pElement2, sizeof(ARG_TYPE), FALSE));// for CMap<CString, LPCTSTR...>
// we are comparing CString == LPCTSTR
return *pElement1 == *pElement2;
}

Therefore, if you want to use CMap with your own custom ClassX, you will have to provide a specialized implementation for HashKey() and CompareElements().

Example: CMap with CString*

Provided as an example, below is what you need to do to make CMap work with CString*, and of course, using the string content as the key, and not the address of the pointer.

 Collapse| Copy Code
template<>
UINT AFXAPI HashKey<CString*> (CString* key)
{return (NULL == key) ? 0 : HashKey((LPCTSTR)(*key));
}// I don't know why, but CompareElements can't work with CString*

// have to define this

typedef CString* LPCString;template<>
BOOL AFXAPI CompareElements<LPCString, LPCString> (const LPCString* pElement1, const LPCString* pElement2)
{if ( *pElement1 == *pElement2 ) {// true even if pE1==pE2==NULL
return true;} else if ( NULL != *pElement1 && NULL != *pElement2 ) {// both are not NULL
return **pElement1 == **pElement2;} else {// either one is NULL
return false;}
}

And the main program is as simple as:

 Collapse| Copy Code
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{CMap<CString*, CString*, int, int> map;CString name1 = "Microsoft";CString name2 = "Microsoft";map[&name1] = 100;int x = map[&name2];printf("%s = %d\n", (LPCTSTR)name1, x);*/return 0;
}
 Collapse| Copy Code
--------- console output ---------
Microsoft = 100

Please note that the program can compile without error even without the specialized HashKey() andCompareElements(), but of course, the output will then be 0, probably not what you want.

My final note about CMap

  1. CMap is a hash map and STL::map is a tree map, there is no meaning comparing the two for performance (it would be like comparing apples and oranges!). But if you will retrieve the keys in sorted order, then you will have to use STL::map.
  2. The design of HashKey() is critical to the overall performance. You should provide a HashKey() that has a low collision rate (different keys generally would have different hash values) and is easy to calculate (not a MD5 of the string, etc..). And we have to note that -- at least for some of the classes -- this is not easy.
  3. When using CMap (as well as STL::hash_map), always beware of the hash table size. As quoted in MSDN, "the hash table size should be a prime number. To minimize collisions, the size should be roughly 20 percent larger than the largest anticipated data set". By default, CMap hash table size is 17, which should be okay for around 10 keys. You can change the hash table size with InitHashTable(UINT nHashSize), and can only do so before the first element is added to the map. You can find more prime numbers here. (And don't mix-up with CMap(UINT nBlockSize)nBlockSize is to acquire more than one CAssoc to speed up the creation of a new node.)

CMap在用CString做key类型时,ARG_KEY要选LPCTSTR相关推荐

  1. Mysql的int和bigint字段类型,映射到Java的Integer和Long类型时,勾选UNSIGNED无符号会导致越界转换。

    使用Mysql查询数据时,自动映射数据类型. 有时候Mysql的int字段,会正确映射到Java的Integer类型,有时候又会映射到Java的Long类型上,最后终于找到原因. 原来是Mysql的i ...

  2. 《编写高质量代码:改善c程序代码的125个建议》——建议4-1:整数转换为新类型时必须做范围检查...

    本节书摘来自华章计算机<编写高质量代码:改善c程序代码的125个建议>一书中的第1章,建议4-1,作者:马 伟 更多章节内容可以访问云栖社区"华章计算机"公众号查看. ...

  3. 《编写高质量代码:改善c程序代码的125个建议》——建议4-2:浮点数转换为新类型时必须做范围检查...

    本节书摘来自华章计算机<编写高质量代码:改善c程序代码的125个建议>一书中的第1章,建议4-2,作者:马 伟 更多章节内容可以访问云栖社区"华章计算机"公众号查看. ...

  4. zabbix计算型监控项函数last_zabbix 自定义key类型之计算(Calculated items)-阿里云开发者社区...

    计算类型的key定义主要是根据已定义过的key值来计算的.例如创建一个计算linux服务器内存实际使用大小的监控项(计算方法为:vm.memory.size[total]-vm.memory.size ...

  5. mysql where varchar_MySQL数据库之MySQL索引使用:字段为varchar类型时,条件要使用''包起来...

    本文主要向大家介绍了MySQL数据库之MySQL索引使用:字段为varchar类型时,条件要使用''包起来 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 结论: 当MySQL中 ...

  6. 企业在做搜索引擎优化时应该注意什么?

    这是一个互联网的时代,每个企业都拥有自己的官方网站,以搜索营销为主的企业,通常在SEO优化中,都会投入大量的资源去获取搜索流量. 对于一些初创企业,刚刚进入SEO行业的时候,经常会遭遇到各种问题,有的 ...

  7. 当你在做SAAS产品时,这些坑你需要了解

    我们卷起袖子来做一款SAAS产品时,发现面前的选择和困难是与消费级应用完全不同的,当我们在做消费级应用时,我们关注核心功能的体验价值和用户交互,产品不需要太复杂,一定要让用户有最清晰的使用路径,不要设 ...

  8. 做什么类型的产品最有成就感,最有趣?

    本文首发于公众号「原住民的自修室」,记录一名产品经理的生活和思考,欢迎订阅. 作为一名产品经理,我做过电商,游戏,to K12的教育工具:作为一名开发者,写过一些简单的App,比如倒数日记录工具,屏幕 ...

  9. mysql varchar 索引_MySQL索引使用:字段为varchar类型时,条件要使用''包起来

    结论: 当MySQL中字段为int类型时,搜索条件where num='111' 与where num=111都可以使用该字段的索引. 当MySQL中字段为varchar类型时,搜索条件where n ...

最新文章

  1. 算法65----字符串
  2. 07/11/20 资料整理
  3. ustc小道消息20220107
  4. OpenCV cv :: Mat的串行输出功能的实例(附完整代码)
  5. python创建excel_python创建Excel文件数据的方法
  6. Sharepoint的文档库用资源管理器方式浏览报错“Explorer View ”解决方案。
  7. 前端学习(3248):react的生命周期getSnapBeforeUpdate举例
  8. asp.net mvc3.0第一个程序helloworld开发图解
  9. html读写txt文件,JS读写文本文件示例代码
  10. SQL Server维护计划自动备份数据库
  11. 恒玉佳壬二酸使用方法,15壬二酸能帮助祛痘吗
  12. FlashBuilder找不到所需要的AdobeFlashPlayer调试器版本的解
  13. 从一个例子来看Tagged Pointer特性
  14. 直播回顾 |「星轨巡讲」技术分享会亮点速递
  15. 沙滩啤酒win7主题【rmzt】
  16. PyEcharts 之 仪表盘
  17. 2020年5G通信工程类项目一览,哪些企业成功抢滩?
  18. 盘点Python那些简单实用的第三方库
  19. 聊聊互联网平台的四个效应
  20. hyperf搭建与使用RPC微服务

热门文章

  1. 机房运维很枯燥?来看看中国银行数据中心基础设施可视化运维管理
  2. 平流式沉淀池计算例题_平流式沉淀池计算例题
  3. Py之pandas:字典格式数据与dataframe格式数据相互转换并导出到csv
  4. Interview:算法岗位面试—10.24下午—上海某软件公司(机器学习,上市)电话面试—考察SVM、逻辑回归、降低过拟合、卷积网络基础等
  5. ML之模型文件:机器学习、深度学习中常见的模型文件(.h5、.keras)简介、h5模型文件下载集锦、使用方法之详细攻略
  6. ML之NB:利用NB朴素贝叶斯算法(CountVectorizer/TfidfVectorizer+去除停用词)进行分类预测、评估
  7. JAVA_OA(六):SpringMVC处理异常
  8. golang 学习 (八)协程
  9. 【Linux】【Services】【SaaS】Docker+kubernetes(13. 部署Jenkins/Maven实现代码自动化发布)...
  10. Linux系统新建用户用ssh远程登陆显示-bash-4.1$