Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 18

Chapter 7

Serialization and CollectiOn Classes


Objectives
Explains the serialization mechanism provided in the Microsoft Foundation Class Library (MFC). Explain concept and need of Serialization in Document / View architecture. List the MFC Classes used in serialization and how they work internally to perform Serialization. CFile, CObject and CArchive classes. Make a class serializable. List collection classes. Iterate / Delete object from collection classes.

Introduction
The basic idea of serialization is that an object should be able to write its current state, usually indicated by the value of its member variables, to persistent storage. Later, the object can be recreated by reading, or deserializing, the objects state from the storage. Serialization handles all the details of object pointers and circular references to objects that are used when you serialize an object. A key point is that the object itself is responsible for reading and writing its own state. Serialization is an important concept in MFC programming because it is the basis for the frameworks ability to open and save document in document / view architecture. This chapter covers all the details of serialization and how to make a class serializable.

Need for Serialization


1) For implementing the file reading and writing, without worrying about the format of the file you are writing to. 2) Concatenating the objects onto the file and then reading them in the order they were written.

Classes involved in Serialization


CObject CFile CArchive

CObject
The basic serialization protocol and functionality are defined in the CObject class. By deriving your class from CObject (or from a class derived from CObject), you gain access to the serialization protocol and functionality of CObject. The Serialize member function, which is defined in the CObject class, is responsible for actually serializing the data necessary to capture an objects current state. The Serialize

Page 96

Serialization and Collection Classes

function has a CArchive argument (which will be covered soon ) that it uses to read and write the object data. The CArchive object has a member function, IsStoring, which indicates whether Serialize is storing (writing data) or loading (reading data). Using the results of IsStoring as a guide, you either insert your objects data in the CArchive object with the insertion operator (<<) or extract data with the extraction operator (>>). One of the most important feature of CObject is run-time class information that lets the developer determine the information about an object such as class name and parent at runtime. The code to support RTCI resides in the macros like

DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC DECLARE_DYNCREATE / IMPLEMENT_ DYNCREATE DECLARE_SERIAL / IMPLEMENT_SERIAL

The class derived from CObject can call IsKindOf( ) function which takes an argument that is created by another macro , RUNTIME_CLASS. The CRuntimeClass contains a data member m_wSchema. Serialization uses the WORD to give a class version. If , serialization is not supported then m_wSchema is oxffff. You can specify your own Schema through the IMPLEMENT_SERIAL macro as the third argument. The CObjects IsSerializable() function makes sure if the user has specified the correct macros for Serialization by checking that the m_wSchema for the CRuntimeClass is not 0xffff. CObject :: IsSerializable( ) BOOL CObject :: IsSerializable( ) const { return ( GetRuntimeClass( ) -> m_wSchema ! = 0xffff) ; }

Does the MFC architecture with CObject as a root cause performance problems ? C++ has to index the class virtual table at runtime to determine the correct function to execute. CObject has five virtual functions , two of which are in the debug builds only , so they dont really affect the release- build performance. The largest overhead with using virtual function is the increased instance size from a virtual table. Thus, the trade-off in deriving from CObject is 3 extra virtual functions added to your classes virtual table in exchange for RTCI, dynamic creation, serialization and memory diagnostics.

CFile

CFile is the base class for Microsoft Foundation file classes. It directly provides unbuffered, binary disk input/output services, and it indirectly supports text files and memory files through its derived classes. CFile works in conjunction with the CArchive class to support serialization CFile family consists of the following : CStdioFile , CMemFile ,CFile derivative class called CSharedFile and CFile itself. CStdioFile : It is buffered , reading and writing non-binary ASCII files. CMemFile : It provides a base class for implementing shared memory through CFile

SEED Infotech Ltd.

Serialization and Collection Classes

Page 97

interface. CSharedFile : Provides some memory sharing. The hierarchical relationship between this class and its derived classes allows your program to operate on all file objects through the polymorphic CFile interface. A memory file, for example, behaves like a disk file. Normally, a disk file is opened automatically on CFile construction and closed on destruction. When you create a CFile Object , you specify a filename and file open mode. E.g. CFile myfile(filename, CFile :: modeCreate|CFile :: modeWrite);

