在 VC++ 複雜專案開發中,動態管理陣列結構是常見需求。C++ 標準庫早期未提供動態陣列,MFC 推出的樣板類別 CArray 填補了此缺口。本文透過原始碼層級分析,揭示 CArray 內部運作原理,幫助開發者寫出高效能、記憶體安全的程式碼。
CArray 繼承自 CObject,接受兩個樣板參數:
TYPE:陣列元素的資料型別ARG_TYPE:函式傳參時的型別(通常為 TYPE& 以提升效能)宣告範例:
CArray<Object, Object> var1; // 傳值方式
CArray<Object, Object&> var2; // 傳參考方式(效率較高)
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(int nNewSize, int nGrowBy = -1) 是記憶體管理的核心,分四大情況處理:
DestructElements 逐一呼叫解構函式,再 delete[] 釋放記憶體。new BYTE[nNewSize * sizeof(TYPE)] 配置原始記憶體(不呼叫建構函式)。ConstructElements 逐一執行 placement new 建構物件。ConstructElements。DestructElements(不釋放記憶體,避免頻繁重新配置)。nNewMax(考慮 nGrowBy)。memcpy 複製舊資料(位元級複製,不呼叫建構/解構)。ConstructElements。若僅配置記憶體不建構:
int、long)無影響。ID 為隨機值),複雜類別更會導致邏輯錯誤。若刪除不解構:
Person::name 指標)將造成記憶體洩漏。// ConstructElements:placement new 呼叫建構函式
template<class TYPE>
AFX_INLINE void AFXAPI ConstructElements(TYPE* pElements, int nCount) {
memset(pElements, 0, nCount * sizeof(TYPE)); // 位元清零
for (; nCount--; pElements++)
::new ((void*)pElements) TYPE; // 在既有記憶體上建構
}
// DestructElements:直接呼叫解構函式
template<class TYPE>
AFX_INLINE void AFXAPI DestructElements(TYPE* pElements, int nCount) {
for (; nCount--; pElements++)
pElements->~TYPE();
}
// const 版本:回傳複本(不可修改原陣列)
AFX_INLINE TYPE CArray<TYPE, ARG_TYPE>::operator[](int nIndex) const {
return GetAt(nIndex); // return m_pData[nIndex];
}
// 非 const 版本:回傳參考(可修改原陣列)
AFX_INLINE TYPE& CArray<TYPE, ARG_TYPE>::operator[](int nIndex) {
return ElementAt(nIndex); // return m_pData[nIndex];
}
差異示範:
CArray<int, int&> arr; arr.SetSize(10);
int n = arr.GetAt(0); // 複本
int& l = arr.ElementAt(0); // 參考
n = 10; // arr[0] 不變
l = 20; // arr[0] 變為 20
Add(ARG_TYPE newElement) 內部呼叫 SetAtGrow,若 ARG_TYPE 為 Object(傳值),會在堆疊額外建構暫時物件;改為 Object& 則避免額外複製,顯著提升大型物件插入效能。
CArray 非邊界安全,ASSERT 僅在 Debug 生效,Release 下越界導致未定義行為。SetSize 預先配置容量,減少重新配置開銷。CArray<T*, T*>。InsertAt、RemoveAt、FreeExtra 等)。深入理解 CArray 實作細節,能讓你在效能敏感場合做出最佳容器選擇與調優決策。