How to Make a Serializable Class

There are five main requirements to make a class serializable. They are listed below and explained in the following sections.

1.Derive your class from CObject (or from some class derived from CObject.)

2.Use the DECLARE_SERIAL macro in the class declaration.

3.Define a constructor that takes no arguments.

4.Use the IMPLEMENT_SERIAL macro in the implementation file for your class.

5.Override the Serialize member function.

Deriving Your Class from CObject and Using the DECLARE_SERIAL Macro

The basic serialization protocol and functionality is defined in the CObject class. By deriving your class from CObject, (or from some class derived from CObject) as shown in the example code below, you gain access to the serialization protocol and functionality of CObject.

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...

};

Defining a Constructor with No Arguments

This constructor is used by the Microsoft Foundation Class Library when it re-creates your objects as they are deserialized (loaded from disk.) Typically, this constructor is an empty function, since the deserialization process will fill in all member variables with the values necessary to re-create the object.

This constructor can be declared public, protected, or private. If you make it protected or private, you can be sure that it will only be used by the serialization functions. The constructor must put the object in a state so that it can be safely deleted if necessary.

Note:

If you forget to define a constructor with no arguments in a class that uses the DECLARE_SERIAL and IMPLEMENT_SERIAL macros, you will get a “no default constructor available” compiler warning on the line where the IMPLEMENT_SERIAL macro is used.

Using the IMPLEMENT_SERIAL Macro in the Implementation File

The IMPLEMENT_SERIAL macro is used to define 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 Microsoft Foundation Class Library 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, then the Foundation will throw an exception, preventing your program from reading an incorrect version of the object.

The following example shows how to use IMPLEMENT_SERIAL for a class, CPerson, that is derived from CObject:

IMPLEMENT_SERIAL( CPerson, CObject, 0 )

Overriding the Serialize Member Function

The Serialize member function, which is defined in the CObject class, is responsible for actually serializing the data necessary to capture an object's current state. The Serialize function has a CArchive argument that it uses to read and write the object data. The CArchive object has a member function, IsStoring, which indicates whether the serialization is storing (writing data) or loading (reading data). Using this function as a guide, you either insert your object data in the CArchive with the insertion operator (<<) or extract data with the extraction operator (>>).

Consider a class that is derived from CObject and has two new member variables, a CString and a UINT. The following class declaration fragment shows the new member variables and the declaration for the overridden Serialize member function:

class CPerson : public CObject

{

public:

DECLARE_SERIAL( CPerson, CObject )

// empty constructor is necessary

CPerson(){};

CString m_name;

UINT m_number;

void Serialize( CArchive& archive );

// rest of class declaration

};

·To override the Serialize member function:

First call your base class version of Serialize to make sure that the inherited portion of the object is serialized.

Then insert or extract the member variables that are specific to your class.

The insertion and extraction operators do all the hard work of interacting with the archive class to read and write the data. The following example shows how to implement Serialize for the CPerson class declared above:

void CPerson::Serialize( CArchive& archive )

{

// call base class function first

// base class is CObject in this case

CObject::Serialize( archive );

// now do the stuff for our specific class

if( archive.IsStoring() )

archive << m_name << m_number;

else

archive >> m_name >> m_number;

}