分类: C/C++
2008-04-24 17:02:06
序列化
序列化是比动态创建更高级的功能,它提供了将对象保存到文件或其它存储设备以及从存储中重茧对象的能力.
添加序列化支持:
CObject提供了如下两个成员支持序列化:IsSerializable和Serialize。第一个用来判断是否支持序列化,而第二个则进行实际的序列化。 当类的CRuntimeClass成员的m_wSchema为0xFFFF时,表示该对象不支持序列化:
BOOL CObject::IsSerializable()const
{
Return(GetRuntimeClass()->m_wSchema != 0xFFFF);
}
因此为了支持序列化,必须把类的GetRuntimeClass静态成员的m_wSchema 成员设置为非0xFFFF值。
这项设置工作是通过一组宏来实现的。首先在类声明中使用DECLARE_SERIAL宏。该宏出了完成DECLARE_DYNCREATE所做的工作外,还声明了友元函数operator>>,以支持ar中创建对象:
#define DECLARE_SERIAL(class_name) _DECLARE_DYNCREATE(class_name)
AFX_API friend Carchive& AFXAPI operator>>(Carchive& ar,class_name*&pOb);
然后在实现文件中使用IMPLEMENT_SERIAL宏。该宏的第三个参数是架构编号。架构编号实质上是类对象的版本号,架构编号使用大于或等于零的整数。 如果要使Serialize成员函数能够读取多个版本(即,读取应用程序的不同版本写入的文件),可将VERSIONABLE_SCHEMA值作为IMPLEMENT_SERIAL宏的参数。 该宏除了有DECLARE_DYNCREATE的全部功能外,还要: 设置CRuntimeClass静态成员的m_pClassInit成员。 将类的CRuntimeClass静态成员添加到全局运行时类信息链表中,也就是设置CRuntimeClass静态成员的m_pClassInit成员。 下面是DYNCREATE的展开形式(省去了与IMPLEMENT_DYNAMIC相同的部分): AFX_COMDAT AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema, class_name::CreateObject,&_init_##class_name) 实现operator>>函数 CArchive& AFXAPI operator>>(CArchive& ar,class_name* &pOb) { pOb = (class_name)ar.ReadObject(RUNTIME_CLASS(class_nmae)) return ar; } 有了上述准备工作,派生类只要重写Serialize成员函数即可。 virtual void Serialize(CArchive& ar);
CArchive对象具有成员函数IsStoring,该成员函数指示Serialize正在存储(写入数据)还是正在加载(读取数据)。用IsStoring的结果作为参考,使用输出运算符(<<)将对象数据插入到CArchive对象中,或使用输入运算符(>>)提取数据。
CArchive类 CArchive对象提供了一个类型安全缓冲机制,用于将可序列化对象写入CFile对象或从中读取可序列化对象。通常,CFile对象表示磁盘文件,也可以表示“剪切板”的内存文件(CsharedFile对象)。 CArchive对象要么存储数据,要么加载数据,但不能同时进行。CArchive对象的寿命只限于将对象写入文件或从文件读取对象的一次传递 。因此,需要两个连续创建的CArchive对象将数据序列化到文件,然后从文件反序列化数据。 创建CArchive对象 显式创建创建CArchive对象,首先需要创建构造CFile对象,然后将CFile对象传递到CArchive的构造函数。如下所示: CFile theFile; theFile.open(…, CFile::modeWrite); CArchive archive(&theFile, CArchive::store); CArchive构造函数的第二个参数指定是向文件中存储数据还是从文件中加载数据。对象的Serialize函数通过调用CArchive对象的IsStoring函数来检查该状态。 构造文件对象 通过两个不骤构造CFile对象。 创建文件对象,但不指定路径或权限标志。通常通过在堆栈上声明CFile变量来创建文件对象。 调用文件对象的Open成员函数,并提供路径和权限标志。 Open成员函数的原型如下: Virtual bool Open(LPCTSTR lpszFileName, UINT nOpenFlags, CfileException* pError = NULL); 打开标志指定要为文件设置的权限。如果成功的打开文件,则Open函数返回TRUE,如果未能打开指定的文件,则返回FALSE。 关闭CArchive对象 当完成向CArchive对象存储数据或从该对象中加载数据时,关闭该对象。虽然CArchive对象会自动关闭文档,好的做法是显式执行,因为这使得由错误恢复更为容易: Archive.Close(); theFile.Close(); “<<”和“>>”运算符 CArchive提供“<<”和“>>”运算符,用于向文件中写入简单的数据类型和CObjects以及从文件中读取它们。 CArchive对象的“<<”和“>>”支持的数据类型
CObject SIZE和CSize Float WORD CString POINT CPoint DWORD BYTE RECT CTect Double LONG CTime CTimeSpan Int COleCurrency COleVariant COleDateTime COleDateTimeSpan
CArchive的“<<”和“>>”运算符总是返回CArchive对象的引用,该引用为第一个操作数。因此可以链接运算符。
CArchive的“>>”运算符基于由文档先前写到文件的CRuntimeClass信息构造内存中的CObject。因此,是使用CArchive的“<<”和“>>”运算符,还是调用Serialize,取决于是否需要加载文档,基于先前存储的CRuntimeClass信息以动态的重新构造对象。在下列情况下使用Serialize函数:
总之,如果可序列化的类将嵌入的CObject定义为成员,则不应使用该对象的CArchive的“<<”和“>>”运算符,而应调用Serialize函数。此外,如果可序列化的类将指向CObject(或从CObject派生的对象)的指针定义为成员,但在自己默认的构造函数中将其构造为其他对象,则也应调用Serialize。