zoukankan      html  css  js  c++  java
  • Persistence机制(永久保存/序列化Serialize)

    转载:https://blog.csdn.net/x13262608581/article/details/48937903

    MFC有一套Serialize机制,目的在于把文件名的选择,文件的开关,缓冲区的建立,数据的读写,提取运算符(>>),插入运算符(<<)的重载,对象的动态创建等都包装起来。
    数据读写:
    CObList:一个链表可放置从CObject派生下来的对象
    CDWordArray:一个数组,每个元素都是double word
    CStroke,CRectangle,CCircle:自定义派生自CObject的类

    写文件机制要考虑保存文件各个元素,屏幕滚动,打印输出,按元素出现的顺序记录。每个类对象记录个格式:
    类名长度,
    类名,
    类元素

    为简化记录:在每次记录对象内容时,先写入一个代码,表示此对象的类是否在文件中记录过了。如新类,乖乖记录其类名称,如是旧类,则以代码表示。【这样可节省文件大小和程序用于解析的时间】
    如何控制文件版本,旧版程序读新版文件,新版程序读旧版文件,最好把版本号码记录上去,最好每个类都有自己的版本号码。
    以要记录以下链表为例:
    CObList m_graphList:
    共六个元素:
    元素1:直线【CStroke】
    元素2:矩形【CRectangle】
    元素3:圆形【CCircle】
    元素4:直线【CStroke】
    元素5:矩形【CRectangle】
    元素6:圆形【CCircle】
    相关类:

    class CMyDoc : public CDocument
    {
        CObject m_graphList;
        CSize m_sizeDoc;
        ...
    };
    class CStroke : public CObject
    {
        CDWordArraym_ptArray;
        ...
    };
    class CRectangle : public CObject
    {
        CRect m_rect;
        ...
    };
    class CCircle : public CObject
    {
        CPoint  m_center;
        UINT m_radius;
        ...
    };

    记录的文件格式:
    文档大小
    元素个数

    新类标志【如类第一次出现】
    schema
    类名长度
    类名
    类元素内容

    老类标志【如类之前已出现】
    类元素内容

    理想的Document读写:

    void CScribDoc::Serialize(CArchive & ar)
    {
        if(ar.IsStoring())
            ar << m_sizeDoc;
        else
            ar >> m_sizeDoc;
    
        m_graphList.Serialize(ar);
    }
    void CObList::Serialize(CArchive &ar)
    {
        if(ar.IsStoring())
        {
            ar << (WORD)m_nCount;
            for(CNode* pNode  =  m_pNodeHead;  pNode != NULL; pNode = pNode->pNext)
            ar << pNode->data;
        }
        else
        {
            WORD nNewCount;
            ar >> nNewCount;
            while(nNewCount--)
            {
                CObject* newData;
                ar >> newData;
                AddTail(newData);
            }
        }
    }
    
    void CStroke::Serialize(CArchive & ar)
    {
        m_ptArray.Serialize(ar);
    }
    void CDWordArray::Serialize(CArchive& ar)
    {
        if(ar.IsStoring())
        {
            ar << (WORD)m_nSize;
            for(int i = 0; i < m_nSize; i++)
                ar << m_pData[i];
        } 
        else
        {
            WORD nOldSize;
            ar >> nOldSize;
            for(int i = 0; i < m_nOldSize; i++)
                ar >> m_pData[i];
        }
    }
    
    void CRectangle::Serialize(CArchive& ar)
    {
        if(ar.IsStoring())
            ar << m_rect;
        else
            ar >> m_rect;
    }
    void CCircle::Serialize(CArchive& ar)
    {
        if(ar.IsStoring())
        {
            ar << (WORD)m_center.x;
            ar << (WORD)m_center.y;
            ar << (WORD)m_radius;
        }
        else
        {
            ar >> (WORD&)m_center.x;
            ar >> (WORD&)m_center.y;
            ar >> (WORD&) m_radius;
        }
    }
    View Code

    每个类在每个类的Serialize函数里面对类的成员变量用ar >> / << 成员进行成员读写。当类成员是MFC类型或基本类型时,一般可以直接ar >>/<<后续的处理已经被设计好。
    这里涉及在Serialize里面什么成员用ar >>/>>,什么成员用 chengyuan.Serialize(ar)问题【一般对简单成员用前面方式,对复合成员【成员可分拆成多个,或自定义类成员等】用后面方式】

    DECLARE_SERIAL/IMPLEMENT_SERIAL

    #define DECLARE_SERIAL(class_name)
        DECLARE_DYNCREATE(class_name)
        friend CArchive& AFXAPI operator >> (CArchive& ar, class_name* &pOb);
    
    #define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)
        CObject* PASCAL class_name::CreateObject()
        {
            return new class_name;
        }
        _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, class_name::CreateObject)
        CArchive& AFXAPI operator >> (CArchive& ar, class_name* &pOb)
        {
            pOb = (class_name*)ar.ReadObject(RUNTIME_CLASS(class_name));
            return ar;
        }
    View Code

    为了在每一个对象被处理【读或写】之前,能够处理琐屑的工作。如判断是否第一次出现,记录版本号码,记录文件名等操作,CRuntimeClass需要Load和Store。

    struct CRuntimeClass
    { 
        LPCSTR m_lpszClassName;
        int m_nObjectSize;
        UINT m_wSchema;
        CObject* (PASCAL* m_pfnCreateObject) ();
        CRuntimeClass*m_pBaseClass;
        CObject* CreateObject();
        void Store(CArchive& ar) const;
        static CRuntimeClass*  PASCALLoad(CArchive& ar, UINT* pwSchemaNum);
    
        static CRuntimeClass*pFirstClass;
        CRuntimeClass*m_pNextClass;
    };
    
    CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar, UINT* pwSchemaNum)
    {
        WORD nLen;
        char szClassName[64];
        CRuntimeClass* pClass;
        ar >> (WORD&) (*pwSchemaNum) >> nLen;
    
        if(nLen >= sizeof(szClassName) || ar.Read(szClassName, nLen) != nLen)
            return NULL;
        szClassName[nLen] = '';
    
        for(pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
        {
            if(lstrcmp(szClassName, pClass->m_lpszClassName) == 0)
                return pClass;
        }
        return NULL;
    }
    
    void CRuntimeClass::Store(CArchive& ar) const
    {
        WORD nLen = (WORD)lstrlenA(m_lpszClassName);// 可视为类的版本
        ar << (WORD)m_wSchema << nLen;
        ar.Write(m_lpszClassName, nLen*sizeof(char));// 类名及其长度
    }
    View Code

    为了让整个Serialize机制运行起来,在各个需要进行ar >> / << 成员,的成员对应的类中要DECLARE_SERIAL(class_name)/IMPLEMENT_SERIAL(class_name, base_class_name, nSchema)和void Serialize(CArchive& ar);【Serialize是CObject的一个虚函数】
    【DECLARE_SERIAL相比于DECLARE_DYNCREATE就是多了一个重载的>>运算符定义】。


    ————————————————

    常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。

    昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。
  • 相关阅读:
    Linux centos7修改根目录
    gitlab的安装
    windows上svn图标不显示 绿色对号
    java中的Serializable接口
    List Map Set的线程安全
    javascript中的each遍历
    jdk 1.7新特性
    jdk 1.6 新特性
    jdk1.5 新特性
    java 运算符
  • 原文地址:https://www.cnblogs.com/htj10/p/13123957.html
Copyright © 2011-2022 走看看