Overview of MFC Collection Classes
MFC ships two generations of collection classes. The older, non-template classes such as CObArray, CObList, and CMapStringToOb store CObject pointers or specific fixed types and predate C++ templates being widely usable in Microsoft's toolchain. The newer template-based classes, CArray<TYPE,ARG_TYPE>, CList<TYPE,ARG_TYPE>, and CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>, let you store any type safely with compile-time type checking, and are the ones you should reach for in new code.
Cricket analogy: Choosing between legacy and template collections is like a coach deciding between an all-rounder squad picked by fixed roles (batsman, bowler, keeper) versus a flexible XI like Ravindra Jadeja who can be slotted anywhere the match situation demands.
Array-Based Collections: CArray and Friends
CArray<TYPE,ARG_TYPE> is a dynamically growable array, similar in spirit to std::vector but predating it in MFC's design. You call SetSize(nCount, nGrowBy) to preallocate storage and control how many extra elements are reserved each time the array must reallocate, GetSize() to query the current element count, and ElementAt(nIndex) or operator[] for direct access. Because CArray stores elements contiguously, random access is O(1), but inserting or removing an element in the middle requires shifting every subsequent element.
Cricket analogy: SetSize with nGrowBy is like a scorer who reserves extra rows on the scoresheet in advance, anticipating that a team like England might need many more overs recorded if the innings runs long, rather than adding one row at a time.
List-Based Collections: CList
CList<TYPE,ARG_TYPE> implements a doubly linked list, which trades random-access speed for cheap insertion and removal anywhere in the sequence. Navigation uses an opaque POSITION handle rather than an integer index: GetHeadPosition() and GetTailPosition() return starting points, and GetNext(pos) or GetPrev(pos) advance through the list while returning the element at the old position. AddHead() and AddTail() insert in O(1) time, which makes CList well suited to queues, undo stacks, and scenarios with frequent mid-sequence insertion.
Cricket analogy: Walking a CList with POSITION and GetNext is like a commentator following the over-by-over sequence of a Test match ball by ball rather than jumping straight to over forty, since each delivery only knows the one before and after it.
Map-Based Collections: CMap
CMap<KEY,ARG_KEY,VALUE,ARG_VALUE> is a hash table associating keys with values. SetAt(key, value) inserts or updates an entry, Lookup(key, value) returns TRUE and fills value by reference if the key exists, and RemoveKey(key) deletes an entry. Because it is hash-based, average-case lookup is O(1), but you can tune GetHashTableSize() and InitHashTable() to control collision behavior for large datasets, and iteration order is unspecified since it depends on hash bucket placement rather than insertion order.
Cricket analogy: Lookup by key is like a scorer instantly finding a player's career average by name in a database rather than scanning every match card in order, similar to instantly pulling up Sachin Tendulkar's stats.
The legacy classes CObArray, CObList, and CMapStringToOb are still present in MFC for backward compatibility with older codebases, but they store CObject* and require RTTI-style dynamic casting to recover the concrete type. New code should prefer the template classes CArray, CList, and CMap, or better yet std::vector, std::list, and std::map from the standard library where MFC interop is not required.
ARG_TYPE in CArray, CList, and CMap is often a reference type used for parameter passing efficiency, but passing a temporary object whose lifetime ends before the collection uses it can leave a dangling reference. Always verify that the value passed to Add(), SetAt(), or similar methods outlives the call, especially when ARG_TYPE is something like const CString&.
- MFC has legacy CObArray/CObList/CMap-style classes and modern template classes CArray, CList, and CMap.
- CArray offers O(1) random access via ElementAt but O(n) insertion in the middle.
- CList offers O(1) insertion at head or tail via a doubly linked list navigated with POSITION handles.
- CMap provides average O(1) key-based lookup using a hash table via SetAt, Lookup, and RemoveKey.
- SetSize with nGrowBy preallocates CArray storage to reduce reallocation overhead.
- Legacy CObject-based collections remain for backward compatibility but lack compile-time type safety.
- Watch for dangling references when ARG_TYPE parameters are passed temporary values.
Practice what you learned
1. Which MFC collection class provides O(1) insertion at both the beginning and end of the sequence?
2. What does CArray::SetSize(nCount, nGrowBy) control?
3. How do you traverse the elements of a CList in order from the front?
4. What does CMap::Lookup(key, value) return when the key is not present?
5. Why should new MFC code generally prefer CArray over the legacy CObArray?
Was this page helpful?
You May Also Like
The CString Class
How MFC's CString class manages dynamic text, formatting, searching, and interop with Win32 string APIs.
File I/O with MFC
Reading and writing files in MFC using CFile, CStdioFile, and CArchive, plus handling file exceptions correctly.
Serialization in MFC
How MFC's CObject::Serialize framework, CArchive, and the DECLARE_SERIAL/IMPLEMENT_SERIAL macros work together to persist objects to and from disk.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics