Accessing All Members of a Collection
The MFC array collection classes — both template-based and not — use indexes to access their elements. The MFC list and map collection classes — both template-based and not — use an indicator of type POSITION to describe a given position within the collection. To access one or more members of these collections, you first initialize the position indicator and then repeatedly pass that position to the collection and ask it to return the next element. The collection is not responsible for maintaining state information about the progress of the iteration. That information is kept in the position indicator. But, given a particular position, the collection is responsible for returning the next element.
The following procedures show how to iterate over the three main types of collections provided with MFC:
Iterating an array
Iterating a list
Iterating a map
To iterate an array
Use sequential index numbers with the GetAt member function:
CTypedPtrArray<CObArray, CPerson*> myArray; myArray.Add(new CPerson()); for (int i = 0; i < myArray.GetSize();i+) { CPerson* thePerson = myArray.GetAt(i); thePerson->AssertValid(); }
This example uses a typed pointer array that contains pointers to CPerson objects. The array is derived from class CObArray, one of the nontemplate predefined classes. GetAt returns a pointer to a CPerson object. For typed pointer collection classes — arrays or lists — the first parameter specifies the base class; the second parameter specifies the type to store.
The CTypedPtrArray class also overloads the [ ] operator so that you can use the customary array-subscript syntax to access elements of an array. An alternative to the statement in the body of the for loop above is
CPerson* thePerson = myArray[i];
This operator exists in both const and non-const versions. The const version, which is invoked for const arrays, can appear only on the right side of an assignment statement.
To iterate a list
Use the member functions GetHeadPosition and GetNext to work your way through the list:
CTypedPtrList<CObList, CPerson*> myList; myList.AddHead(new CPerson()); POSITION pos = myList.GetHeadPosition(); while(pos != NULL) { CPerson* thePerson = myList.GetNext(pos); thePerson->AssertValid(); }
This example uses a typed pointer list to contain pointers to CPerson objects. The list declaration resembles the one for the array in the procedure To iterate an array but is derived from class CObList. GetNext returns a pointer to a CPerson object.
To iterate a map
Use GetStartPosition to get to the beginning of the map and GetNextAssoc to repeatedly get the next key and value from the map, as shown by the following example:
CMap<CString, LPCTSTR, CPerson*, CPerson*> myMap; CPerson myPerson; myMap.SetAt(_T("Bill"), &myPerson); POSITION pos = myMap.GetStartPosition(); while(pos != NULL) { CPerson* pPerson; CString string; // Get key (string) and value (pPerson) myMap.GetNextAssoc(pos, string, pPerson); // Use string and pPerson }
This example uses a simple map template (rather than a typed pointer collection) that uses CString keys and stores pointers to CPerson objects. When you use access functions such as GetNextAssoc, the class provides pointers to CPerson objects. If you use one of the nontemplate map collections instead, you must cast the returned CObject pointer to a pointer to a CPerson.
Note
For nontemplate maps, the compiler requires a reference to a CObject pointer in the last parameter to GetNextAssoc. On input, you must cast your pointers to that type, as shown in the next example.
The template solution is simpler and helps provide better type safety. The nontemplate code is more complicated, as you can see here:
CMapStringToOb myMap; // A nontemplate collection class CPerson myPerson; myMap.SetAt(_T("Bill"), &myPerson); POSITION pos = myMap.GetStartPosition(); while(pos != NULL) { CPerson* pPerson; CString string; // Gets key (string) and value (pPerson) myMap.GetNextAssoc(pos, string, (CObject*&)pPerson); ASSERT(pPerson->IsKindOf( RUNTIME_CLASS(CPerson))); // Use string and pPerson }
For more information, see Deleting All Objects in a CObject Collection.