Serialization (Serialization) Reprinted from: VC Knowledge Author: Arong serial Microsoft is the right target for file I / O of a mechanism, the mechanism in the framework of (Frame) / document (Document) / The view (View) mode has been well applying. Many people do not understand what is serialization, how to make objects have serialization capabilities and how to use serialization functions. This article tries to make a simple explanation for serialization. Since I don't have much to use the serialization function, I would like to understand.
MFC frame / document / view structure in the structure
CFile is the base class for all file classes in the MFC class library. All MFCs provided by the file I / O functionality are related to this class. In many cases, everyone likes to call CFile :: Write / Writehuge to write files, call cfile :: read / readhuge to read the file. Such file I / O actually and does not use MFC file I / O nothing difference, and even with the previous ANSI C file I / O does not have much difference, the difference is nothing more than the calling API.
When you start learning C , everyone must be very familiar with CIN / COUT. These two objects use very clear << and >> operators for I / O, which is:
// Sample Code 1
INT I;
Cin >> I;
// Here do Something To Object I
Cout << i;
When using this approach, use the operator overload function, you can use a statement to complete the read and write to a series of objects without distinguishing between object specific types. The MFC provides class CARCHIVE to implement the operator << and >> overload, and it is desirable to perform file I / O in the previous CIN and COUT. Through the cooperation of the CFILE class, not only the file read and write to simple types such as int / float, but also to read and write the file read or written to the sequentialized object (SERIALIZABLE Objects, this concept).
Under normal circumstances, the procedure for reading the operation using CARCHIVE is as follows:
// Sample Code 2
/ / Define file objects and file exception objects
CFILE FILE;
CfileException Fe;
/ / Open the file in reading mode
IF (! file.open (filename, cfile :: ModeRead, & fe)
{
Fe.ReportError ();
Return;
}
// Build a CARCHIVE object
CARCHIVE AR (& File, Carchive :: Load);
Ar >> Obj1 >> OBJ2 >> OBJ3 ... >> OBJN;
ar.flush ();
// After reading, turn off the file stream
ar.close ();
File.Close ();
The process of writing using CARCHIVE is as follows:
// Sample Code 3
/ / Define file objects and file exception objects
CFILE FILE;
CfileException Fe;
/ / Open the file in reading mode
IF (file.open (filename, cfile :: modeter | cfile :: modecreate, & fe)
{
Fe.ReportError ();
Return;
}
// Build a CARCHIVE object
CARCHIVE AR (& File, Carchive :: Load);
Ar << obj1 << obj2 << obj3 ... << Objn;
ar.flush ();
// Write, turn off the file stream
ar.close ();
File.Close ();
It can be seen that for a file, if the order of the object within the file is fixed, only the document read and write only the operator used in the form. In the MFC's frame / document / view structure, the composition of an internal object of a document is often fixed, in which case the object is also fixed in the file when writing to the file. Therefore, CDocument uses the SERILIZE virtual function provided by its base class COBject to implement the read and write of the automatic document. When the user selects the file menu / open file (id_file_open) on the interface, the ONFileOpen function of the CWINAPP derived class is automatically called, which creates (MDI) / reuse (SDI) framework, documentation and view objects through document template, and finally call cdocument. :: onOpendocument to read the file, cdocument :: onopendocument processing process is as follows:
// Sample Code 4
Bool cdocument :: onopendocument (lpctstr lpszpathname)
{
IF (ismodified ())
TRACE0 ("Warning: onopendocument replacs an unsaved document./N");
CfileException Fe;
Cfile * pfile = getfile (LPSZPATHNAME,
Cfile :: ModeRead | CFILE :: Sharednywrite, & fe);
IF (pfile == null)
{
Reportsaveloadexception (LPSZPATHNAME, & FE,
False, AFX_IDP_FAILED_TO_OPEN_DOC;
Return False;
}
DeleteContents ();
SetModifiedFlag (); // dirty during de-serialize
Carchive Loadarchive (Pfile, Carchive :: Load | CARCHIVE :: Bnoflush ";
Loadarchive.m_pdocument = tris;
Loadarchive.m_bforceflat = false;
Try
{
CWAITCURSOR WAIT;
IF (pfile-> getLength ()! = 0)
Serialize (loadingarchive); // load me
Loadarchive.close ();
ReleaseFile (Pfile, False);
}
Catch_all (e)
{
ReleaseFile (Pfile, True);
DeleteContents (); // Remove Failed Contents
Try
{
Reportsaveloadexception (lpszpathname, e,
False, AFX_IDP_FAILED_TO_OPEN_DOC;
}
END_TRY
DELETE_EXCEPTION (E);
Return False;
}
END_CATCH_ALL
SetModifiedFlag (False); // Start Off with unmodified
Return True;
}
Similarly, when the user selects menu file / file saves (id_file_save) or file / saving as ... (id_file_saveas), the cDocument :: OnfilesAveSave and CWINAPP :: OnfileSaves final call cDocument :: OnSaveDocument, this function is processed as follows:
// Sample Code 5
Bool cdocument :: OnSaveDocument (lpctstr lpszpathname) {
CfileException Fe;
CFILE * PFILE = NULL;
Pfile = getfile (lpszpathname, cfile :: modecreate |
Cfile :: ModeReadwrite | CFILE :: ShareExClusive, & fE);
IF (pfile == null)
{
Reportsaveloadexception (LPSZPATHNAME, & FE,
TRUE, AFX_IDP_INVALID_FILENAME);
Return False;
}
Carchive Savearchive (Pfile, Carchive :: Store | CARCHIVE :: Bnoflush ";
SavearchiVe.m_pdocument = this;
SavearchiVe.m_bforceflat = false;
Try
{
CWAITCURSOR WAIT;
Serialize (savearchive); // Save me
Savearchive.close ();
ReleaseFile (Pfile, False);
}
Catch_all (e)
{
ReleaseFile (Pfile, True);
Try
{
Reportsaveloadexception (lpszpathname, e,
TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
}
END_TRY
DELETE_EXCEPTION (E);
Return False;
}
END_CATCH_ALL
SetModifiedFlag (false); // back to unmodified
Return True; // Success
}
As you can see from the previous two segments, the structure of file read and file written is basically the same, and eventually calls the COBject :: Serialize function to complete the document yourself (see Save Me and Load Me in the comment). For MDI and SDI generated with AppWizard, the system automatically generates overload implementation of this function, the default implementation is:
// Sample code 6
Void CMYDOC :: Serialize (CARCHIVE & AR)
{
IF (ar.isstoring ())
{
// Todo: Add Storing Code Here
}
Else
{
// Todo: add loading code here
}
}
If a person who is very familiar with VC, I like to generate all the code (of course, this is very wasteful time is not necessary), then he provides the cdocument derived class, which should also achieve this default Serialize function, otherwise, the system is in the file You can only call COBJECT :: Serialize when you read and write, and this function does not do, and of course you can't complete file saving / loading of a specific object. Of course, the user can intercept the ID_FILE_OPEN and other menus, implement their own file read and write functions, but such code will become very cumbersome, it is not easy to read.
Go back to the CMYDoc :: Serialize function. This function determines whether it is still read or written by the judgment of the AR object. Since AppWizard doesn't know what your document is dry, it will not add the actual file read and write code. Suppose there are three objects m_obj_a, m_obj_b, m_obj_c in your document, and the actual code should be:
// Sample code 7
Void CMYDoc :: Serialize (CARCHIVE & AR) {
IF (ar.isstoring ())
{
Ar << m_obj_a << m_obj_b << m_obj_c;
}
Else
{
Ar >> M_OBJ_A >> m_obj_b >> m_obj_c;
}
}
Serializable Objects (SERIALIZABLE OBJECT)
One basic condition to be used in the way of using the sample code 7 is: m_obj_a and other objects must be a serialized object. A serialized object is:
This class is derived from the COBJECT. This class implements the Serialize function. This class uses the declare_serial macro in the custom, using the implementation_serial macro in the implementation file of the class has a constructor that does not have a parameter, or a band-band constructor All parameters provide the default parameters
Here, there is no simplified type in the serialized object conditions. For simple types, CARCHIVE basically implements overloaded operators << and >>, so you can use a serialization method to read and write.
Detect from the COBJECT class
Serialization requirements are derived from COBJECT or derived from a COBJECT. This requirement is relatively simple, because almost all classes (excluding cstring) are derived from COBJECT, so this requirement is met for classes inherited from the MFC class. For your own data class, you can specify its base class as cobject to meet this requirement.
Implement a Serialize function
The Serialize function is a function of the object that actually saves the data is the core of the entire serialization. It is the same as CMYDOC :: Serialize, using carchive :: isstoring and carchive :: isloading to determine the current operation and select << and >> to save and read objects.
Use Declare_Serial Macro
The Declare_Serial Macro includes the Declare_Dynamic and Declare_DyncReate feature, which defines a class of cruntimeClass related information and implements the default Operator >> overload. After this macro, CARCHIVE can use ReadObject and WriteObject to perform object I / O, and can read objects from the file in the case of the type in advance.
Use Implement_Serial
Declare_serial Macro and Implement_Serial Macros must appear, otherwise the Declare_Serial macro definition entity will not be implemented, eventually resulting in connection errors.
Default constructor
This is the requirements of CRUNTIMECLASS :: CreateObject to objects.
Special case
Ready to objects only by the Serialize function, without using the ReadObject / WriteObject and operator overload, the front of the serialization conditions are not required, as long as the Serialize function is implemented. For existing classes, if it does not provide a serialization function, you can implement it using the heavy-duty friend operator << and Operator >>.
example
Assume that a geometric graphic display, editing program, supports scalable graphics feature. Here, you don't want to discuss the implementation of the specific graphics system, only to discuss the save and load of the image object.
Base class CPICTURE
Each graphic object is derived from CPICTURE. This class implements a serialization function. In fact, the current code is:
// Header Picture.h
#if! defined (__ picture_h__)
#define __picture_h__
#if _MSC_VER> 1000 # Pragma ONCE
#ENDIF / / 100 m _ _ _
const INT TYPE_UNKNOWN = -1;
Class CPICTure: Public COBJECT
{
INT M_NTYPE; / / Graphics Category
DECLARE_SERIAL (CPICTURE)
PUBLIC:
CPICTURE (int m_ntype = type_unknown): m_ntype (m_NTYPE) {};
INT gettype () const {return m_ntype;};
Virtual Void Draw (CDC * PDC);
Void Serialize (CARCHIVE & A);
}
#ENDIF
// CPP file Picture.cpp
#include "stdafx.h"
#include "picture.h"
#ifdef _Debug
#define new debug_new
#undef this_file
Static char this_file [] = __file__;
#ENDIF
Void CPICTURE :: DRAW (CDC * PDC)
{
// The base class does not realize the drawing function, implemented by derived class
}
Void CPICTure :: Serialize (CARCHIVE & A)
{
IF (ar.doading ())
{
Ar << m_ntype;
} else {
Ar >> M_NTYPE;
}
}
Note: Since CRUNTIMECLASS requires this object to be able to be instantiated, although the DRAW function does not have any drawing operations, this class still does not define it into a pure virtual function.
Objects in CDocument Derivation Class Save and File I / O Process
To simplify the design, in the CDocument class, the template cptrlist provided by MFC is saved. This object is defined as:
protected:
Ctypedptrlist
M_ListPictures;
Since ctyptrlist and cptrlist do not implement the serialize function, it is not possible to serialize the object via Ar
Void CTSDoc :: Serialize (CARCHIVE & A)
{
Position POS;
IF (ar.isstoring ())
{
// Todo: Add Storing Code Here
POS = M_ListPictures.getHeadPosition ();
While (POS! = null)
{
Ar << M_ListPictures.getNext (POS);
}
}
Else
{
// Todo: add loading code here
REMOVEAll ();
CPICTure * PPICTURE;
Do {
Try
{
AR >> PPICTURE;
Trace ("Read Object% D / N", PPICTURE-> GetType ());
M_ListPictures.addtail (PPICTURE);
}
Catch (CEXCEPTION * E)
{
E-> delete ();
Break;
}
} while (PPICTURE! = NULL);
}
m_pcurrent = NULL;
SetModifiedFlag (false);
}
The serial function of the derived class is implemented in line, rectangular, triangular, elliptical, and other graphics, respectively, with CLINE, CRECTANGLE, CTRIANGLE and CELLIPSE. Take a class CLINE as an example to implement serialization function:
Detecting CLINE from CPICTURE, adding the following member variables in the CLINE class: cpoint m_ptstart, m_ptend;
In this line, the line is increased as the following macro: declare_serial (cline)
Implement Serialize Functions Void Cline :: Serialize (CARCHIVE & A)
{
CPICTURE :: Serialize (AR);
IF (ar.doading ())
{
Ar >> m_ptstart.x >> m_ptostart.y >> m_ptend.y;
} else {
Ar << m_ptstart.x << m_ptstart.y << m_ptend.x << m_ptend.y;
}
}
Add import_serial (Cline, CPICTURE, TYPE_LIN) in the CPP file;
This definition of CLINE has serialization, and other graphics classes can be similarly defined.