https://baike.baidu.com/item/CMap/2334917?fr=aladdin
CMap 编辑
CMap,计算机语言函数,作用是映射的关键码。
外文名 CMap 用 作 映射的关键码 包 括 KEY对象的类 属 于 参数KEY使用的数据类型
目录
1 参数
2 说明
▪ 构造函数
▪ 操作符
▪ 状态
3 CMap用法
参数编辑
KEY对象的类,用作映射的关键码。ARG_KEY参数KEY使用的数据类型,通常为KEY的参考。VALUE存储在映射中对象的类。ARG_VALUE参数VALUE使用的数据类型,通常为VALUE的参考。
说明编辑
CMap是把唯一关键码映射到值的字典收集类。一旦在映射中插入了一个关键码值对(元素),就可以使用这些关键码,有效地获取或删除对。同样,也可以反复使用映射中的所有元素。
POSITION类型变量用于替换所有映射变量的入口。可以使用POSITION来“记忆”入口和映射中的遍历。可能认为这种遍历是通过关键码值来依次进行的,但其实不是。获取元素的次序没有确定。
该类的某些成员函数调用了全局的帮助函数,它们必须定制,以满足CMap类的更多用途。请参阅“Microsoft Visual C++ MFC库参考”中的“宏和全局”部分中的“收集类帮助程序”。
CMap引入了宏IMPLEMENT_SERIAL,支持其元素的串行化和转储。如果映射存储到档案文件中,那么每一元素都可利用加载插入(<<)操作符或Serialize成员函数来依次进行串行化。如果要了解有关在映射中进行个别元素的诊断转储,那么转储内容的深度必须为1或更大。当CMap对象删除或其元素被删除,那么关键码和值都将被删除。映射类的派生与列表的派生相似。
请参阅联机文档“Visual C++程序员指南”中的“收集”部分,以进一步了解特殊用途的列表类的派生。
#include <afxtempl.h>
CMap类的成员
构造函数
CMap构造一个映射关键码为值的收集
操作符
Lookup查找与指定关键码对应的值;SetAt在映射中插入一个元素,但假如发现了相匹配的关键码,则替换已经存在的元素;operator 在映射中插入一个元素,它是代替SetAt的操作符;RemoveKey删除关键码指定的元素;RemoveAll删除映射中所有的元素;GetStartPosition返回第一个元素的位置;GetNextAssoc获取循环中下一个元素;GetHashTableSize返回散列表的大小(元素的个数);InitHashTable初始化散列表,并指定其大小。
状态
GetCount返回映射中元素的个数;IsEmpty测试是否为空映射(即没有元素)。
CMap用法编辑
归根到底,CMap是用CPair来存放数据的,CPair的形式是{KEY, VALUE}。因此CMap实际存放的是KEY,而不是ARG_KEY。但是,如果你查阅MFC的代码,你会发现几乎所有的CMap成员函数的参数都标有ARG_KEY和ARG_VALUE类型,所以,用KEY&来作为ARG_KEY的类型通常是正确的,除非:
1. 你使用原子类型数据类型如int, char,此时值传递和引用传递并没有什么差别(甚至值传递更快)。
2. 如果你用CString作为键(KEY)类型,你应使用LPCTSTR作为ARG_KEY的类型,而不是用CString&,原因我稍后说明。
我如何将CMap用于我自己的类ClassX正如我刚才提到的,CMap是一种Hash Map,Hash Map要求每个元素都要有一个Hash值——一个关于KEY的函数,Hash Map用这个值作为Hash表的索引。如果有多个KEY的Hash值相同,它们将以链表的方式存储。所以,你要做的第一件事就是提供一个Hash函数。
CMap会调用模板函数HashKey()来计算Hash值。
默认的实现以及对于LPCSTR和LPCWSTR的专门实现如下:
// 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;
}
如你所见,缺省行为会“假定”KEY是一个指针,并将它转换为DWORD类型,这就是为什么当你没有为你的ClassX提供一个专门的HashKey()时你会得到“error C2440: ''type cast'': cannot convert from ''ClassXXX'' to ''DWORD_PTR''”错误的原因。
同时,因为MFC只是实际了LPCSTR和LPCWSTR的专门化,并没有实现CStringA和CStringW的专门化,因此如果你想使用CString作为CMap的键类型,你要声明为CMap<CString, LPCTSTR, ……>。
好了,现在你知道CMap如何计算Hash值了,但是由于可能会有多个键的Hash值相同,CMap需要遍历整个链表来找到要求的数据,而不只是在相同的Hash值中。并且当CMap进行匹配时,会调用CompareElements(),这是另一个模板函数。
// 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;
}
因此,如果你想让CMap用于你自己的类ClassX,就必须提供HashKey()和CompareElements()的专门化实现。
示例:CMap用于CString*作为一个例子,以下说明了要将CMap用于CString*前你需要做的。当然了,是使用字符串的值作为键(KEY),而不是用指针的地址。
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;
}
}
Main函数如下:
int _tmain(int argc, TCHAR* argv, TCHAR* envp)
...{
CMap<CString*, CString*, int, int> map;
CString name1 = "Microsoft";
CString name2 = "Microsoft";
map = 100;
int x = map;
printf("%s = %d ", (LPCTSTR)name1, x);*/
return 0;
}
--------- console output ---------
Microsoft = 100
注意即使你没有提供HashKey()和CompareElements()的专门化编译器也不会报错,但这样的话输出为0,或许不是你想要的。
关于CMap的总结CMap是一种Hash Map而STL::map是Tree Map,比较两者的效率是没有意义的(如同比较苹果和桔子!)。但是如果你要按顺序取得关键字,你需要使用STL::map。
HashKey()的设计是效率的关键。你应该提供一个低碰撞(即不同的关键字产生相同的Hash值)率、容易计算(而不是像MD5那样)的HashKey()。我们必须注意这点——至少对于某些类来说——并不是件容易的事。
当使用CMap(以及STL::hash_map)时,注意Hash表的大小。引用一段MSDN的说明:“Hash表的大小应该是一个质数。为了减少碰撞,Hash表的大小应该超出最大预计容量的20%。默认情况下,CMap的Hash表大小为17,这对于10个关键字左右的数据很合适。你可以用InitHashTable(UINT nHashSize)来改变Hash表的大小,并且只能在加入第一个元素之前这样做。你可能在这里找到很多质数。(不要与CMap(UINT nBlockSize)弄混了,nBlockSize用于获得多个CAssoc来加速创建新结点。)
Last edited by zzz19760225 on 2017-12-11 at 19:53 ]
https://baike.baidu.com/item/CMap/2334917?fr=aladdin
CMap Editing
CMap is a function in the computer language, which functions as the key of the mapping.
English Name CMap Function Used as the key of the mapping Includes Classes of KEY Objects Belongs to Data type used by parameter KEY
Table of Contents
1 Parameters
2 Explanation
▪ Constructor
▪ Operators
▪ Status
3 Usage of CMap
Parameter Editing
Classes of KEY objects, used as the key of the mapping. ARG_KEY data type used by parameter KEY, usually a reference of KEY. VALUE classes of objects stored in the mapping. ARG_VALUE data type used by parameter VALUE, usually a reference of VALUE.
Explanation Editing
CMap is a dictionary collection class that maps unique keys to values. Once a key-value pair (element) is inserted into the mapping, these keys can be used to effectively obtain or delete the pairs. Similarly, all elements in the mapping can be reused repeatedly.
The POSITION type variable is used to replace the entries of all mapping variables. POSITION can be used to "remember" the entries and traversal in the mapping. You may think that this traversal is carried out in sequence through key values, but actually it is not. The order of obtaining elements is not determined.
Some member functions of this class call global helper functions, which must be customized to meet more uses of the CMap class. Please refer to the "Macro and Global" part in the "Microsoft Visual C++ MFC Library Reference" for "Collection Class Helpers".
CMap introduces the macro IMPLEMENT_SERIAL, which supports serialization and dumping of its elements. If the mapping is stored in an archive file, each element can be serialized one by one using the loading insertion (<<) operator or the Serialize member function. If you want to understand the diagnostic dumping of individual elements in the mapping, the depth of the dumped content must be 1 or greater. When the CMap object is deleted or its elements are deleted, both the key and the value will be deleted. The derivation of the mapping class is similar to the derivation of the list.
Please refer to the "Collections" part in the online document "Visual C++ Programmer's Guide" to further understand the derivation of special-purpose list classes.
#include <afxtempl.h>
Members of the CMap class
Constructor
CMap constructs a collection where keys are mapped to values.
Operators
Lookup finds the value corresponding to the specified key; SetAt inserts an element into the mapping, but if a matching key is found, the existing element is replaced; operator inserts an element into the mapping, which is an operator instead of SetAt; RemoveKey deletes the element specified by the key; RemoveAll deletes all elements in the mapping; GetStartPosition returns the position of the first element; GetNextAssoc gets the next element in the cycle; GetHashTableSize returns the size of the hash table (number of elements); InitHashTable initializes the hash table and specifies its size.
Status
GetCount returns the number of elements in the mapping; IsEmpty tests whether it is an empty mapping (i.e., has no elements).
Usage of CMap Editing
In the final analysis, CMap stores data with CPair, and the form of CPair is {KEY, VALUE}. Therefore, CMap actually stores KEY, not ARG_KEY. However, if you refer to the code of MFC, you will find that the parameters of almost all CMap member functions are marked with ARG_KEY and ARG_VALUE types. So, using KEY& as the type of ARG_KEY is usually correct, unless:
1. You use atomic data types such as int, char. At this time, there is no difference between value passing and reference passing (even value passing is faster).
2. If you use CString as the key (KEY) type, you should use LPCTSTR as the type of ARG_KEY instead of using CString&. The reason will be explained later.
How do I use CMap for my own class ClassX? As I just mentioned, CMap is a Hash Map. Hash Map requires that each element has a Hash value - a function about KEY. Hash Map uses this value as the index of the hash table. If the Hash values of multiple KEYs are the same, they will be stored in the form of a linked list. So, the first thing you need to do is provide a Hash function.
CMap will call the template function HashKey() to calculate the Hash value.
The default implementation and the specialized implementations for LPCSTR and LPCWSTR are as follows:
// 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 will "assume" that KEY is a pointer and convert it to DWORD type. This is why you will get the "error C2440: ''type cast'': cannot convert from ''ClassXXX'' to ''DWORD_PTR''" error when you do not provide a specialized HashKey() for your ClassX.
At the same time, because MFC only implements the specialization of LPCSTR and LPCWSTR, and does not implement the specialization of CStringA and CStringW, so if you want to use CString as the key type of CMap, you should declare it as CMap<CString, LPCTSTR, ……>.
Okay, now you know how CMap calculates the Hash value. But because there may be multiple keys with the same Hash value, CMap needs to traverse the entire linked list to find the required data, not just in the same Hash value. And when CMap performs matching, it will call CompareElements(), which is another template function.
// 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 for your own class ClassX, you must provide specialized implementations of HashKey() and CompareElements().
Example: CMap used for CString* As an example, the following illustrates what you need to do before using CMap for CString*. Of course, the value of the string is used as the key (KEY), not the address of the pointer.
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;
}
}
The main function is as follows:
int _tmain(int argc, TCHAR* argv, TCHAR* envp)
...{
CMap<CString*, CString*, int, int> map;
CString name1 = "Microsoft";
CString name2 = "Microsoft";
map = 100;
int x = map;
printf("%s = %d ", (LPCTSTR)name1, x);*/
return 0;
}
--------- console output ---------
Microsoft = 100
Note that even if you do not provide specialized implementations of HashKey() and CompareElements(), the compiler will not report an error, but in this case the output is 0, which may not be what you want.
Summary of CMap CMap is a Hash Map and STL::map is a Tree Map. It is meaningless to compare the efficiencies of the two (it is like comparing apples and oranges!). But if you want to obtain keywords in order, you need to use STL::map.
The design of HashKey() is the key to efficiency. You should provide a HashKey() with a low collision rate (i.e., different keywords generate the same Hash value) and easy to calculate (not like MD5). We must pay attention to this - at least for some classes - it is not an easy task.
When using CMap (and STL::hash_map), pay attention to the size of the hash table. Quoting a note from MSDN: "The size of the hash table should be a prime number. To reduce collisions, the size of the hash table should exceed 20% of the maximum expected capacity. By default, the hash table size of CMap is 17, which is suitable for data with about 10 keywords. You can use InitHashTable(UINT nHashSize) to change the size of the hash table, and you can only do this before adding the first element. You may find many prime numbers here. (Do not confuse with CMap(UINT nBlockSize), nBlockSize is used to obtain multiple CAssoc to speed up the creation of new nodes.)
Last edited by zzz19760225 on 2017-12-11 at 19:53 ]