CArchive
CArchive does not have a base class. MFC uses an object of the CArchive class as an intermediary between the object to be serialized and the storage medium. This object is always associated with a CFile object, from which it obtains the necessary information for serialization, including the file name and whether the requested operation is a read or write. The object that performs a serialization operation can use the CArchive object without regard to the nature of the storage medium. CArchive object uses overloaded insertion ( <<) and extraction (>>) operators to perform writing and reading operations. There are two ways to create a CArchive object: 1) Implicit creation of a CArchive object via the framework 2) Explicit creation of a CArchive object

1. Implicit creation of a CArchive object via the framework


The most common, and easiest, way is to let the framework create a CArchive object for your document on behalf of the Save, Save As, and Open commands on the File menu. Your documents Serialize function then writes data to the CArchive object. Upon return from your Serialize function, the framework destroys the CArchive object and then the CFile object. Thus, if you let the framework create the CArchive object for your document, all you have to do is implement the documents Serialize function that writes and reads to and from the archive. You also have to implement Serialize for any CObject-derived objects that the documents Serialize function in turn serializes directly or indirectly.

2. Explicit creation of a CArchive object


Besides serializing a document via the framework, there are other occasions when you may need a CArchive object. For example, you might want to serialize data to and from the Clipboard, represented by a CSharedFile object. Or, you may want to use a user interface for saving a file that is different from the one offered by the framework. In this case, you can explicitly create a CArchive object. You do this the same way the framework does, using the following procedure. 1) Construct a CFile object or an object derived from CFile. 2) Pass the CFile object to the constructor for CArchive E.g. CFile theFile; theFile.Open(..., CFile::modeWrite); CArchive archive(&theFile, CArchive::store);

SEED Infotech Ltd.

Page 98

Serialization and Collection Classes

SEED Infotech Ltd.

Serialization and Collection Classes

Page 99

Serialization Macros
The DECLARE_SERIAL macro is required in the declaration of classes that will support serialization, as shown here: class CPerson : public CObject { DECLARE_SERIAL( CPerson ) // rest of declaration follows... };

The IMPLEMENT_SERIAL macro is used to define the various functions needed when you derive a serializable class from CObject. You use this macro in the implementation file (.CPP) for your class. The first two arguments to the macro are the name of the class and the name of its immediate base class. The third argument to this macro is a schema number. The schema number is essentially a version number for objects of the class. Use an integer greater than or equal to 0 for the schema number. The MFC serialization code checks the schema number when reading objects into memory. If the schema number of the object on disk does not match the schema number of the class in memory, the library will throw a CArchiveException, preventing your program from reading an incorrect version of the object. If you want your Serialize member function to be able to read multiple versions that is, files written with different versions of the application you can use the value VERSIONABLE_SCHEMA as an argument to the IMPLEMENT_SERIAL macro. The following example shows how to use IMPLEMENT_SERIAL for a class, CPerson, that is derived from CObject: IMPLEMENT_SERIAL( CPerson, CObject, 1 )

How does IsStoring() and IsLoading() work


The following diagram shows the steps taken by MFC and CDocument / CObject derivatives during serialization store process.

SEED Infotech Ltd.

Page 100

Serialization and Collection Classes

CDocument :: OnSaveDocument() [ doccore.cpp] CArchive :: Carchive() CMyDocument :: Serialize() CArchive :: IsStoring() operator << ( CArchive& , CObject* ) CArchive :: WriteObject () CArchive :: WriteClass () CMyClass:: Serialize() CArchive :: IsStoring ( ) CArchive :: operator << ( WORD) CArchive :: operator << ( dWORD) operator << ( CArchive& , point ) CArchive :: Write ( ) CArchive :: Close( )
In first step CDocument :: OnSaveDocument( ) creates a CArchive object in store mode and attaches to a file already opened by the Common Dialog. Next OnSaveDocument( ) calls the documents Serialize( ) method and finally closes the archive. In steps 3 - 5 the CMyDocument :: OnSaveDocument( ) is called which checks if the archive is storing via IsStoring( ) and uses the insertion operator for writing , as shown in the fifth step. In steps 6- 8 the insertion operator calls the CArchive::WriteObject ( ) which calls the WriteClass(), then Serialize( ) for the CMyDocument object. From steps 9-13 the actual data is written into the output buffer. Finally the Archive is closed by CDocument :: OnSaveDocument( ) as shown above.

SEED Infotech Ltd.

Serialization and Collection Classes

Page 101

Steps taken while reading the same object that is stored


CDocument :: OnOpenDocument( ) [ doccore.cpp]

CArchive :: CArchive ( ) CMyDocument :: Serialize ( ) CArchive :: IsStoring ( ) FALSE operator >> ( CArchive& , CMyClass* ) CArchive :: ReadObject ( ) CArchive :: Serialize ( ) CMyClass:: IsStoring ( ) CArchive :: operator >> ( WORD) CArchive :: operator >> (DWORD) operator >> ( CArchive& , point ) CArchive :: Read( ) CArchive :: Close( )
The framework unwinds what is wrote in the save operation when reading. The framework can make sure that the data is read in the order that is written.

Making a class Serializable


There are five main steps to make a class Serializable :

Steps :
1) 2) 3) 4) 5) 6) Deriving your class from CObject (or from some class derived from CObject). Using the DECLARE_SERIAL macro in the class declaration. Overriding the Serialize member function. Defining a constructor that takes no arguments. Using the IMPLEMENT_SERIAL macro in the implementation file for your class. If you call Serialize directly rather than through the >> and << operators of CArchive, the last three steps are not required for serialization.

