STL中的集合類數組、列表、關聯,MFC中也有相似的類,下面列出MFC中的一些使用方法。
實際上集合類就是資料結構中的順序儲存結構和鏈表格儲存體結構。它的優點就是建立這樣的儲存結構簡單,並且把相關的操作集中成函數,方便使用者的調用。比如順序儲存用到數組,如果要在數組中的固定位置添加一項,普通的方法就是要用一個for迴圈,有點煩瑣。而在集合類中只要用一個add函數就完成了,相對簡單一點。
資料結構是軟體設計中的一個重要組成部分。因此我們有必要搞清楚集合類的相關問題。
1.3種類型
集合類中有3種大的類型:
(1) Array: 數組,可以動態改變大小,有索引值和最大下標等。
(2) List:雙向鏈表,無索引,鏈表有頭尾,插入元素要比數組快。
(3) Map:是一種映射,俗名“字典”,是一種關聯式數組。(在此不做討論)
2.各自特點:
類型 |
是否有序 |
插入元素速度 |
搜尋元素速度 |
索引 |
Array |
有 |
慢 |
慢 |
有 |
List |
有 |
快 |
慢 |
無 |
3.MFC中的集合類
類 |
是否使用模板 |
是否支援序列化 |
是否支援傾印 |
CArray |
是 |
是 |
是 |
CTypedPtrArray |
是 |
可能 |
是 |
CByteArray |
否 |
是 |
是 |
CDWordArray |
否 |
是 |
是 |
CObArray |
否 |
是 |
是 |
CPtrArray |
否 |
否 |
是 |
CStringArray |
否 |
是 |
是 |
CWordArray |
否 |
是 |
是 |
CUIntArray |
否 |
否 |
是 |
CList |
是 |
是 |
是 |
CTypedPtrList |
是 |
可能 |
是 |
COblist |
否 |
是 |
是 |
CStringList |
否 |
是 |
是 |
4.幾點說明
(1) 有些集合類是從c++模板支援的,如CArray,CList,使用時必須指出所要收集對象的類型,比如int,char,CPoint等,可以參考一下c++模板的知識。
(2)在表中有2個“可能“,因為CTypedPtrArray,CTypedPtrList這些類在使用時要指定基類,如果基類可以序列化,那麼它就可以序列化。
(3)在深入淺出MFC中,作者說CUIntArray是可以序列化的,但我在做項目時證明它是不可以序列化的,希望讀者注意
如一個對象類:
Void CStudent::Serialize(CArchive &ar)
{
……
m_array.Serialize(ar);//這裡m_array是CUIntArray的對象
}
可是運行後發現並沒有成功。因此只好採用for迴圈:
Void CStudent::Serialize(CArchive &ar)
{
If(ar.IsStoring( ))//儲存
{
ar<<m_array.GetSize();
for(int i=0;i<m_array.GetSize();i++)
ar<<m_array.GetAt(i)
}
…..//讀取略
}
5.常用函數舉例
(1) 數組
如int 數組:
CArray<int,int> m_intArray;
m_intArray.Add(15); // 添加一個元素
CArray<CPoint,CPoint> pArray;
pArray.Add(CPoint(10,10));
l 添加元素
注意,此時開始並沒有分配數組的儲存空間,但是add可以動態分配空間。如果可以預計數組大小,可以先用SetSize()來分配空間,因為如果頻繁使用add,會產生記憶體片段。SetSize可以增加數組元素,也可以減少,但是在減少時,並不會自動縮小儲存數組資料的緩衝區,還是先調用removeAt先把元素刪掉。
推薦使用:SetAtGrow(int index,ARG_TYPE newElement ),它與Add相比,就是可以利用它修改數組中的數組;而如果用add,那麼必須先RemoveAll
l 獲得元素個數和最大下標
GetSize():可以返回數組中元素的個數。
GetUpperBound():返回數組中的最大下標,一般加1就和GetSize()相等。
l 獲得成員值
一般可以用GetAt(),有時可能要強制類型轉化。如:
CObArray array;
Cline *pline=new Cline(100,100,200,200);//Cline為直線類,用起點和終點座標初始化
array.Add(pline);//此處儲存的只是一個地址
Cline *p=(Cline*)array.GetAt(0);//必須強制類型轉換
注意這裡必須使用new動態建立記憶體空間。如果是局部變數,等到函數結束,就不能夠再通過數組來引用這塊記憶體了,因為已經析構。
當然也可以不用類型轉化。此時可以用集合類CTypedPtrArray,例如
CTypedPtrArray<CobArray,Cline*) array ;//表示array是CobArray對象,專門儲存Cline*指標
Cline *pline=new Cline(100,100,200,200);//Cline為直線類,用起點和終點座標初始化
array.Add(pline);
Cline *p=array.GetAt(0);//不用強制類型轉換
l 修改成員值
一般可以使用函數SetAt(),但是在修改之前,這個元素的記憶體空間必須已經分配,比如下面這樣寫是錯誤的:
CUIntArray Array;
Array.SetAt(0,10) ;//想把第一項修改為10;
可以這樣寫來修改元素的值:
CUIntArray Array;
Array.Add(5) ;
Array.SetAt(0,10) ;//把第一項5修改為10;
另外用數群組成員引用符號[]來表示也可以來修改或者是獲得元素值。
CUIntArray Array;
Array.Add(5) ;
Array[0]=5 ;//把第一項5修改為10;
同樣 這樣寫是錯誤的:
CUIntArray Array;
Array.Add(5) ;
Array.GetAt(0)=5 ;//想把第一項5修改為10;
l 刪除元素
RemoveAt():刪除一個元素,刪除後,數組中的索引號會自動改變。
RemoveAll():刪除所有元素。
注意如果要同時刪除幾個元素,必須從後面刪除起。
比如:
CUIntArray Array;
Array.Add(5) ;
Array.Add(10);
Array.Add(13);
Array.RemoveAt(0);
Array.RemoveAt(1);
本來是要刪除第1個元素和第二個元素,但結果卻是把第一個元素和第三個元素刪除了,因為當調用Array.RemoveAt(0);刪除第一個元素後,索引號開始變化,元素10成為索引要從1變為0,而元素13的索引號從2變為1。解決這個問題可以從後面開始刪除:
CUIntArray Array;
Array.Add(5) ;
Array.Add(10);
Array.Add(13);
Array.RemoveAt(1);
Array.RemoveAt(0);
只是改變了順序,效果卻大不相同哦!
(2) 鏈表
l 新增成員
Clist<int,int> m_intlist ;
m_intlist.AddTail(36) ;//在尾部添加
m_intlist.AddHead(34) ;//在頭部添加
l 遍曆
POSITION pos=m_intlist.GetHeadPosition() ;
While(pos !=NULL)
{
int i=m_intlist.GetNext(pos) ;
…
}//數組中有索引號,要方便一些。
其他用法數組差不多,不作介紹。
6. 動態建立的含義
舉例說明:
還是有個直線類,現在要序列化儲存。
l 添加元素
Cline *line =new Cline(100,100,200,200);
m_array.Add(line); //CObArray m_array; 類中的成員變數
l
在文檔類中儲存和讀取
Void CMyDocument::Serialize(CArchive & ar)
{
m_array.Serialize(ar);
}
實際上在m_array.Serialize(ar);的內部實現用到了for迴圈,一個一個元素的儲存和讀取的。
前面已經說過line必須是動態分配空間的,所以儲存是可以理解的。但是如果關閉這個程式,所有記憶體全部釋放,此時再次運行程式,開啟檔案,肯定會調用m_array.Serialize(ar);,在內部又是採用for迴圈一個一個讀取,但是這裡並我並沒有動態分配空間,它從磁碟讀取的內容(Cline)到底存在哪裡啊?
在深入淺出MFC中給出了很好的說明。在調用CArchive中這個重載符號”>>”,也就是讀取時,內部會再次調用一系列的函數,有一個是CreateObject(),它的代碼就是
CObject *Cline ::CreateObject()
{
return new Cline;
}