Serialization: Making a Serializable Class
The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.
The latest version of this topic can be found at Serialization: Making a Serializable Class.
Five main steps are required to make a class serializable. They are listed below and explained in the following sections:
Deriving your class from CObject (or from some class derived from
CObject
).Overriding the Serialize member function.
Using the DECLARE_SERIAL macro in the class declaration.
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.
Deriving Your Class from 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
), as shown in the following declaration of class CPerson
, you gain access to the serialization protocol and functionality of CObject
.
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 Serialize
is storing (writing data) or loading (reading data). Using the results of IsStoring
as a guide, you either insert your object's data in the CArchive
object 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, of types CString
and WORD. 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 )
// empty constructor is necessary
CPerson();
virtual ~CPerson();
CString m_name;
WORD m_number;
void Serialize( CArchive& archive );
};
To override the Serialize member 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 example shows how to implement
Serialize
for theCPerson
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; }
You can also use the CArchive::Read and CArchive::Write member functions to read and write large amounts of untyped data.
Using the DECLARE_SERIAL Macro
The DECLARE_SERIAL
macro is required in the declaration of classes that will support serialization, as shown here:
class CPerson : public CObject
{
public:
DECLARE_SERIAL( CPerson )
Defining a Constructor with No Arguments
MFC requires a default constructor when it re-creates your objects as they are deserialized (loaded from disk). The deserialization process will fill in all member variables with the values required to re-create the object.
This constructor can be declared public, protected, or private. If you make it protected or private, you help make sure that it will only be used by the serialization functions. The constructor must put the object in a state that allows it to be 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 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. (Don't confuse this schema number with database terminology.)
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. For usage information and an example, see the GetObjectSchema
member function of class CArchive
.
The following example shows how to use IMPLEMENT_SERIAL
for a class, CPerson
, that is derived from CObject
:
IMPLEMENT_SERIAL( CPerson, CObject, 1 )
Once you have a serializable class, you can serialize objects of the class, as discussed in the article Serialization: Serializing an Object.