CMap的用法

来源:互联网 发布:arashi smap 地位知乎 编辑:程序博客网 时间:2024/04/28 08:05
 

如何声明CMAP

许多人对Cmap的声明模式CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>感到迷惑,为什么不用CMap<KEY,VALUE>呢?

归根到底,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()的专门化实现。

1.HashKey的专门化实现

第一种方式:

 示例:CMap用于CString*作为一个例子,以下说明了要将CMap用于CString*前你需要做的。当然了,是使用字符串的值作为键(KEY),而不是用指针的地址。

  template<>

  UINT AFXAPI HashKey<CString*> (CString* key)

  ...{

  return (NULL == key) ? 0 : HashKey((LPCTSTR)(*key));

  }

第二种方式:

在自己的类里面定义operator unsigned long() 函数

2、CompareElements()的专门化实现。

  只需在自己的类里定义operator== 函数即可。