SEED Infotech Ltd.

Page 102

Serialization and Collection Classes

Overriding a Serialize function


Call your base class version of Serialize to make sure that the inherited portion of the object is serialized. Insert or extract the member variables specific to your class. The insertion and extraction operators interact with the archive class to read and write the data. The following eg shows how to implement Serialize for the CPerson class declared above

class CPerson : public CObject { DECLARE_SERIAL (CPerson); CString m_fname; WORD m_lname; public: virtual void Serialize ( CArchive& ar) }; void CPerson ::Serialize ( CArchive& ar) { CObject::Serialize ( ar ); //call base class //function first if ( ar.IsStoring ( ) ){ ar << m_fname; ar<< m_lname; } else ar >> m_fname; ar >> m_lname; }

Collection Classes
MFC contains a number of ready-to-use lists, arrays, and maps that are referred to as collection classes. A collection is a very useful programming idiom for holding and processing groups of class objects or groups of standard types. A collection object appears as a single object. Class member functions can operate on all elements of the collection. MFC supplies two kinds of collection classes: Collection classes based on templates Collection classes not based on templates

The collection template classes are based on C++ templates, but the original collection classes released with MFC version 1.0 not based on templates are still available. Most collections can be archived or sent to a dump context. The Dump and Serialize member functions for CObject pointer collections call the corresponding functions for each of their elements. Some collections cannot be archived for example, pointer collections. When you program with the application framework, the collection classes will be especially useful for implementing data structures in your document class. The Microsoft Foundation Class Library provides collection classes to manage groups of objects. These classes are of two types: Collection classes created from C++ templates Collection classes not created from templates

SEED Infotech Ltd.

Serialization and Collection Classes

Page 103

MFC predefines two categories of template-based collections: Simple array, list, and map classes Arrays, lists, and maps of typed pointers CArray, CList, CMap CTypedPtrArray,CTypedPtrList,

CTypedPtrMap

The simple collection classes are all derived from class CObject, so they inherit the serialization, dynamic creation, and other properties of CObject. The typed pointer collection classes require you to specify the class you derive from which must be one of the nontemplate pointer collections predefined by MFC, such as CPtrList or CPtrArray. Your new collection class inherits from the specified base class, and the new classs member functions use encapsulated calls to the base class members to enforce type safety.

Using Simple Array, List, and Map Templates


To use the simple collection templates, you need to know what kind of data you can store in these collections and what parameters to use in your collection declarations.

Simple Array and List Usage


The simple arrays and lists classes, CArray and CList, take two parameters: TYPE and ARG_TYPE. These classes can store any data type, which you specify in the TYPE parameter: 1) Fundamental C++ data types, such as int, char, and float 2) C++ structures and classes 3) Other types that you define For convenience and efficiency, you can use the ARG_TYPE parameter to specify the type of function arguments. Typically, you specify ARG_TYPE as a reference to the type you named in the TYPE parameter. For example: CArray<int, int> myArray; CList<CPerson, CPerson&> myList;

