The Serialization Framework
MFC's serialization framework centers on a virtual Serialize(CArchive& ar) method declared on CObject and overridden by any class that wants to persist itself. A CArchive object, constructed around a CFile in either store or load mode, drives the process: calling pDoc->Serialize(ar) on a document triggers a chain of Serialize() calls down through its contained objects. To participate fully, including support for dynamic creation when loading, a class must use the DECLARE_SERIAL(class) macro in its header and the matching IMPLEMENT_SERIAL(class, baseClass, schema) macro in its source file.
Cricket analogy: The Serialize() chain flowing from a document down through contained objects is like a scorecard summary that pulls detailed figures from each individual player's card, which in turn pulls from each ball bowled, cascading down through layers.
Implementing Serialize() in a Derived Class
A typical override calls the base class's Serialize() first, then checks ar.IsStoring() to decide whether to write or read its own members using operator<< and operator>>, which are overloaded for built-in types, CString, and other CObject-derived classes. Following this base-first pattern ensures that inherited state is always handled consistently regardless of how many levels deep the class hierarchy goes, and it mirrors the same discipline expected when writing constructors that initialize base classes before derived members.
Cricket analogy: Calling the base Serialize() first is like a team announcing its playing XI in the standard batting-order sequence before adding any match-specific notes, ensuring consistency no matter which team is reporting.
class CPlayerRecord : public CObject
{
DECLARE_SERIAL(CPlayerRecord)
public:
CString m_strName;
int m_nScore;
virtual void Serialize(CArchive& ar) override
{
CObject::Serialize(ar); // base class first
if (ar.IsStoring())
{
ar << m_strName << m_nScore;
}
else
{
ar >> m_strName >> m_nScore;
}
}
};
IMPLEMENT_SERIAL(CPlayerRecord, CObject, 1 /* schema */)Serializing Collections and Pointers
CArchive has built-in support for serializing entire collections of CObject-derived pointers, such as CObArray or CTypedPtrArray, via a single ar << collection call, and it correctly tracks object identity: if two different pointers in memory reference the same underlying object, CArchive detects this using its internal MapObject bookkeeping and reconstructs the shared reference on load rather than duplicating the object into two separate copies. This makes CArchive suitable for object graphs with shared or cross-referenced nodes, not just simple trees.
Cricket analogy: CArchive tracking shared object identity is like a stats database recognizing that the same player object, Rohit Sharma, appears as both captain and opener in two different lineup slots, and storing him once rather than duplicating his record.
Versioning with Schema Numbers
The third argument to IMPLEMENT_SERIAL, the schema number, is written into the archive alongside the class's runtime type information, and GetObjectSchema() lets a Serialize() override read back which schema version produced the data currently being loaded. This allows a class to branch its loading logic, for example reading an extra field only if the schema number is 2 or higher, so that files saved by an older version of the application can still be loaded correctly by a newer version without corrupting or misinterpreting the data.
Cricket analogy: Branching on schema number is like a scoring app reading an old scoresheet format that predates DRS reviews and simply skipping the review-count field, while newer scoresheets include and parse that field.
CArchive is built on top of a CFile, buffering reads and writes for efficiency, so changes are not guaranteed to be fully on disk until the archive is closed, either explicitly via Close() or implicitly when its destructor runs. Always ensure the CArchive goes out of scope or is explicitly closed before assuming the underlying file is complete and safe to share or copy.
Forgetting the DECLARE_SERIAL(class) macro in the header, or the matching IMPLEMENT_SERIAL(class, baseClass, schema) macro in the source file, means the class lacks the runtime type information MFC's serialization framework needs to dynamically create instances while loading. This typically surfaces as an assertion failure or a corrupted read when the archive tries to reconstruct an object of that class from a CObArray or similar collection during load.
- CObject::Serialize(CArchive&) is the virtual method every serializable class overrides.
- DECLARE_SERIAL and IMPLEMENT_SERIAL macros enable runtime type info and dynamic creation during load.
- Always call the base class's Serialize() before handling a derived class's own members.
- IsStoring() and IsLoading() let one Serialize() method handle both save and load directions.
- CArchive tracks shared object identity via internal MapObject bookkeeping, avoiding duplicate copies.
- The schema number passed to IMPLEMENT_SERIAL enables backward-compatible version branching via GetObjectSchema().
- CArchive buffers internally on top of CFile and must be closed to guarantee data is flushed.
Practice what you learned
1. Which virtual method does a class override to participate in MFC's serialization framework?
2. What is the purpose of the schema number passed to IMPLEMENT_SERIAL?
3. Why does CArchive track object identity via MapObject bookkeeping?
4. What is the correct first step inside a derived class's Serialize() override?
5. What commonly happens if DECLARE_SERIAL/IMPLEMENT_SERIAL macros are omitted from a class used in serialized collections?
Was this page helpful?
You May Also Like
File I/O with MFC
Reading and writing files in MFC using CFile, CStdioFile, and CArchive, plus handling file exceptions correctly.
MFC Exception Handling
How MFC's CException hierarchy works, how to catch and clean up MFC exceptions correctly, and how they interact with standard C++ exceptions.
MFC Collection Classes
A tour of MFC's template and legacy collection classes for storing arrays, lists, and maps of data, and how to choose between them.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics