Serialization
Throughout many of the MFC classes, a Serialize
function is present that has the responsibility for saving and loading that
document to and from a disk file. This example of polymorphism is introduced
in the CObject class and is implemented for most of the classes derived
from it.
The CDocument class, being the class responsible for data saving and loading,
also implements a Serialize function, but it's an empty function
that AppWixard creates for you. It's your job to place in this function
the code needed to save and restore any persistant data you want.
Serialize
The skeleton Serialize function
is created as:
void CappDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } }
It's parameter, ar, is a CArchive object which has already been open
to the source or destination file for reading or writing. MFC will open
the file in response to the user's File/Save or File/Open
menu selection, then pass the open file to this function as ar.
The code if( ar.IsStoring() ) , servers as the method to determine if you are being
asked to store data or not (if not, your loading). You can then use the
CArchive member functions to store and load the data.
Making your custom objects serializable
In order for your custom classes
to take advantage of serialization, you must implement the following items
for your class:
CArchive and CFile
A CArchive is not derived
from a CFile, but it's constructor needs a valid CFile as a parameter.
The CArchive object implements it's own set of << and >>
operators, like cin and cout do. The CFile object, is more
designed for reading and writing blocks of data (binary access, not text).
Normally, MFC will create the CArchive for you. But, there may be times
you want to create your own. For example: Imagine your creating a program
to edit HTML code, for a web browser. You want the user to be able to store
the document (an HTML file) into a temporary file and then launch a web
browser with that temporary file. You don't want the user to have to save
their changes to the real file however, because they only want to test their
changes. So, you would create a CFile to a temporary file, then create a
CArchive from that, and then invoke the Document's Serialize routine
yourself. For example (this code has not been tested):
void CappView::OnViewWithBrowser() { CFile Tmp; if( Tmp.Open("C:\\TEMP\\TEST.HTML", CFile::modeWrite, NULL ) ) { CArchive TmpArc( &Tmp, CArchive::store ); GetDocument()->Serialize( &TmpArc ); // Serialize to temporary file TmpArc.Close(); Tmp.Close(); // Launch system's default browser: ShellExecute( this->m_hWnd, "OPEN", "C:\\TEMP\\TEST.HTML", NULL, NULL, SW_NORMAL ); } else MessageBox( "Error creating temporary viewing file" ); }
Serialization Example
Mflat.h
class MFlatFile : public Cfile // Step 1: derived from CObject { public: DECLARE_SERIAL( MFlatFile ) // Step 3: Use DECLARE_SERIAL MFlatFile() { RecordSize=0; } // Step 4: Default constructor void Serialize( CArchive& ar ); // Step 2: We provide a Serialize protected: CObArray FldList; int RecordSize; };
Mflat.cpp
IMPLEMENT_SERIAL( MFlatFile, CFile, 1 ) //Step 5: use IMPLEMENT_SERIAL void MFlatFile::Serialize( CArchive & ar ) { CFile::Serialize(ar); // Call Base class's Serialize routine FldList.Serialize( ar ); // Call Serialize for each data member (that supports it) if( !ar.IsStoring() ) // We don't serialize RecordSize, it is re-calculated when a file is loaded { RecordSize=0; for(int i=0; i < GetFieldCount(); i++ ) RecordSize += GetField(i)->GetSize(); } }
Note: The FldList data member is a collection class. When it is serialized
for storing, it will save the number of elements it contains, and then iterate
through each element calling that elements Serialize routine. The
reverse is done on serializing for loading. This means that the CObArray
class implements the basic serialize ability, but ultimately whatever class
it maintains must also be serializable.