The first example declares an array collection, myArray, which contains ints. The second example declares a list collection, myList, which stores CPerson objects. Certain member functions of the collection classes take arguments whose type is specified by the ARG_TYPE template parameter. For example, the Add member function of class CArray takes an ARG_TYPE argument: CArray<CPerson, CPerson&> myArray; CPerson person; myArray->Add( person );

Simple Map Usage


The simple map class, CMap, takes four parameters: KEY, ARG_KEY, VALUE, and ARG_VALUE. Like the array and list classes, the map classes can store any data type. Unlike arrays and lists, which index and order the data they store, maps associate keys and values: you access a value stored in a map by specifying the values associated key. The KEY parameter specifies the data type of the keys used to access data stored in the map. If the type of KEY is a structure or class, the ARG_KEY parameter is typically a reference to the type specified in KEY. The VALUE parameter specifies the type of the items stored in the map.
SEED Infotech Ltd.

Page 104

Serialization and Collection Classes

If the type of ARG_VALUE is a structure or class, the ARG_VALUE parameter is typically a reference to the type specified in VALUE. For example: CMap< int, int, MY_STRUCT, MY_STRUCT& > myMap1; CMap< CString, LPCSTR, CPerson, CPerson& > myMap2;

The first example stores MY_STRUCT values, accesses them by int keys, and returns accessed MY_STRUCT items by reference. The second example stores CPerson values, accesses them by CString keys, and returns references to accessed items. This example might represent a simple address book, in which you look up persons by last name. Because the KEY parameter is of type CString and the KEY_TYPE parameter is of type LPCSTR, the keys are stored in the map as items of type CString but are referenced in functions such as SetAt through pointers of type LPCSTR. For example: CMap< CString, LPCSTR, CPerson, CPerson& > myMap2; CPerson person; LPCSTR lpstrName = "Jones"; myMap2->SetAt( lpstrName, person );

Using Typed-Pointer Collection Templates


To use the typed-pointer collection templates, you need to know what kinds of data you can store in these collections and what parameters to use in your collection declarations.

Typed-Pointer Array and List Usage


The typed-pointer array and list classes, CTypedPtrArray and CTypedPtrList, take two parameters: BASE_CLASS and TYPE. These classes can store any data type, which you specify in the TYPE parameter. They are derived from one of the nontemplate collection classes that store pointers; you specify this base class in BASE_CLASS. For arrays, use either CObArray or CPtrArray. For lists, use either CObList or CPtrList. In effect, when you declare a collection based on, say CObList, the new class not only inherits the members of its base class, but it also declares a number of additional type-safe member functions and operators that provide type safety by encapsulating calls to the base class members. These encapsulations manage all necessary type conversion. For example: CTypedPtrArray<CObArray, CPerson*> myArray; CTypedPtrList<CPtrList, MY_STRUCT*> myList;

The first example declares a typed-pointer array, myArray, derived from CObArray. The array stores and returns pointers to CPerson objects (where CPerson is a class derived from CObject). You can call any CObArray member function, or you can call the new type-safe GetAt and ElementAt functions or use the type-safe [ ] operator. The second example declares a typed-pointer list, myList, derived from CPtrList. The list stores and returns pointers to MY_STRUCT objects. A class based on CPtrList is used for storing pointers to objects not derived from CObject. CTypedPtrList has a number of typesafe member functions: GetHead, GetTail, RemoveHead, RemoveTail, GetNext, GetPrev, and GetAt.

