CArray

CArray類支持與C arrays相似的數組,但是必要時可以動態壓縮並擴展。數組索引從0開始。可以決定是固定數組上界還是允許當添加元素時擴展當前的邊界。記憶體對上界是連續地分配空間,甚至一些元素可為空。 和C arrays一樣,CArray索引元素的訪問時間是不變的,與數組大小無關。

CArray

需要包含的頭檔案

提示:

在使用一個數組之前,使用SetSize建立它的大小和為它分配記憶體。如果不使用SetSize,則為數組添加元素就會引起頻繁地重新分配和拷貝。頻繁地重新分配和拷貝不但沒有效率,而且導致記憶體碎片。

如果需要一堆數組中的個別數據,必須設定CDumpContext對象的深度為1或更大。

此類的某成員函式調用全局幫助函式,它必須為CArray的大多數使用而定製。請參閱宏和全局量部分中的“類收集幫助器”。

當從一個CArray對象中移去元素時,幫助函式DestructElements被調用。

當添加元素時,幫助函式ConstructElements被調用。

數組類的派生與列表的派生相似。

MFC提供了一套模板庫,來實現一些比較常見的數據結構如Array,List,Map。CArray即為其中的一個,用來實現動態數組的功能。CArray是從CObject派生,有兩個模板參數,第一個參數就是CArray類數組元素的變數類型,後一個是函式調用時的參數類型。有一個類 class Object,要定義一個Object的動態數組,那么可以用以下兩種方法:

CArray Var1;

CArray Var2;

Var2的效率要高。

先了解一下CArray中的成員變數及作用。TYPE* m_pData; // 數據保存地址的指針

int m_nSize; // 用戶當前定義的數組的大小

int m_nMaxSize; // 當前實際分配的數組的大小

int m_nGrowBy; // 分配記憶體時增長的元素個數

構造函式,對成員變數進行了初始化。

CArray ::CArray()

{

m_pData = NULL;

m_nSize = m_nMaxSize = m_nGrowBy = 0;

}

SetSize成員函式是用來為數組分配空間的。SetSize的函式定義如下:

void SetSize( int nNewSize, int nGrowBy = -1 );

nNewSize 指定數組的大小

nGrowBy 如果需要增加數組大小時增加的元素的個數。

對SetSize的代碼,進行分析。

void CArray ::SetSize(int nNewSize, int nGrowBy)

{

if (nNewSize == 0)

{

// 第一種情況

// 當nNewSize為0時,需要將數組置為空,

// 如果數組本身即為空,則不需做任何處理

// 如果數組本身已含有數據,則需要清除數組元素

if (m_pData != NULL)

{

//DestructElements 函式實現了對數組元素析構函式的調用

//不能使用delete m_pData 因為我們必須要調用數組元素的析構函式

DestructElements (m_pData, m_nSize);

//現在才能釋放記憶體

delete[] (BYTE*)m_pData;

m_pData = NULL;

}

m_nSize = m_nMaxSize = 0;

}

else if (m_pData == NULL)

{

// 第二種情況

// 當m_pData==NULL時還沒有為數組分配記憶體

//首先我們要為數組分配記憶體,sizeof(TYPE)可以得到數組元素所需的位元組數

//使用new 數組分配了記憶體。注意,沒有調用構造函式

m_pData = (TYPE*) new BYTE[nNewSize * sizeof(TYPE)];

//下面的函式調用數組元素的構造函式

ConstructElements (m_pData, nNewSize);

//記錄下當前數組元素的個數

m_nSize = m_nMaxSize = nNewSize;

}

else if (nNewSize m_nSize)

{

// 需要增加元素的情況

// 與第二種情況的處理過程,既然元素空間已經分配,

// 只要調用新增元素的構造函式就Ok

ConstructElements (&m_pData[m_nSize], nNewSize-m_nSize);

}

else if (m_nSize > nNewSize)

{

// 現在是元素減少的情況,我們是否要重新分配記憶體呢?

// No,這種做法不好,後面來討論。

// 下面代碼釋放多餘的元素,不是釋放記憶體,只是調用析構函式

DestructElements (&m_pData[nNewSize], m_nSize-nNewSize);

}

m_nSize = nNewSize;

}

else

{

//這是最糟糕的情況,因為需要的元素大於m_nMaxSize,

// 意味著需要重新分配記憶體才能解決問題

// 計算需要分配的數組元素的個數

int nNewMax;

if (nNewSize < m_nMaxSize + nGrowBy)

nNewMax = m_nMaxSize + nGrowBy;

else

nNewMax = nNewSize;

// 重新分配一塊記憶體

TYPE* pNewData = (TYPE*) new BYTE[nNewMax * sizeof(TYPE)];

//實現將已有的數據複製到新的的記憶體空間

memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));

// 對新增的元素調用構造函式

ConstructElements (&pNewData[m_nSize], nNewSize-m_nSize);

//釋放記憶體

delete[] (BYTE*)m_pData;

//將數據保存

m_pData = pNewData;

m_nSize = nNewSize;

m_nMaxSize = nNewMax;

}

}

下面是ConstructElements函式的實現代碼template

AFX_INLINE void AFXAPI ConstructElements(TYPE* pElements, int nCount)

{

// first do bit-wise zero initialization

memset((void*)pElements, 0, nCount * sizeof(TYPE));

for (; nCount--; pElements++)

::new((void*)pElements) TYPE;

}

ConstructElements是一個模板函式。對構造函式的調用是通過標為黑體的代碼實現的。可能很多人不熟悉new 的這種用法,它可以實現指定的記憶體空間中構造類的實例,不會再分配新的記憶體空間。類的實例產生在已經分配的記憶體中,並且new操作會調用對象的構造函式。因為vc中沒有辦法直接調用構造函式,而通過這種方法,巧妙的實現對構造函式的調用。

再來看DestructElements 函式的代碼template

AFX_INLINE void AFXAPI DestructElements(TYPE* pElements, int nCount)

{

for (; nCount--; pElements++)

pElements->~TYPE();

}

DestructElements函式同樣是一個模板函式,實現很簡單,直接調用類的析構函式即可。

如果定義一個CArray對象 CArray myObject ,對myObject就可象數組一樣,通過下標來訪問指定的數組元素。

CArray[]有兩種實現,區別在於返回值不同。

template

AFX_INLINE TYPE CArray ::operator[](int nIndex) const

{ return GetAt(nIndex); }

template

AFX_INLINE TYPE& CArray ::operator[](int nIndex)

{ return ElementAt(nIndex); }

前一種情況是返回的對象的實例,後一種情況是返回對象的引用。分別調用不同的成員函式來實現。

TYPE GetAt(int nIndex) const

{ ASSERT(nIndex >= 0 && nIndex < m_nSize);

return m_pData[nIndex]; }

TYPE& ElementAt(int nIndex)

{ ASSERT(nIndex >= 0 && nIndex < m_nSize);

return m_pData[nIndex]; }

除了返回值不同,其它都一樣.

CArray arrInt;

arrInt.SetSize(10);

int n = arrInt.GetAt(0);

int& l = arrInt.ElementAt(0);

cout

相關詞條

相關搜尋

熱門詞條

聯絡我們