在MFC中使用CArchive进行串行化数据

发布时间 2024-01-05 21:55:28作者: 饼干`

使用CArchive进行数据串行化非常的方便。

CArchive持有一个CFile对象,通过此对象进行读写,那么所有基于CFile的子类,都可以使用CArchive来进行串行化。

示例:

CFile file(_T("1.txt"), CFile::modeReadWrite);
CArchive ar(&file, CArchive::load);

此时CArchive对象就是处于读取的状态,我们可以直接通过ar >> obj进行读取。

这里有一个要求,就是obj的类型必须是已经实现的operator >> 重载的类型。像CString,int,double这种,CArchive都已经实现了,可以直接使用。

这里有一个注意就是CArchive在串行化的时候,会向输出中添加以下其他信息,用来识别数据的类型,版本等。

那么来看一下如何让自定义的类型支持串行化,自定义的类型实现串行化,这里主要依靠operator >> 重载的CObject*的串行化版本。那么所有基于CObject的子类都可以实现串行化。

class MyDoc :
    public CObject
{
    DECLARE_SERIAL(MyDoc);
public:
    MyDoc() = default;
    MyDoc(int i, CString str);


    int m_i = 0;
    CString m_str = _T("");
public:
    virtual void Serialize(CArchive& ar);
};

这里主要是有三点是必须要做的:

1、继承CObject

2、实现宏 DECLARE_SERIAL(MyDoc);

3、重现CObject的虚函数 virtual void Serialize(CArchive& ar);

4、编写默认构造函数。

#include "MyDoc.h"

IMPLEMENT_SERIAL(MyDoc, CObject, 1 | VERSIONABLE_SCHEMA);

MyDoc::MyDoc(int i, CString str):m_i(i),m_str(str)
{
}


void MyDoc::Serialize(CArchive& ar)
{
    CObject::Serialize(ar);
    if (ar.IsStoring())
    {    
        // storing code
        ar << m_i << m_str;
    }
    else
    {    
        // loading code
        UINT schema = ar.GetObjectSchema();
        switch (schema)
        {
        case 1:
            ar >> m_i >> m_str;
            break;
        default:
            AfxThrowArchiveException(CArchiveException::badSchema);
            break;
        }
    }
}

首先看一下, IMPLEMENT_SERIAL(MyDoc, CObject, 1 | VERSIONABLE_SCHEMA); 这句话的意思,

此宏是DECLARE_SERIAL(MyDoc);的定义实现,通过前两个参数实现MyDoc与CObject的链接,后面1是版本号,宏VERSIONABLE_SCHEMA表示版本号是可变的。

版本号是什么意思呢,就是说如果我们的MyDoc对象在后期增加了数据成员,由于之前串行化的数据没有这个成员,那么版本号就起作用了,我们可以读取文件的版本号,来选择如何反序列化对象。如果老版本的串行化没有后期增加的数据成员,这里只需要反序列化已经有的数据,没有的做单独的初始化即可。

看一下虚函数的实现:

虚函数的实现首先串行化基类CObect对象,然后再串行化自身,这是常规的方式。

对于要序列化的MyDoc对象,这里可以直接使用CArchive对象进行串行化写入,那么在读取时,需要先获取文件的版本号,根据版本号进行操作。

下面是具体的使用例子:将MyDoc对象串行化。

try
{
    CFile file(_T("1.txt"), CFile::modeReadWrite);
    CArchive ar(&file, CArchive::store);
    MyDoc* doc = new MyDoc(666,_T("hello,word!"));
    ar << doc;
    ar.Flush();
    delete doc;

}
catch (CArchiveException* e)
{
    e->ReportError();
    e->Delete();
}

然后将对象反序列化:

try
{
    CFile file(_T("1.txt"), CFile::modeReadWrite);
    CArchive ar(&file, CArchive::load);
    MyDoc* doc = new MyDoc;
    ar >> doc;
    // 处理doc
    //。。。。。。。。。。。

    delete doc;

}
catch (CArchiveException* e)
{
    e->ReportError();
    e->Delete();
}

CArchive在串行化基于CObject的子类时,要注意,一定是指针,在反序列化时,对象必须时基于堆上的内存指针。