當前位置:編程學習大全網 - 編程語言 - carray用法

carray用法

CArray的 用法

需要包含的頭文件 <afxtempl.h>

CArray類支持與C arrays相似的數組,但是必要時可以動態壓縮並擴展。數組索引從0開始。可以決定是固定數組上界還是允許當添加元素時擴展當前的邊界。內存對上界是連續地分配空間,甚至壹些元素可為空。

和C arrays壹樣,CArray索引元素的訪問時間是不變的,與數組大小無關。

提示:

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

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

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

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

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

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

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

CArray<Object,Object> Var1;

CArray<Object,Object&> Var2;

Var2的效率要高。

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

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

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

int m_nGrowBy; // 分配內存時增長的元素個數

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

CArray<TYPE, ARG_TYPE>::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<TYPE, ARG_TYPE>::SetSize(int nNewSize, int nGrowBy)

{

if (nNewSize == 0)

{

// 第壹種情況

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

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

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

if (m_pData != NULL)

{

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

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

DestructElements<TYPE>(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<TYPE>(m_pData, nNewSize);

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

m_nSize = m_nMaxSize = nNewSize;

}

else if (nNewSize <= m_nMaxSize)

{

// 第三種情況

// 這種情況需要分配的元素個數比已經實際已經分配的元素個數要少

if (nNewSize > m_nSize)

{

// 需要增加元素的情況

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

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

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

}

else if (m_nSize > nNewSize)

{

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

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

// 下面代碼釋放多余的元素,不是釋放內存,只是調用析構函數

DestructElements<TYPE>(&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<TYPE>(&pNewData[m_nSize], nNewSize-m_nSize);

//釋放內存

delete[] (BYTE*)m_pData;

//將數據保存

m_pData = pNewData;

m_nSize = nNewSize;

m_nMaxSize = nNewMax;

}

}

下面是ConstructElements函數的實現代碼template<class TYPE>

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<class TYPE>

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

{

for (; nCount--; pElements++)

pElements->~TYPE();

}

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

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

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

template<class TYPE, class ARG_TYPE>

AFX_INLINE TYPE CArray<TYPE, ARG_TYPE>::operator[](int nIndex) const

{ return GetAt(nIndex); }

template<class TYPE, class ARG_TYPE>

AFX_INLINE TYPE& CArray<TYPE, ARG_TYPE>::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<int,int&> arrInt;

arrInt.SetSize(10);

int n = arrInt.GetAt(0);

int& l = arrInt.ElementAt(0);

cout << arrInt[0] <<endl;

n = 10;

cout << arrInt[0] <<endl;

l = 20;

count << arrInt[0] << endl;

結果會發現,n的變化不會影響到數組,而l的變化會改變數組元素的值。實際即是對C++中引用運算符的運用。

CArray下標訪問是非安全的,它並沒有超標預警功能。雖然使用ASSERT提示,但下標超範圍時沒有進行處理,會引起非法內存訪問的錯誤。

Add函數的作用是向數組添加壹個元素。下面是它的定義: int CArray<TYPE, ARG_TYPE>::Add(ARG_TYPE newElement).Add函數使用的參數是模板參數的二個參數,也就是說,這個參數的類型是我們來決定的,可以使用Object或Object&的方式。熟悉C++的朋友都知道,傳引用的效率要高壹些。如果是傳值的話,會在堆棧中再產生壹個新的對象,需要花費更多的時間。

template<class TYPE, class ARG_TYPE>

AFX_INLINE int CArray<TYPE, ARG_TYPE>::Add(ARG_TYPE newElement)

{

int nIndex = m_nSize;

SetAtGrow(nIndex, newElement);

return nIndex;

}

它實際是通過SetAtGrow函數來完成這個功能的,它的作用是設置指定元素的值。

template<class TYPE, class ARG_TYPE>

void CArray<TYPE, ARG_TYPE>::SetAtGrow(int nIndex, ARG_TYPE newElement)

{

if (nIndex >= m_nSize)

SetSize(nIndex+1, -1);

m_pData[nIndex] = newElement;

}

SetAtGrow的實現也很簡單,如果指定的元素已經存在,就把改變指定元素的值。如果指定的元素不存在,也就是 nIndex>=m_nSize的情況,就調用SetSize來調整數組的大小

首先定義

CArray<char *> arryPChar;

這裏以定義char*的為例子。

接下來我們來熟悉CArray這個類裏的函數。

INT_PTR GetCount() const;

獲得當前這個數組有多少個元素。

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

設置數組的大小。

TYPE& GetAt(INT_PTR nIndex);

void SetAt(INT_PTR nIndex, ARG_TYPE newElement);

獲得/設置序列的元素

INT_PTR Add(ARG_TYPE newElement);

在數組的末尾添加壹個元素,數組的長度加1。如果之前使用SetSize是nGrowBy大於1,則內存按照nGrowBy增加。函數返回newElement的數組元素索引

void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1);

從指定的nIndex位置開始,刪除nCount個數組元素,所有元素自動下移,並且減少數組的上限,但是不釋放內存。這裏我們自己手動的申請的就必須自己釋放。new對應delete相信大家都知道的。

void RemoveAll();

從數組中移除素有的元素,如果數組為空,該行數也起作用。

INT_PTR Append(const CArray& src);

將同個類型的壹個數組A附加到本數組的尾部,返回A第壹數組元素在本數組的索引。

void InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount = 1);

void InsertAt(INT_PTR nStartIndex, CArray* pNewArray);

在指定的nIndex或者nStartIndex位置插入nCount個newElement數組元素或者pNewArray數組

下面是我應用的實例:

view plaincopy to clipboardprint?

CArray <char*>arrPChar;

//初始化元素

arrPChar.SetSize(10);

for (int i=0;i<10;i++)

{

char *aChar=new char[10];

strcpy_s(aChar,10,"hello arr");

arrPChar.SetAt(i,aChar);

}

//在數組的末尾插入壹個元素

char *bChar = new char[10];

strcpy_s(bChar,10,"asdfefdsd");

arrPChar.Add(bChar);

//在索引2的位置插入壹個元素,即在第三位插入壹個元素

char *cChar=new char[5];

strcpy_s(cChar,5,"aidy");

arrPChar.InsertAt(2,cChar);

for (int j=0;j<arrPChar.GetCount();j++)

{

TRACE("%d,%s\n",j,arrPChar.GetAt(j));

}

//刪除數組裏的所有元素,要釋放內存,如果單單Remove的話則內存不會被釋放

//這裏因為使用RemoveAll的話內存無法被釋放,所以沒有給實例。

int count = arrPChar.GetCount();

for (int k=0; k<count; k++)

{

char *dChar=arrPChar.GetAt(0);

arrPChar.RemoveAt(0);

delete dChar;

}

  • 上一篇:寧波哪裏有好的幼兒園?
  • 下一篇:居家親子體適能:動得開心,學的專心
  • copyright 2024編程學習大全網