Typed-Pointer Map Usage


SEED Infotech Ltd.

Serialization and Collection Classes

Page 105

The typed-pointer map class, CTypedPtrMap, takes three parameters: BASE_CLASS, KEY, and VALUE. The BASE_CLASS parameter specifies the class from which to derive the new class: CMapPtrToWord, CMapPtrToPtr, CMapStringToPtr, CMapWordToPtr, CMapStringToOb, and so on. KEY is analogous to KEY in CMap: it specifies the type of the key used for lookups. VALUE is analogous to VALUE in CMap: it specifies the type of object stored in the map. For example: CTypedPtrMap<CMapPtrToPtr, CString, MY_STRUCT*> myPtrMap; CTypedPtrMap<CMapStringToOb, CString, CMyObject*> myObjectMap;

The first example is a map based on CMapPtrToPtr it uses CString keys mapped to pointers to MY_STRUCT. You can look up a stored pointer by calling a type-safe Lookup member function. You can use the [ ] operator to look up a stored pointer and add it if not found. And you can iterate the map using the type-safe GetNextAssoc function. You can also call other member functions of class CMapPtrToPtr. The second example is a map based on CMapStringToOb it uses string keys mapped to stored pointers to CMyObject objects. You can use the same type-safe members described in the previous paragraph, or you can call members of class CMapStringToOb.

Note
If you specify a class or struct type for the VALUE parameter, rather than a pointer or reference to the type, the class or structure must have a copy constructor. The Microsoft Foundation Class Library provides predefined type-safe collections based on C++ templates. Because they are templates, these classes provide type safety and ease of use without the type-casting and other extra work involved in using a nontemplate class for this purpose.

Serializing Elements
The CArray, CList, and CMap classes call SerializeElements to store collection elements to or read them from an archive. The default implementation of the SerializeElements helper function does a bitwise write from the objects to the archive, or a bitwise read from the archive to the objects, depending on whether the objects are being stored in or retrieved from the archive. Override SerializeElements if this action is not appropriate. If your collection stores objects derived from CObject and you use the IMPLEMENT_SERIAL macro in the implementation of the collection element class, you can take advantage of the serialization functionality built into CArchive and CObject CPerson : public CObject { . . . }; CArray< CPerson, CPerson& > personArray; void SerializeElements( CArchive& ar, CPerson* pNewPersons, int nCount ) { for ( int i = 0; i < nCount; i++, pNewPersons++ ) { // Serialize each CPerson object pNewPersons->Serialize( ar ); } }

The overloaded insertion operators for CArchive call CObject::Serialize (or an override of

SEED Infotech Ltd.

Page 106

Serialization and Collection Classes

that function) for each CPerson object.

Using Nontemplate Collection Classes


MFC also supports the collection classes introduced with MFC version 1.0. These classes are not based on templates. They can be used to contain data of the supported types CObject*, UINT, DWORD, and CString. You can use these predefined collections (such as CObList) to hold collections of any objects derived from CObject. MFC also provides other predefined collections to hold primitive types such as UINT and void pointers (void*). In general, however, it is often useful to define your own type-safe collections to hold objects of a more specific class and its derivatives. Note that doing so with the collection classes not based on templates is more work than using the template-based classes. There are two ways to create type-safe collections with the nontemplate collections: 1) Use the nontemplate collections, with type casting if necessary. This is the easiest approach. 2) Derive from and extend a nontemplate type-safe collection.

To use the nontemplate collections with type casting.


Use one of the nontemplate classes, such as CWordArray, directly. For example, you can create a CWordArray and add any 32-bit values to it, then retrieve them. There is nothing more to do. You just use the predefined functionality. You can also use a predefined collection, such as CObList, to hold any objects derived from CObject. A CObList collection is defined to hold pointers to CObject. When you retrieve an object from the list, you may have to cast the result to the proper type since the CObList functions return pointers to CObject. For example, if you store CPerson objects in a CObList collection, you have to cast a retrieved element to be a pointer to a CPerson object. The following example uses a CObList collection to hold CPerson objects: class CPerson : public CObject {...}; CPerson* p1 = new CPerson(...); CObList myList; myList.AddHead( p1 ); // No cast needed CPerson* p2 = ( CPerson* )myList.GetHead(); This technique of using a predefined collection type and casting as necessary may be adequate for many of your collection needs. If you need further functionality or more type safety, use a template-based class, or read the next procedure.

