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读写:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
每个类在每个类的Serialize函数里面对类的成员变量用ar >> / << 成员进行成员读写。当类成员是MFC类型或基本类型时,一般可以直接ar >>/<<后续的处理已经被设计好。
这里涉及在Serialize里面什么成员用ar >>/>>,什么成员用 chengyuan.Serialize(ar)问题【一般对简单成员用前面方式,对复合成员【成员可分拆成多个,或自定义类成员等】用后面方式】
DECLARE_SERIAL/IMPLEMENT_SERIAL
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;\ }\
为了在每一个对象被处理【读或写】之前,能够处理琐屑的工作。如判断是否第一次出现,记录版本号码,记录文件名等操作,CRuntimeClass需要Load和Store。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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] = '\0'; 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));// 类名及其长度 }
为了让整个Serialize机制运行起来,在各个需要进行ar >> / << 成员,的成员对应的类中要DECLARE_SERIAL(class_name)/IMPLEMENT_SERIAL(class_name, base_class_name, nSchema)和void Serialize(CArchive& ar);【Serialize是CObject的一个虚函数】
【DECLARE_SERIAL相比于DECLARE_DYNCREATE就是多了一个重载的>>运算符定义】。
————————————————
常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。
昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。