7.4 定制串行化
前面编辑器的例子使用CString类的字符串来保存文本行,由于它是MFC类,因此可以串行化自己,将自己写入磁盘或从磁盘文件中读取二进制数据来建立对象。那么,如果不是标准的MFC类,比如用户自己定义的类,如何让它支持串行化呢?下面,我们结合前面第五章提到的就业调查表的例子来演示如何让用户定义的类支持串行化功能。
要让用户定义的类支持串行化,一般分为五步:
1.从CObject或其派生类派生出用户的类
2.重载Serialize()成员函数,加入必要的代码,用以保存对象的数据成员到CArchive对象以及从CArchive对象载入对象的数据成员状态。
3.在类声明文件中,加入DECLARE_SERIAL宏。编译时,编译器将扩充该宏,这是串行化对象所必需的。
4.定义一个不带参数的构造函数。
5.在实现文件中加入IMPLEMENT_SERIAL宏。
class CRegister:public CObject
{
public:
DECLARE_SERIAL( CRegister)
//必需提供一个不带任何参数的空的构造函数
CRegister(){};
public:
CString strIncome;
CString strKind;
BOOL bMarried;
CString strName;
int nSex;
CString strUnit;
int nWork;
UINT nAge;
void Serialize(CArchive&);
};
MFC在从磁盘文件载入对象状态并重建对象时,需要有一个缺省的不带任何参数的构造函数。串行化对象将用该构造函数生成一个对象,然后调用Serialize()函数,用重建对象所需的值来填充对象的所有数据成员变量。
构造函数可以声明为public、protected或private。如果使它成为protect或private,则可以确保它只被串行化过程所使用。
在类定义文件中给出Serialize()的定义。它包括对象的保存和载入两部分。前面已经提到,CArchive类提供一个IsStoring()成员函数指示是保存数据到磁盘文件还是从磁盘文件载入对象。
void CRegister::Serialize(CArchive& ar)
{
//首先调用基类的Serialize()方法。
CObject::Serialize( ar);
if(ar.IsStoring())
{
ar<<strIncome;
ar<<strKind;
ar<<(int)bMarried;
ar<<strName;
ar<<nSez;
ar<<strUnit;
ar<<nWork;
ar<<(WORD)nAge;
}
else
{
ar>>strIncome;
ar>>strKind;
ar>>(int)bMarried;
ar>>strName;
ar>>nSex;
ar>>strUnit;
ar>>nWork;
ar>>(WORD)nAge;
}
}
我们看到,对象的串行化实际上是通过调用对象中的数据成员的串行化来完成的。
注意:CArchive类的>>和<<操作符并不支持所有的标准数据类型。它支持的数据类型有:CObject、BYTE、WORD、int、LONG、DWORD、float和double。其他的类型的数据要进行串行化输入输出时,需要将该类型的数据转化为上述几种类型之一方可。
另外,在类的实现(类定义)文件开始处,还要加入IMPLEMENT_SERIAL宏。
IMPLEMENT_SERIAL( CRegister, CObject, 1 )
IMPLEMENT_SERIAL宏用于定义一个从CObject派生的可串行化类的各种函数。宏的第一和第二个参数分别代表可串行化的类名和该类的直接基类。
第三个参数是对象的版本号,它是一个大于或等于零的整数。MFC串行化代码在将对象读入内存时检查版本号。如果磁盘文件上的对象的版本号和内存中的对象的版本号不一致,MFC将抛出一个CArchiveException异常,阻止程序读入一个不匹配版本的对象。
现在,我们就可以象使用标准MFC类一样使用CRegister的串行化功能了。
CArchive ar;
CRegister reg1,reg2;
ar<<reg1<<reg2;
读者请试着在第五章职工调查表程序基础上,增加保存调查信息到文件以及从文件中读入调查表信息功能。对于多个调查表,可考虑采用CObjList链表保存多个对象的指针。
串行化简化了对象的保存和载入,为对象提供了持续性。但是,串行化本身还是具有一定的局限性的。串行化一次从文件中载入所有对象,这不适合于大文件编辑器和数据库。对于数据库和大文件编辑器,它们每次只是从文件中读入一部分。此时,就要避开文档的串行化机制来直接读取和保存文件了。另外,使用外部文件格式(预先定义的文件格式而不是本应用程序定义的文件格式)的程序一般也不使用文档的串行化。下面我们就给出这样一个例子,说明在不使用串行化情况下如何读取和保存文件。