MFC中動態陣列CArray的使用
MFC中動態陣列CArray的使用
陣列——這個C語言中的怪物,自從被直接繼承到C++中,便讓無數有識之士們前赴後繼、絞盡腦汁,試圖尋找一種可以動態增長的替代資料型別。當然,最著名的,應該就是vector向量。但是,它的資料定義極其複雜,還有迭代的出現,幾乎徹底摧毀了它僅存的一點優勢。所以,引入MFC之後,微軟斬釘截鐵地拋棄了標準C++的模板庫。
CArray是MFC中非常重要的幾個類模板之一,其他的還有CList、CMap等,但它們的定義略微有點晦澀。以我自己的經驗,在MFC中使用CArray定義動態陣列是非常方便的。在MSDN中,CArray的宣告如下:
template< class TYPE, class ARG_TYPE > class CArray : public CObject
引數
TYPE
模板引數,指定儲存在陣列中物件的型別。TYPE是CArray返回的引數型別。
ARG_TYPE
模板引數,指定用來訪問儲存在陣列中物件的變數型別,通常是TYPE的引用。ARG_TYPE是傳遞給CArray的引數型別。
備註
CArray類支援與C中相似的陣列,但是必要時可以動態收縮和增長。陣列索引總是從0開始。你可以決定是固定陣列上界還是允許當新增元素超過當前邊界時擴充套件陣列。記憶體被連續地分配到上界,即使一些元素可能為空。和C中陣列一樣,CArray索引元素的訪問時間是不變的,與陣列大小無關。
提示 在使用一個數組之前,使用SetSize建立它的大小和為它分配記憶體。如果不使用SetSize,則為陣列新增元素就會引起頻繁地重新分配和拷貝。頻繁地重新分配和拷貝不但沒有效率,而且會導致記憶體碎片。
如果需要一堆陣列中的個別資料,必須設定CDumpContext物件的深度為1或更大。
此類的某些成員函式呼叫全域性幫助函式,它必須為CArray的大多數使用而定製。請參閱巨集和全域性量章節中的Collection Class Helpers。
當從一個CArray物件中移去元素時,幫助函式DestructElements被呼叫。當新增元素時,幫助函式ConstructElements被呼叫。
陣列類的派生與列表的派生類似。
有關使用CArray類的更多資訊,請參考Visual C++ Programmer's Guide中的論文集。
#include <afxtempl.h>
看到上面這麼一大段文字是不是覺得有點頭暈?MSDN就是這樣的,不然怎麼能體現出它的“權威”呢。其實不要覺得它很複雜,使用CArray類構造動態陣列非常簡單。首先,你需要包含標頭檔案Afxtempl.h,然後就可以定義自己的動態陣列了。例如定義一個int型和CPoint型的動態陣列:
#include <afxtempl.h>
CArray <int, int> num;
CArray <CPoint, CPoint&> pt; // 也可以這樣:CArray <CPoint, CPoint> pt;
現在,我們構造了兩個動態陣列,按照MSDN的提示,我們要使用SetSize函式建立它的大小和分配記憶體。(但其實這一步可以省略,而且我自己就是這麼做的,雖然這不符合一個規範程式設計師的風格。)SetSize的函式原型是:
void SetSize( int nNewSize, int nGrowBy = -1 );
這個函式在MSDN中也有詳細的說明,我就不再去翻譯了。其中第一個引數指定陣列大小,即陣列中元素個數(必需大於或等於0)。對於第二個引數,MSDN中有這樣一句話:如果使用了預設值,MFC以一種在大多數情況下能夠避免記憶體碎片和最高效的方式去分配記憶體。既然人家MSDN都這麼說了,那我們第二個引數就使用它的預設值了。如果要加上這一步的話,可以這麼寫(我們先不寫):
num.SetSize(40); // 其實大小設為多少沒有關係,只要是你認為最合適的就行了
pt.SetSize(10); // 一般地,設得大些可以避免記憶體碎片和提高效率,但所需空間越大
現在我們可以使用Add函式向陣列中新增一個元素,也可以用GetAt函式來獲得一個元素。它們的函式的原型分別是:
int Add( ARG_TYPE newElement );
TYPE GetAt( int nIndex ) const;
Add函式向陣列末尾增加一個元素,必要的話會擴充套件陣列,它返回增加元素的索引值。GetAt函式返回給定索引的元素值。例如,我們可以這樣寫:
for (int i=0; i<20; i++) num.Add(i); // 陣列大小為20,陣列中元素依次為0, 1, 2, … , 19
int n = num.GetAt(10); // 由於索引是從0開始,得到第11個元素,也就是10
CPoint point(100,100);
pt.Add(point); // 或者不用定義point,直接pt.Add(CPoint(100,100));
需要特別說明的是,如果使用了SetSize函式,上面介紹的Add函式會在陣列原來的大小上擴充套件陣列,陣列前面的元素全為0。即這時候陣列的大小變成了50,前30個元素為0,後面的元素依次為0, 1, 2, … , 19。為了避免這種情況,這時候就需要使用SetAt函式來向陣列中新增元素,SetAt函式設定給定索引的元素值,不允許擴充套件陣列。其函式原型為:
void SetAt( int nIndex, ARG_TYPE newElement );
如果使用SetAt函式,那麼前面的程式碼就要這麼寫:
for (int i=0; i<20; i++) num.SetAt(i, i);
如果要向陣列中間插入或者刪除一個或多個元素,可以使用InsertAt和RemoveAt函式。它們的函式原型分別是:
void InsertAt( int nIndex, ARG_TYPE newElement, int nCount = 1 );
void InsertAt( int nStartIndex, CArray* pNewArray );
void RemoveAt( int nIndex, int nCount = 1 );
接著上面的例子,我們可以這樣寫:
num.InsertAt(3, 100); // 在索引為3的位置插入元素100
// 那麼陣列中的元素是這樣的:0, 1, 2, 100, 3, 4, 5, … , 19
// num.InsertAt(3, 100, 2); // 在索引為3的位置向後插入兩個元素,均為100
// 則陣列中元素是這樣的:0, 1, 2, 100, 100, 3, 4, 5, … , 19
CArray <int, int> numNew;
for (i=0; i<10; i++) numNew.Add(i); // 新建陣列的元素為:0, 1, 2, … , 9
num.InsertAt(5, &numNew); // InsertAt的第二個版本,插入一個數組
// 現在陣列中的元素是這樣的:0, 1, 2, 100, 3, 0, 1, 2, … , 9, 4, 5, 6, … , 19
num.RemoveAt(2); // 移除索引為2的元素
// 陣列中的元素變成這樣:0, 1, 100, 3, 0, 1, 2, … , 9, 4, 5, 6, … , 19
// num.RemoveAt(2, 3); // 從索引為2的位置開始,移除3個元素
// 陣列中的元素變成這樣:0, 1, 0, 1, 2, … , 9, 4, 5, 6, … , 19
我們可以使用GetSize函式得到陣列當前的大小(元素個數),可以使用GetUpperBound函式得到陣列的上界(最大索引值),可以使用RemoveAll函式清空陣列。跟C中一樣,我們還可以使用操作符[ ]來訪問陣列元素,例如接著上面的例子:
n = num.GetSize(); // n = 30
n = num.GetUpperBound(); // n = 29
n = num[2]; // n = 100
num.RemoveAll();
n = num.GetSize(); // n = 0
寫到這裡,對CArray類的介紹已經差不多了,使用上面的函式基本上可以滿足動態陣列的操作。該類中還有一些其他函式,需要進一步瞭解的可以查閱MSDN,在這裡只是對CArray的常用函式作一個大概的介紹。