To derive and extend a nontemplate type-safe collection


Derive your own collection class from one of the predefined nontemplate classes. When you derive your class, you can add type-safe wrapper functions to provide a type-safe interface to existing functions. For example, if you derived a list from CObList to hold CPerson objects, you might add the wrapper functions AddHeadPerson and GetHeadPerson, as shown below. class CPersonList : public CObList { public: void AddHeadPerson(CPerson* person) {AddHead(person);}

SEED Infotech Ltd.

Serialization and Collection Classes

Page 107

const CPerson* GetHeadPerson() {return (CPerson*)GetHead();} };

These wrapper functions provide a type-safe way to add and retrieve CPerson objects from the derived list. You can see that for the GetHeadPerson function, you are simply encapsulating the type casting. You can also add new functionality by defining new functions that extend the capabilities of the collection rather than just wrapping existing functionality in type-safe wrappers. For example, the article Collections: Deleting All Objects in a CObject Collection describes a function to delete all the objects contained in a list. This function could be added to the derived class as a member function. 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.

Iterating an Collection class


Iterating an array Iterating a list Iterating a map

SEED Infotech Ltd.

Page 108

Serialization and Collection Classes

To iterate an array
Use sequential index numbers with the GetAt member function: CTypedPtrArray<CObArray, CPerson*> myArray; for( int i = 0; i < myArray.GetSize();i++ ) { CPerson* thePerson = myArray.GetAt( i ); ... }

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; POSITION pos = myList.GetHeadPosition(); while( pos != NULL ) { CPerson* thePerson = myList.GetNext( pos ); ... }

This example uses a typed pointer list to contain pointers to CPerson objects.

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; 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 }

SEED Infotech Ltd.

Serialization and Collection Classes

Page 109

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 cleaner and provides better type safety. The nontemplate code is more complicated, as you can see here: CMapStringToOb myMap; //A nontemplate collection class 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 }

Deleting Object in a Collection class


A list An array A map

To delete all objects in a list of pointers to CObject


3) Use GetHeadPosition and GetNext to iterate through the list. 4) Use the delete operator to delete each object as it is encountered in the iteration. 5) Call the RemoveAll function to remove all elements from the list after the objects associated with those elements have been deleted. The following example shows how to delete all objects from a list of CPerson objects. Each object in the list is a pointer to a CPerson object that was originally allocated on the heap. CTypedPtrList<CObList, CPerson*> myList; POSITION pos = myList.GetHeadPosition(); while( pos != NULL ) { delete myList.GetNext( pos ); } myList.RemoveAll();

The last function call, RemoveAll, is a list member function that removes all elements from the list. The member function RemoveAt removes a single element.

SEED Infotech Ltd.

Page 110

Serialization and Collection Classes

Notice the difference between deleting an elements object and removing the element itself. Removing an element from the list merely removes the lists reference to the object. The object still exists in memory. When you delete an object, it ceases to exist and its memory is reclaimed. Thus, it is important to remove an element immediately after the elements object has been deleted so that the list wont try to access objects that no longer exist.

To delete all elements in an array


1) Use GetSize and integer index values to iterate through the array. 2) Use the delete operator to delete each element as it is encountered in the iteration. 3) Call the RemoveAll function to remove all elements from the array after they have been deleted. The code for deleting all elements of an array is as follows: CArray<CPerson*, CPerson*> myArray; int i = 0; while (i < myArray.GetSize() ) { delete myArray.GetAt( i++ ); } myArray.RemoveAll();

Like the list example above, you can call RemoveAll to remove all elements in an array or RemoveAt to remove an individual element.

To delete all elements in a map


