• MFC中对象序列化技术的实现


    1、需求
    对于支持序列化操作的类
    可以将不同类的不同对象以序列的形式写到文件中;
    可以通过读取序列化文件还原对应类的对应实例;
    针对对象的哪些内容进行序列化由对象来决定;
    2、需求示例
    2.1、需要序列化的对象对应的类
    l   CName

    class CName:public CObject 

    {

    public:       

        DECLARE_SERIAL(CName)

        CName()                    

    {m_d1=0;m_d2=0;};

        CName(double d1,double d2)       

    {m_d1=d1;m_d2=d2;};

           void Serialize(CArchive& ar)

           {

                  CObject::Serialize(ar);

                  if (ar.IsStoring()){ar<<m_d1;       ar<<m_d2;}

                  else {ar>>m_d1;ar>>m_d2;}

           };

        virtual ~CName(){};     

    public:

        double m_d1;

        double m_d2;

    };

    IMPLEMENT_SERIAL(CName,CObject,0)

     

    l         CChildName

    class CChildName:public CName

    {

    public:

    DECLARE_SERIAL(CChildName)

     CChildName()                               

    {m_i=0; m_c=0;m_d1=0;m_d2=0;};

        CChildName(int i,char c,double d1=1.1,double d2=2.2)

    {m_i=i;m_c=c;m_d1=d1;m_d2=d2;};

    void Serialize(CArchive& ar)

           {

                  CObject::Serialize(ar);

                  CName::Serialize(ar);

                  if (ar.IsStoring()){ar<<m_i;ar<<m_c;}

                  else{ar>>m_i; ar>>m_c;       }

           }

        virtual ~CChildName(){};     

    public:

        int m_i;

        char m_c;

    };

    IMPLEMENT_SERIAL(CChildName,CName,0)

     

    2.2、辅助类

    l         CObject

    所有MFC类的基类

    l         CObList

    存放CObject对象指针的链表

    l         CFile

    进行文件读写操作的类

    l         CArchive

    MFC封装对象序列化功能的核心类

    2.3、测试程序

    l         main函数

    void main()

        testSerialStore();

        testSerialLoad();    

    }

     

    l         testSerialStore函数

    void testSerialStore()

    {

           CObList cObList;

           CName cName1(1.1,2.2),cName2(2.2,4.4);

           CChildName cChildName1(1,'a',1.11,2.22),cChildName2(2,'b',2.22,4.44);

           cObList.AddHead(&cName1);

           cObList.AddHead(&cChildName1);

           cObList.AddHead(&cName2);

           cObList.AddHead(&cChildName2);

           printf("######Store"n");

           printf("     CName[%3.1f,%3.1f]"n",cName1.m_d1,cName1.m_d2);

           printf("CChildName[%3.1f,%3.1f,%d,%c]"n",

                  cChildName1.m_d1,cChildName1.m_d2,cChildName1.m_i,cChildName1.m_c);

           printf("     CName[%3.1f,%3.1f]"n",cName2.m_d1,cName2.m_d2);

           printf("CChildName[%3.1f,%3.1f,%d,%c]"n",

                  cChildName2.m_d1,cChildName2.m_d2,cChildName2.m_i,cChildName2.m_c);

           CFile m_fileIn("ser.bin", CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive);

           CArchive arStore(&m_fileIn,CArchive::store);

           cObList.Serialize(arStore);

           arStore.Close();

           m_fileIn.Close();   

    }

     

    l         testSerialLoad函数

    void testSerialLoad()

    {

           CObList cObList;  

           CFile m_fileOut("ser.bin", CFile::modeRead| CFile::shareExclusive);

           CArchive arLoad(&m_fileOut,CArchive::load);

           cObList.Serialize(arLoad);

           printf("######Load"n");

           CObject *pObject;        

           POSITION pos = cObList.GetTailPosition();      

           while (pos != NULL)

           {           

                  pObject=cObList.GetPrev(pos);

                  if(pObject->IsKindOf(RUNTIME_CLASS(CChildName)))

                         printf("CChildName[%3.1f,%3.1f,%d,%c]"n",

                                ((CChildName*)pObject)->m_d1,((CChildName*)pObject)->m_d2,

                                ((CChildName*)pObject)->m_i,((CChildName*)pObject)->m_c);

                  else if(pObject->IsKindOf(RUNTIME_CLASS(CName)))

                         printf("     CName[%3.1f,%3.1f]"n",

                                ((CName*)pObject)->m_d1,((CName*)pObject)->m_d2);

                  delete pObject;

           }

           printf(""n");

           arLoad.Close();

           m_fileOut.Close();

    }

     

    l         运行结果

    ######Store

         CName[1.1,2.2]

    CChildName[1.1,2.2,1,a]

         CName[2.2,4.4]

    CChildName[2.2,4.4,2,b]

    ######Load

         CName[1.1,2.2]

    CChildName[1.1,2.2,1,a]

         CName[2.2,4.4]

             CChildName[2.2,4.4,2,b]

     

    3、实现原理

     

    3.1、实现一个双向链表容器-CObList

     

    l         保存序列化对象的对象指针

    l         遍历链表中的每个对象,调用每个对象自己的序列化方法

    void CObList::Serialize(CArchive& ar)

    {

        if (ar.IsStoring()) { //写序列化信息

               ar.WriteCount(m_nCount);//写入序列化对象的数目

               for (CNode* pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext)

               {

                      ar << pNode->data;//写入每个对象的序列化信息

               }

        }else{  //读序列化信息

               DWORD nNewCount = ar.ReadCount();//读出序列化对象的数目

               CObject* newData;

               while (nNewCount--)

               {

                      ar >> newData;//读出每个对象的序列化信息、动态创建对象

                      AddTail(newData);//将创建的对象的对象指针添加到容器中

               }

        }

    }

     

    3.2、实现对象序列化的核心功能-CArchive

     

    l         包含一块动态内存空间,用来缓存序列化相关的信息

    int m_nBufSize;//动态内存的大小

    BYTE* m_lpBufCur;//动态内存的当前读写位置

    BYTE* m_lpBufMax;//动态内存的结束位置

    BYTE* m_lpBufStart;//动态内存的起始位置

    l         包含一个文件对象指针,可以将序列化相关信息永久保存在文件中;

    l         在动态内存和文件之间实现序列化相关信息的同步

    void CArchive::Flush()//内存->文件

    {

           if (IsLoading()){//读序列化信息

                  if (m_lpBufMax != m_lpBufCur)//文件内部指针偏移

                         m_pFile->Seek(-(m_lpBufMax - m_lpBufCur), CFile::current);

                  m_lpBufCur = m_lpBufMax;   

           }

           else {//写序列化信息

                  if (m_lpBufCur != m_lpBufStart)

                         m_pFile->Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);

                  m_lpBufCur = m_lpBufStart;

           }

    }

    void CArchive::FillBuffer(UINT nBytesNeeded) //文件-> 内存

    {

           UINT nUnused = m_lpBufMax - m_lpBufCur;

           ULONG nTotalNeeded = ((ULONG)nBytesNeeded) + nUnused;

           if (m_lpBufCur > m_lpBufStart){

                  if ((int)nUnused > 0){

                         memmove(m_lpBufStart, m_lpBufCur, nUnused);

                         m_lpBufCur = m_lpBufStart;

                         m_lpBufMax = m_lpBufStart + nUnused;

                  }

                  UINT nRead = nUnused;UINT nLeft = m_nBufSize-nUnused;

                  UINT nBytes; BYTE* lpTemp = m_lpBufStart + nUnused;

                  do{

                         nBytes = m_pFile->Read(lpTemp, nLeft);

                         lpTemp = lpTemp + nBytes;nRead += nBytes;nLeft -= nBytes;

                  }while (nBytes > 0 && nLeft > 0 && nRead < nBytesNeeded);

                  m_lpBufCur = m_lpBufStart; m_lpBufMax = m_lpBufStart + nRead;

           }

    }

    l         /从动态内存/文件中写入、读出各种类型变量的值

    CArchive& operator<<(WORD w)//WORD变量的值写到内存/文件中

           {

                  if (m_lpBufCur + sizeof(WORD) > m_lpBufMax)

                         Flush();//内存->文件

                  *((WORD*)m_lpBufCur) = w;

                  m_lpBufCur += sizeof(WORD);

                  return *this;

           };                 

           CArchive& operator>>(WORD& w)//从内存/文件中读出一个WORD变量的值

           {

                  if (m_lpBufCur + sizeof(WORD) > m_lpBufMax)

                         FillBuffer(sizeof(WORD) - (UINT)(m_lpBufMax - m_lpBufCur)); //文件-> 内存

                  w = *((WORD*)m_lpBufCur);

                  m_lpBufCur += sizeof(WORD);

                  return *this;

           };

    l         /从动态内存/文件中写入、读出各种类型对象的值(公开接口-友元方法)

    friend CArchive& operator<<(CArchive& ar, const CObject* pOb)

           {

                  ar.WriteObject(pOb);

                  return ar;

           };

           friend CArchive& operator>>(CArchive& ar, CObject*& pOb)

           {

                  pOb = ar.ReadObject(NULL);

                  return ar;

           };

    //同一类型的对象,其class信息只序列化一次

    //同一个对象,即使多次调用也只序列化一次

    void CArchive::WriteObject(const CObject* pOb)

    {    

           DWORD nObIndex;     

           MapObject(NULL); // m_pStoreMap[CObject指针,index指针]

           if (pOb == NULL){ //1-写入wNullTag       

                  *this << wNullTag;

           }

           //通过CObject指针在Map中查找到了对应的index(同一个对象多次调用)

           else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0)

           {

                  if (nObIndex < wBigObjectTag) //2-写入object对应的index信息

                         *this << (WORD)nObIndex;

                  else  {

                         *this << wBigObjectTag;*this << nObIndex;}

           }

           //通过CObject指针在Map中没有查找到对应的index

           else  {

                  //3-写入object对应的class信息

                  CRuntimeClass* pClassRef = pOb->GetRuntimeClass();

                  WriteClass(pClassRef); 

                  (*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;

                  //4-写入object本身的序列化信息

                  ((CObject*)pOb)->Serialize(*this);

           }

    }

    void CArchive::WriteClass(const CRuntimeClass* pClassRef)

    {

           MapObject(NULL); // m_pStoreMap[CRuntimeClass指针,index指针]

           DWORD nClassIndex;

           //通过CRuntimeClass指针(类型信息)在Map中查找到了对应的index

           if ((nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]) != 0)

           {

                  //4.1-写入Class对应的index信息

                  if (nClassIndex < wBigObjectTag)

                         *this << (WORD)(wClassTag | nClassIndex);

                  else  {

                         *this << wBigObjectTag;*this << (dwBigClassTag | nClassIndex);

                  }

           }

           //通过CRuntimeClass指针(类型信息)在Map中没有查找到对应的index

           else

           {

                  //4.2-写入Class相关的信息,index/类型信息,wNewClassTag指示为第1次实例化

                  *this << wNewClassTag;

                  pClassRef->Store(*this);             

                  (*m_pStoreMap)[(void*)pClassRef] = (void*)m_nMapCount++;

           }

    }

     

    CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)

    {……}

     

    CRuntimeClass* CArchive::ReadClass(const CRuntimeClass* pClassRefRequested,

           UINT* pSchema, DWORD* pObTag)

    {……}

     

    3.3、底层支持功能

     

    l         RTTI的实现

    参见"MFCRTTI技术的实现原理"

    l         Dynamic Creation的实现

    参见"MFCDynamic Creation技术的实现原理"

     

     


  • 相关阅读:
    计总与排名SUM和RANK函数
    计算获取最小值和最大值
    列值中获取第一个非空的值
    连续数字使用连接符替换
    展开中断或忽略的序号
    以连接字符截取字符串
    逗号分割字符串经存储过程存入数据表中
    符号分割的字符串转换为XML
    MS SQL Server的STRING_SPLIT和STRING_AGG函数
    MS SQL Server的LTRIM,RTRIM和TRIM函数
  • 原文地址:https://www.cnblogs.com/BIGFOOT/p/1335273.html
Copyright © 2020-2023  润新知