CMap

CMap

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[&name1] = 100;

int x = map[&name2];

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來加速創建新結點。)

相關詞條

相關搜尋

熱門詞條

聯絡我們