1) Use GetStartPosition and GetNextAssoc to iterate through the array. 2) Use the delete operator to delete the key and/or value for each map element as it is encountered in the iteration. 3) Call the RemoveAll function to remove all elements from the map after they have been deleted. The code for deleting all elements of a CMap collection is as follows. Each element in the map has a string as the key and a CPerson object (derived from CObject) as the value. CMap<CString, LPCSTR, CPerson*, CPerson*> myMap; // ... Add some key-value elements... // Now delete the elements POSITION pos = myMap.GetStartPosition(); while( pos != NULL ) { CPerson* pPerson; CString string; // Gets key (string) and value (pPerson) myMap.GetNextAssoc( pos, string, pPerson ); delete pPerson; } // RemoveAll deletes the keys myMap.RemoveAll();

You can call RemoveAll to remove all elements in a map or RemoveKey to remove an individual element with the specified key.

SEED Infotech Ltd.

Serialization and Collection Classes

Page 111

Stacks
Because the standard list collection has both a head and a tail, it is easy to create a derived list collection that mimics the behavior of a last-in-first-out (LIFO) stack. A stack is like a stack of trays in a cafeteria. As trays are added to the stack, they go on top of the stack. The last tray added is the first to be removed. The list collection member functions AddHead and RemoveHead can be used to add and remove elements specifically from the head of the list; thus the most recently added element is the first to be removed.

To create a stack collection


Derive a new list class from one of the existing MFC list classes and add more member functions to support the functionality of stack operations. The following example shows how to add member functions to push elements on to the stack, peek at the top element of the stack, and pop the top element from the stack class CTray : public CObject { ... }; class CStack : public CTypedPtrList< CObList, CTray* > { public: // Add element to top of stack void Push( CTray* newTray ) { AddHead( newTray ); } // Peek at top element of stack CTray* Peek() { return IsEmpty() ? NULL : GetHead(); } // Pop top element off stack CTray* Pop() { return RemoveHead(); } };

Note that this approach exposes the underlying CObList class. The user can call any CObList member function, whether it makes sense for a stack or not.

Queues
Because the standard list collection has both a head and a tail, it is also easy to create a derived list collection that mimics the behavior of a first-in-first-out queue. A queue is like a line of people in a cafeteria. The first person in line is the first to be served. As more people come, they go to the end of the line to wait their turn. The list collection member functions AddTail and RemoveHead can be used to add and remove elements specifically from the head or tail of the list; thus the most recently added element is always the last to be removed.

To create a queue collection


Derive a new list class from one of the predefined list classes provided with the Microsoft Foundation Class Library and add more member functions to support the semantics of queue operations.

SEED Infotech Ltd.

Page 112

Serialization and Collection Classes

The following example shows how you can append member functions to add an element to the end of the queue and get the element from the front of the queue. class CPerson : public CObject { ... }; class CQueue:public CTypedPtrList< CObList, CPerson* > { public: // Go to the end of the line void AddToEnd( CPerson* newPerson ) { AddTail( newPerson ); } // End of the queue // Get first element in line CPerson* GetFromFront() { return IsEmpty() ? NULL : RemoveHead(); } };

Summary
Serialization is the process of writing or reading an object to or from a persistent storage medium, such as a disk file. MFC supplies built-in support for serialization in the class CObject. MFC uses an object of the CArchive class as an intermediary between the object to be serialized and the storage medium. A CArchive object uses overloaded insertion (<<) and extraction (>>) operators to perform writing and reading operations. MFC supplies two kinds of collection classes

Collection classes based on templates Collection classes not based on templates

MFC predefines two categories of template-based collections: Simple array, list, and map classes

CArray, CList, CMap CTypedPtrArray, CTypedPtrList, CTypedPtrMap

Arrays, lists, and maps of typed pointers

Quiz
3) 4) 5) 6) 7) 8) What is serialization? Which are the classes involved in serialization? What are the five main steps to make a class serializable? What is the significance of schema number in serialization? How to iterate an collection class (CArray, CList, CMap)? How to delete object from collection class (CArray, CList, CMap)?

SEED Infotech Ltd.

You might also like