Template Collection Classes Revisited—The CArray Class

In EX16B in Chapter 16, you saw the MFC library CTypedPtrList template collection class, which was used to store a list of pointers to CStudent objects. Another collection class, CArray, is appropriate for the next example, EX19B. This class is different from CTypedPtrList in two ways. First, it's an array, with elements accessible by index, just like CStringArray in EX19A. Second, the array holds actual objects, not pointers to objects. In EX19B, the elements are CRect objects. The elements' class does not have to be derived from CObject, and indeed, CRect is not.

As in EX16B, a typedef makes the template collection easier to use. We use the statement

typedef CArray<CRect, CRect&> CRectArray;

to define an array class that holds CRect objects and whose functions take CRect reference parameters. (It's cheaper to pass a 32-bit pointer than to copy a 128bit object.) To use the template array, you declare an instance of CRectArray and then you call CArray member functions such as SetSize. You can also use the CArray subscript operator to get and set elements.

The template classes CArray, CList, and CMap are easy to use if the element class is sufficiently simple. The CRect class fits that description because it contains no pointer data members. Each template class uses a global function, SerializeElements, to serialize all the elements in the collection. The default SerializeElements function does a bitwise copy of each element to and from the archive.

If your element class contains pointers or is otherwise complex, you'll need to write your own SerializeElements function. If you wrote this function for the rectangle array (not required), your code would look like this:

void AFXAPI SerializeElements(CArchive& ar, CRect* pNewRects,
    int nCount)
{
    for (int i = 0; i < nCount; i++, pNewRects++) {
        if (ar.IsStoring()) {
            ar << *pNewRects;
        }
        else {
            ar >> *pNewRects;
        }
    }
}

When the compiler sees this function, it uses the function to replace the SerializeElements function inside the template. This only works, however, if the compiler sees the SerializeElements prototype before it sees the template class declaration.

The template classes depend on two other global functions, ConstructElements and DestructElements. Starting with Visual C++ version 4.0, these functions call the element class constructor and destructor for each object. Therefore, there's no real need to replace them.