MFC - 什麼是 CMap?

 

Microsoft的公司在MFC中提供的類別CList,在資列結構中就是串列,如果以手工製造串列,加入、刪除,存取,動態配置記憶體加上指標,會有點複雜,學生時代也曾被搞得昏天暗地,一不小心還會產生Memory Leak,電腦怎麼死的都還覺得莫名奇妙。在MFC中提供這個類別,可大大提升效率。還是要提醒,如果你是初入門本科系學生,別投機取巧,還是腳踏實地的「昏天暗地」,這樣才能了解其中的訣竅,那天希望效能更高的時候,要手工打造的時候,才不會手足無措。

 

有雜湊函數都有如下一個基本特性:如果兩個雜湊值是不相同的(根據同一函式),那麼這兩個雜湊值的原始輸入也是不相同的。這個特性是雜湊函數具有確定性的結果。但另一方面,雜湊函數的輸入和輸出不是唯一對應關係的,如果兩個雜湊值相同,兩個輸入值很可能是相同的。但也可能不同,這種情況稱為「雜湊碰撞」,這通常是兩個不同長度的雜湊值,刻意計算出相同的輸出值。輸入一些資料計算出雜湊值,然後部分改變輸入值,一個具有強混淆特性的雜湊函數會產生一個完全不同的雜湊值。

典型的雜湊函數都有無限定義域,比如任意長度的位元組字串,和有限的值域,比如固定長度的位元串。在某些情況下,雜湊函數可以設計成具有相同大小的定義域和值域間的一一對應。一一對應的雜湊函數也稱為排列。可逆性可以透過使用一系列的對於輸入值的可逆「混合」運算而得到。

 

引用字afxtempl.h部份程式

template<class TYPE, class ARG_TYPE>

BOOL AFXAPI CompareElements(const TYPE* pElement1, const ARG_TYPE* pElement2)

{

      ENSURE(pElement1 != NULL && pElement2 != NULL);

      ASSERT(AfxIsValidAddress(pElement1, sizeof(TYPE), FALSE));

      ASSERT(AfxIsValidAddress(pElement2, sizeof(ARG_TYPE), FALSE));

 

      return *pElement1 == *pElement2;

}

 

 

 

 

 

 

 

 

template<class ARG_KEY>

AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)

{

      // (algorithm copied from STL hash in xfunctional)

      ldiv_t HashVal = ldiv((long)(ARG_KEY)key, 127773);

 

 

      //覆蓋掉原來的餘數

      //HashVal.rem=16807*原來的餘數-2836*原來的商

      HashVal.rem = 16807 * HashVal.rem - 2836 * HashVal.quot;

 

      //如果計算結果小於0

      if (HashVal.rem < 0)

            //再一次覆蓋掉餘數

            //HashVal.rem=第一次覆蓋的餘數+2147483647

            HashVal.rem += 2147483647;

 

      //返回餘數

      return ((UINT)HashVal.rem);

}

 

 

template<> AFX_INLINE UINT AFXAPI HashKey<__int64>(__int64 key)

{

      // (algorithm copied from STL hash in xfunctional)

      return (HashKey<DWORD>((DWORD)(key & 0xffffffffUL)) ^ HashKey<DWORD>((DWORD)(key >> 32)));

}

 

 

雜湊表雜湊函數的幾乎不可能/不切實際的理想是把每個關鍵字對映到唯一的索引上(參考完美雜湊),因為這樣能夠保證直接存取表中的每一個資料。

一個好的雜湊函數(包括大多數加密雜湊函數)具有均勻的真正隨機輸出,因而平均只需要一兩次探測(依賴於裝填因子)就能找到標的。同樣重要的是,隨機雜湊函數不太會出現非常高的衝突率。但是,少量的可以估計的衝突在實際狀況下是不可避免的(參考生日悖論鴿洞原理)。

 

 

CMap是一種類似字典Collection的類別,利用單一鍵值來映射儲存的變數值,說明如下:

 

Template < classKEY,class ARG_KEY,class VALUE ,class ARG_VALUE >Class CMap:Public CObject

 

前面的部份是類別樣板(class template),透過這個功能達到型態的變化,中間的部份則是型態引數,最後則是說明繼承(inherit)那個類別,CMap是繼承CObject

 

