MFC序列化

序列化:把对象信息保存到存储区(如把对象信息写到文件)

反序列化:从存储区中读取对象并创建对象(从文件中读取对象信息并创建对象)

MFC序列化使用步骤

  1. 将所要序列化的类先继承CObject
  2. 类中重写虚函数void Serialize(CArchive& archive)
  3. 在类的声明中使用宏DECLARE_SERIAL(CPerson)
  4. 类中必须提供一个默认构造
  5. 在类的实现中使用宏DECLARE_SERIAL(CPerson)
class CPerson : public CObject//第一步:继承自CObject类
{
    DECLARE_SERIAL(CPerson)//第三步:类中添加宏DECLARE_SERIAL

public:
    CPerson() {}//第四步:提供默认构造
    CPerson(char* szName, int nAge):m_csName(szName), m_nAge(nAge){}

    void Serialize(CArchive& archive) override;//第二步:重写虚函数

private:
  CString m_csName;
  int m_nAge;
};

IMPLEMENT_SERIAL(CPerson, CObject, 1)//第五步:类实现中,添加宏

void CPerson::Serialize(CArchive& archive) override
{
    CObject::Serialize(archive);

    if (archive.IsStoring()) //存储写入到存储区
    {
        archive << m_csName << m_nAge;
    }
    else //从存储区读取数据,
    {
        archive >> m_csName >> m_nAge;
    }
}

MFC序列化主要是靠RTTI和CARchive这个类进行读写操作,看MFC的宏就可以看出

 #define DECLARE_SERIAL(class_name) \
    _DECLARE_DYNCREATE(class_name) \  调用的也是动态创建这个宏,我在博客中已经分析过这个宏的作用,这个宏主要是操作CRuntimeClass这个结构体,通过这个结构体形成RTTI,而通过RTTI就可以获取这个对象的信息,这样就可以通过CARchive进行读写对象

CRuntimeClass形成序列化的RTTI的链表用的是另一个成员m_pNextClass,如下就是序列化主要用到的成员

void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);//这个函数用来把注册的类添加进链表
struct AFX_CLASSINIT
	{ AFX_CLASSINIT(CRuntimeClass* pNewClass) { AfxClassInit(pNewClass); } };//通过构造函数调用AfxClassInit
struct CRuntimeClass
{

	CRuntimeClass* m_pNextClass;       // 指向下一个注册类的链表指针
	const AFX_CLASSINIT* m_pClassInit;//通过这个类的构造函数把自己注册添加进链表,m_pNextClass指向下一个注册类
};

简单模拟MFC序列化例子

//1. CObject类中添加一个虚函数,用于存储和读取数据
//   子类重写虚函数可以实现自身数据的存储和读取
virtual void Serialize(FILE* file, BOOL bStore);

//2. 保存一个全局对象的CRuntimeClass指针链表的头节点
//将所有类的信息保存起来,用于反序列化的时候根据类名new对应对象
CRuntimeClass* g_pRuntimeClassLstHead = NULL;

//全局函数将类信息添加到链表中
void AfxClassInit(CRuntimeClass* pNewRuntimeClass)
{
    pNewRuntimeClass->m_pNextClass = g_pRuntimeClassLstHead;
    g_pRuntimeClassLstHead = pNewRuntimeClass;
}

//每个类中都会定义一个此结构体类型的静态成员,通过构造函数添加类信息
struct AFX_CLASSINIT
{
  AFX_CLASSINIT(CRuntimeClass* pNewRuntimeClass)
  {
    AfxClassInit(pNewRuntimeClass);
  }
};

//3. 目标类中定义一个静态成员,在其类的构造中将类信息添加到全局链表中
class CObject  
{
public:
	CObject();
	virtual ~CObject();
  virtual void Serialize(FILE* file, BOOL bStore);

	static CRuntimeClass classCObject; 
	static AFX_CLASSINIT _init_CObject; //通过此成员的构造函数,将类信息添加到全局链表中
	virtual CRuntimeClass* GetRuntimeClass() const; 
	static CObject* CreateObject();
};

CRuntimeClass CObject::classCObject = { 
		  "CObject", 
      sizeof(class CObject), 
      0xFFFF, 
      NULL, 
      NULL
      }; 

//静态成员实现,目的:添加此类的类信息的全局链表中
AFX_CLASSINIT CObject::_init_CObject((CRuntimeClass*)&CObject::classCObject);


//3. 封装一个CArchive类用于操作文件,存读数据
class CArchive : public CObject  
{
public:
	CArchive(FILE* file)
	{
	    m_file = file;
	}
	virtual ~CArchive();

    void WriteObject(FILE* file, CObject* pObj)
    {
        //写入对象的类型信息
        CRuntimeClass* pRC = pObj->GetRuntimeClass();
        int nLen = strlen(pRC->m_lpszClassName);
        fwrite(&nLen, sizeof(int), 1, file);
        fwrite(pRC->m_lpszClassName, nLen, 1, file);
        
        //写入对象的数据
        pObj->Serialize(file, TRUE);
    }
    void ReadObject(FILE* file, CObject*& pObj)
    {
        //1. 读取类名
        int nLen = 0;
        fread(&nLen, sizeof(int), 1, file);
        char* szName = new char[nLen + 1];
        fread(szName, nLen, 1, file);
        szName[nLen] = 0;
        
        //2. 到RTTI链表中查找对应的RuntimeClass
        CRuntimeClass* pRC = g_pRuntimeClassLstHead;
        CRuntimeClass* pCurrentRC = NULL;
        while (pRC != NULL)
        {
            if (strcmp(pRC->m_lpszClassName, szName) == 0)
            {
                pCurrentRC = pRC;
                break;
            }
            pRC = pRC->m_pNextClass;
        }
        if (pCurrentRC == NULL)
        {
            return;
        }
        
        //3. 动态创建对象
        pObj = pCurrentRC->CreateObject();
        if (pObj == NULL)
        {
            return;
        }
        
        //4. 读取对象的数据
        pObj->Serialize(file, FALSE);
    }

public:
    FILE* m_file;
};

//为了让使用更加方便,会在子类中通过友元函数重载运算符,使文件存读通过流操作即可完成
class CData : public CObject
{
    DECLARE_SERIAL(CData)
public:
    CData();
    virtual ~CData();
    virtual void Serialize(FILE* file, BOOL bStore)
    {
        CObject::Serialize(file, bStore);//先调用父类的虚函数,存读父类的数据

        if (bStore)
        {
            fwrite(&m_nData, sizeof(int), 1, file);
        }
        else
        {
            fread(&m_nData, sizeof(int), 1, file);
        }
    }

    friend CArchive&  operator<<(CArchive& arc, CData* pObj) //数据写入
    {
        arc.WriteObject(arc.m_file, pObj);
        return arc;
    }

    friend CArchive&  operator>>(CArchive& arc, CObject*& pObj)//数据读取
    {
        arc.ReadObject(arc.m_file, pObj);
        return arc;
    }

private:
    int m_nData;
};


int main(int argc, char* argv[])
{
  CData data1;
  FILE* file = fopen("b.bin", "w+");
  CArchive arc(file);
  arc << &data1;
  fclose(file);

  FILE* file1 = fopen("b.bin", "rb+");
  CObject* pObj = NULL;
  CArchive arc2(file1);
  arc2 >> pObj;
  fclose(file1);
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值