序列化:定义可序列化的类

使类可序列化所需的五个主要步骤。 下面列出了这些步骤,后续章节中对它们进行了说明:

  1. 从 CObject 派生类(或者从派生自 CObject 的某个类进行派生)。

  2. 替代 Serialize 成员函数

  3. 在类声明中的使用 DECLARE_SERIAL 宏

  4. 定义没有参数的构造函数

  5. 为你的类在实现文件中使用 IMPLEMENT_SERIAL 宏

如果直接调用 Serialize 而不是通过 CArchive 的 >> 和 << 运算符进行调用,则最后三个步骤对序列化来说不是必需的。

从 CObject 派生你的类

基本序列化协议和功能在 CObject 类中定义。 通过从 CObject 或派生自 CObject 的类派生您的类(如类 CPerson 的以下声明中所示),您将能够访问 CObject 的序列化协议和功能。

替代 Serialize 成员函数

Serialize 类中定义的 CObject 成员函数负责实际上序列化捕获对象的当前状态所需的数据。 Serialize 函数具有一个 CArchive 参数,供它读取和写入对象数据。 CArchive 对象具有成员函数 IsStoring,该函数指示 Serialize 是进行存储(写入数据)还是加载(读取数据)。 以 IsStoring 的结果为参考,可使用插入运算符 (<<) 将对象的数据插入 CArchive 对象或使用提取运算符 (>>) 提取数据。

请考虑使用派生自 CObject 并包含两个类型分别为 CString 和 WORD 的新成员变量的类。 以下类声明片段显示了新成员变量以及重写的 Serialize 成员函数的声明:

class CPerson : public CObject
{
public:
   DECLARE_SERIAL(CPerson)
   // empty constructor is necessary
   CPerson();
   virtual ~CPerson();

   CString m_name;
   WORD   m_number;

   void Serialize(CArchive& archive);
};

重写 Serialize 成员函数

  1. 调用基类版本的 Serialize 以确保对象的继承部分已序列化。

  2. 插入或提取特定于您的类的成员变量。

    插入和提取运算符与存档类交互以读取和写入数据。 以下代码示例演示如何为前面声明的 Serialize 类实现 CPerson

    void CPerson::Serialize(CArchive& archive)
    {
       // call base class function first
       // base class is CObject in this case
       CObject::Serialize(archive);
    
       // now do the stuff for our specific class
       if (archive.IsStoring())
          archive << m_name << m_number;
       else
          archive >> m_name >> m_number;
    }
    

还可使用 CArchive::ReadCArchive::Write 成员函数读取和写入大量非类型化数据。

使用 DECLARE_SERIAL 宏

声明支持序列化的类需要 DECLARE_SERIAL 宏,如下所示:

class CPerson : public CObject
{
public:
   DECLARE_SERIAL(CPerson)

定义没有参数的构造函数

当 MFC 因为对象被反序列化(从磁盘加载)而重新创建对象时,MFC 需要一个默认构造函数。 反序列化过程将使用重新创建对象所需的值填写所有成员变量。

此构造函数可声明为公共、受保护或私有。 如果您使它成为受保护或私有构造函数,您就帮助确保了它只会由序列化函数使用。 构造函数必须将对象设为允许它在必要时删除的状态。

注意

如果忘记在使用 DECLARE_SERIAL 和 IMPLEMENT_SERIAL 宏的类中定义没有参数的构造函数,你将在使用 IMPLEMENT_SERIAL 宏的行上获得“没有默认构造函数可用”编译器警告。

在实现文件中使用 IMPLEMENT_SERIAL 宏

IMPLEMENT_SERIAL 宏用于定义从 CObject 派生可序列化类时所需的各种功能。 您为您的类在实现文件 (.cpp) 中使用此宏。 此宏的前两个参数是类的名称和其直接基类的名称。

该宏的第三个参数是一个架构数字。 架构数字本质上是类的对象的版本号。 请对架构数字使用大于或等于 0 的整数值。 (不要将此架构数字与数据库术语混淆。)

MFC 序列化代码在将对象读入内存时检查架构数字。 如果磁盘上的对象的架构数字与内存中的架构数字不匹配,库将引发 CArchiveException,这会阻止您的程序读取不正确的版本的对象。

如果希望 Serialize 成员函数能够读取多个版本(即,用不同版本的应用程序编写的文件),可使用值 VERSIONABLE_SCHEMA 作为 IMPLEMENT_SERIAL 宏的自变量。 有关用法信息和示例,请参阅类 GetObjectSchemaCArchive 成员函数。

以下示例演示如何为派生自 CObject 的类 CPerson 使用 IMPLEMENT_SERIAL:

IMPLEMENT_SERIAL(CPerson, CObject, 1)

获得可序列化的类后,可序列化该类的对象,如序列化:序列化对象一文所述。

另请参阅

序列化