參數說明:

 

KEY:這物件類別試用當做鍵值映射用。

 

ARG_KEY:鍵值資料型,參考KEY資料型態。

 

VALUE:儲存在Map的物件類別。

 

ARG_MAP:儲存參數的資料型態,參考VALUE的資料型態。

 

 

補充說明:

 

你已經插入一組(key,value)Map中,你可以很有效率的依據鍵值(key)來擷取、刪除這組資料,你也可以透過指標(iterate)來巡視(指向)在儲存Map中的所有元素。

 

你可以使用POSITION型態的變數,來指向Map中資料位置,也可以記錄資料位置,並且可以利用這個紀錄的資料,指向Map中的該筆資料。或許你會認為指向這筆資料是透過鍵值來排序達成,事實上不是的,擷取資料的排序是沒有固定的順序的。

 

CMap變更CObject::Serialize的函式,支援序列化及元素的大量資料存取(dumping),如果Map儲存的模式是採用SerializeMap中的每一個元素依序列化(Serialized in turn )排序,SerializeElements是預設的執行方式,透過該函式資料會以位元(Bit)長度模式寫入(目前僅依字面意義翻譯,須查證實際情況來印證想像)

 

如果你需要檢視Map中個別元素資料內容(key,value),你必須設定檢視資料的長度,檢視資料長度至少為1或更多。

 

當一個CMap的物件被刪除時,或是元素被移除,則keyvalue整組資料都會被移除。

 

 

CMap Members

 

建構子

 

CMap

建構一個Collection,可以讓鍵值去映射到儲存值

 

Operations

 

GetHashTableSize

返回雜湊表(Hash Table,元素的個數)大小。

GetNextAssoc

取得目前指標的下一個元素。

PGetNextAssoc

取得目前指標的下一個元素指標(address)

GetStartPosition

取得第一個元素的位置(POSITION)

PGetFirstAssoc

取得第一個元素的指標(address)

InitHashTable

初始化雜湊表(Hash Table),並指定大小。

Lookup

尋找儲存值對應的鍵值

PLookup

指向儲存值吻合指定值的鍵值的指標。

Operator[]

插入元素到Map-這個操作子可以替代SetAt的功能函式。

RemoveAll

移除Map中的所有元素。

RemoveKey

移除指定鍵值的元素。

SetAt

插入元素至Map;如果有發現相同的鍵值,則會取代原來的元素。

 

Status

 

GetCount

返回串列元素的個數。

GetSize

返回串列元素的個數。

IsEmpty

測試串列是否為無任何元素狀態。

 

Data Members

 

CMap::CPair

巢狀結構包含鍵值(Key)和相關物件的儲存值。

 

 

標頭檔:

afxtempl.h

 

範例:

// MFC_CMap.cpp : 定義主控台應用程式的進入點。

//

 

#include "stdafx.h"

#include <iostream>

 

#include <afxtempl.h>

 

//-----------------------------------------------------------

 

//要使用CList一定要將afxtempl.h包含進來,

//並且專案屬性中的「MFC的使用」也要設定

#include <afxtempl.h>

 

//-----------------------------------------------------------

 

using namespace std;

 

 

int _tmain(int argc, _TCHAR* argv[])

{

   CList<int,int&> CL_Int_Data;

 

   int Int_Temp,Int_Loop1;

   POSITION Pos_Temp;

 

   //-----------------------------------------------------------

  

   cout<<"驗證 IsEmptyGetCountGetSize"<<endl;

   //IsEmpty:檢測是否為空串列

 

   Int_Temp=CL_Int_Data.IsEmpty();

   if(Int_Temp==1)

   {

      cout<<"目前是空的串列"<<endl;

   }

 

   //-----------------------------------------------------------

 

   //GetCount:檢測目前串列大小

   Int_Temp=CL_Int_Data.GetCount();

   cout<<"目前串列所含元素數目(GetCount)"<<Int_Temp<<endl;

 

   //GetSize:檢測目前串列大小

   Int_Temp=CL_Int_Data.GetSize();

   cout<<"目前串列所含元素數目(GetSize)"<<Int_Temp<<endl;

 

   cout<<endl<<endl;

 

   //-----------------------------------------------------------

 

   cout<<"驗證 AddHeadAddTail"<<endl;

 

   //AddHead:往頭的方向加入新元素

   Int_Temp=1;

   CL_Int_Data.AddHead(Int_Temp);

  

   Int_Temp=2;

   CL_Int_Data.AddHead(Int_Temp);

 

   //AddTail:往尾的方向加入新元素

   Int_Temp=-1;

   CL_Int_Data.AddTail(Int_Temp);

 

   Int_Temp=-2;

   CL_Int_Data.AddTail(Int_Temp);

 

  

   //GetHead:取出開頭位置的內容

   Int_Temp=CL_Int_Data.GetHead();

   cout<<"目前頭的位置內容值(GetHead)(2,1.-1,-2) "<<Int_Temp<<endl;

  

   //GetTail:取出結尾位置的內容

   Int_Temp=CL_Int_Data.GetTail();

   cout<<"目前頭的位置內容值(GetHead)(2,1.-1,-2) "<<Int_Temp<<endl;

  

  

   //IsEmpty:檢測是否為空串列

 

   Int_Temp=CL_Int_Data.IsEmpty();

   if(Int_Temp==1)

   {

      cout<<"目前是空的串列"<<endl;

   }

   else

   {

      cout<<"目前不是空串列"<<endl;

   }

 

   //GetCount:檢測目前串列大小

   Int_Temp=CL_Int_Data.GetCount();

   cout<<"目前串列所含元素數目(GetCount)"<<Int_Temp<<endl;

 

   //GetSize:檢測目前串列大小

   Int_Temp=CL_Int_Data.GetSize();

   cout<<"目前串列所含元素數目(GetSize)"<<Int_Temp<<endl;

 

   cout<<endl<<endl;

 

   //-----------------------------------------------------------

 

   cout<<"驗證 GetHeadPositionGetNextGetPrevGetTailPosition"<<endl;

   cout<<"驗證 GetAtRemoveAtSetAt"<<endl;

 

   //GetHeadPosition:取出串列開頭位置(POSITION)

   Pos_Temp=CL_Int_Data.GetHeadPosition ();

   cout<<"目前開頭位置(POSITION)的內容(GetHeadPositionGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

   //GetNext(POSTION):以現在位置(括號內的POSITION變數)往下一個位置移動

   CL_Int_Data.GetNext(Pos_Temp);

   cout<<"目前開頭位置(POSITION)移動到下一個位置的元素內容(GetNextGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

   CL_Int_Data.GetNext(Pos_Temp);

   cout<<"目前位置(POSITION)移動到下一個位置的元素內容(GetNextGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

   //GetPrev(POSITION):以現在位置(括號內的POSITION變數)往前一個位置移動

   CL_Int_Data.GetPrev (Pos_Temp);

   cout<<"目前位置(POSITION)移動到前一個位置的元素內容(GetPrevGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

 

   //GetTailPosition:取出串列結尾位置(POSITION)

   Pos_Temp=CL_Int_Data.GetTailPosition ();

   cout<<"目前結尾位置(POSITION)的內容(GetTailPositionGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

   //RemoveAt(POSITION):移除本位置元素

   CL_Int_Data.RemoveAt (Pos_Temp);

   Pos_Temp=CL_Int_Data.GetTailPosition ();

   cout<<"移除原結尾元素,目前結尾位置(POSITION)的內容(RemoveAtGetTailPositionGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

   //SetAt(POSITION):設定本位置元素

   Int_Temp=100;

   CL_Int_Data.SetAt (Pos_Temp,Int_Temp);

   cout<<"設定目前位置(POSITION)的內容(SetAtGetAt)"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

   cout<<endl<<endl;

 

   //-----------------------------------------------------------

  

   cout<<"驗證 RemoveHeadRemoveTail"<<endl;

 

   //RemoveHead:移除開頭位置元素

   CL_Int_Data.RemoveHead();

 

   //GetHead:取出開頭位置的內容

   Int_Temp=CL_Int_Data.GetHead();

   cout<<"目前頭的位置內容值(RemoveHead,GetHead)(1.100) "<<Int_Temp<<endl;

  

   //RemoveTail:移除結尾位置元素

   CL_Int_Data.RemoveTail();

 

   //GetTail:取出結尾位置的內容

   Int_Temp=CL_Int_Data.GetTail();

   cout<<"目前結尾的位置內容值(RemoveTail,GetTail)(1) "<<Int_Temp<<endl;

 

   //IsEmpty:檢測是否為空串列

 

   Int_Temp=CL_Int_Data.IsEmpty();

   if(Int_Temp==1)

   {

      cout<<"目前是空的串列"<<endl;

   }

   else

   {

      cout<<"目前不是空串列"<<endl;

   }

 

   //GetCount:檢測目前串列大小

   Int_Temp=CL_Int_Data.GetCount();

   cout<<"目前串列所含元素數目(GetCount)"<<Int_Temp<<endl;

 

   //GetSize:檢測目前串列大小

   Int_Temp=CL_Int_Data.GetSize();

   cout<<"目前串列所含元素數目(GetSize)"<<Int_Temp<<endl;

 

   cout<<endl<<endl;

 

   //-----------------------------------------------------------

  

   cout<<"驗證 InsertAfterInsertBefore"<<endl;

 

   //InsertAfter:插入一個元素至目前位置的下一個位置

   Pos_Temp=CL_Int_Data.GetHeadPosition ();

   Int_Temp=-3;

   CL_Int_Data.InsertAfter (Pos_Temp,Int_Temp);

   cout<<"串列(InsertAfter)(1,-3)"<<endl;

 

   Int_Temp=-2;

   CL_Int_Data.InsertAfter (Pos_Temp,Int_Temp);

   cout<<"串列(InsertAfter)(1,-2,-3)"<<endl;

 

   Int_Temp=-1;

   CL_Int_Data.InsertAfter (Pos_Temp,Int_Temp);

   cout<<"串列(InsertAfter)(1,-1.-2,-3)"<<endl;

 

   //InsertBefore:插入一個元素至目前位置的前一個位置

   Int_Temp=100;

   CL_Int_Data.InsertBefore (Pos_Temp,Int_Temp);

   cout<<"串列(InsertBefore)(100,1,-1.-2,-3)"<<endl;

 

   Int_Temp=120;

   CL_Int_Data.InsertBefore (Pos_Temp,Int_Temp);

   cout<<"串列(InsertBefore)(100,120,1,-1.-2,-3)"<<endl;

 

 

   //使用回圈將資料讀出

   Pos_Temp=CL_Int_Data.GetHeadPosition ();

 

   for(Int_Loop1=0;Int_Loop1<6;Int_Loop1++)

   {

      cout<<"串列資料["<<Int_Loop1<<"]="<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

      CL_Int_Data.GetNext (Pos_Temp);

   }

 

   cout<<endl<<endl;

 

   //-----------------------------------------------------------

  

   cout<<"驗證 FindFindIndex"<<endl;

  

   //Find:尋找特定 POSITION

   cout<<"請輸入搜尋資料:";

   cin>>Int_Temp;

   Pos_Temp=CL_Int_Data.Find(Int_Temp);

 

   if(Pos_Temp==NULL)

   {

      cout<<"未在串列中搜尋到資料"<<endl;

   }

   else

   {

      cout<<"串列資料存在搜尋資料:"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

   }

 

   //FindIndex:尋找特定索引值內容

   cout<<"請輸入尋找索引值位置:";

   cin>>Int_Temp;

   Pos_Temp=CL_Int_Data.FindIndex (Int_Temp);

   cout<<""<<Int_Temp<<"位置內容值:"<<CL_Int_Data.GetAt (Pos_Temp)<<endl;

 

 

 

   system("Pause");

 

   return 0;

}

 

 

 

 

 

 

執行結果 - 省略 

 

 

 

參考資料:

  1. MSDNCMap Classhttp://msdn.microsoft.com/zh-tw/library/s897094z(v=VS.100).aspx
  2. MSDNCMap Membershttp://msdn.microsoft.com/zh-tw/library/tw6s6xay.aspx
  3. 維基百科,雜湊函式(Hash Function)http://zh.wikipedia.org/wiki/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B8

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 向夢想飛行 的頭像
    向夢想飛行

    向夢想飛行

    向夢想飛行 發表在 痞客邦 留言(0) 人氣()