您的位置:首页 > 其它

MFC深入浅出--永久保存

2007-04-22 09:18 357 查看
Persistence(永久保存)机制
面向对象有一个术语:Persistence,意思是不对象永久保留下来。对象如何能够永久保存?当然是写到文件中去。
将数据写到文件中是身简单的。Document/View结构中,数据都放在一份document中,只要把其中的成员变量依次写进文件即可/成员变量很可能是一个对象,而面向对象,首先应该记载你类名称,然后才是对象的数据。
当程序从文件中读到一个类名称时,它如何实现(instantiate)一个对象。这就是动态创建的技术。
MFC有一套Serialize机制,目的是把文件名的选择,文件的开关,缓冲去的建立、数据的读写、提取运算符(>>)和插入运算符(<<)的重载、对象的动态创建等都包装起来。
Serialize的各部分工作,除了数据的读写和动态创建外,其余都是枝节。

Serialize(数据读写)
假设现有一份文件,用以记录一张图形。图形只有 中基本元素:线条(Stroke)、圆形、矩形。准备用下面的类组织这份文件:
CObject CRect
CobList CPoint
CDWordArray CSize
Cstroke
Crectangle
Ccircle
Cdocument
CmyDoc
其中CObList和CDWordArray是MFC提供的类,前者是一个链表,可放置任何从CObject派生下来的对象,后者是一个数组,每个元素都是double word.另外三个类:Cstroke和Crectangle和Ccircle是我们从CObject派生出来的。
Class CMyDoc : public Cdocument
{
CObList m_graphList;
Csize m_sizeDoc;

};
Class CStroke : public CObject
{
CDWordArray m_ptrArray; //series of connected points

};
Class CRectangle : public CObject
{
CRect m_rect;

};
Class Cstroke : public CObject
{
Cpoint m_center;
UINT m_radius;

};
每次记录对象内容室,先写一段代码,表示此对象是否在文件中已经记录过,若是新类记录其类名称;若为旧类,则以代码表示。这样可以节省文件大小和程序用于解析的时间。
最好每个类都有自己的版本号码,把版本号码记录上去,可以防止旧版和新版程序的互相读取。
希望有一个专门负责Seralization的函数,叫Serialize若现在的Document类名称为CScribDoc,期望有便利的程序方法:
class CScribDoc:: Serialize (CArchive & ar)
{
if(ar.IsSorting( ))
ar<<m_sizeDoc;
else
ar>>m_sizeDoc;
m_graphList.Serialize(ar);
}
class CObList:: Serialize (CArchive & ar)
{
if(ar.IsSorting( ))
{
ar<<(WORD)m_nCount;
for(Cnode *pNode= m_pNodeHead;pNode!=NULL;pNode= pNode->next)
ar<<pNode->data;
}
else
{
WORD nNewCount;
Ar>>nNewCount;
While(nNewCount){
CObject *new Data;
Ar>>newData;
AddTail(newData);
}
}
];
void Cstroke :: Serialize(CArchive &ar)
{
m_pterArray.Serialize(ar);
}
void CDWordArray:: Serialize(CArchive &ar)
{
if(ar.IsSorting())
{
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_nSize;I++)
ar>>m_pData[I];
}
}
void Crectangle::Serialize(CArchive &ar)
{
if(ar.IsSorting( ))
ar>>m_rect;
else
ar>>m_rect;
}
void CCircle::Serialize(CArchive &ar)
{
if(ar.IsSorting( ))
{
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函数,负责其自己的数据读写文件操作。此类并且应该改写<<和>>运算符,把数据导流到archive中。Archive是一个与文件息息相关的缓冲区,暂时可以把它想象成文件的化身。

DECLARE_SERIAL/IMPLEMENT_SERIAL宏
>><<两个运算符重载,还要让Serialize函数不知觉地放入类声明中,最好的做法仍然是使用宏。
类之所以能够进行文件读写操作,前提是拥有动态创建的能力,所以,MFC设计了两个宏DECLARE_SERIALIMPLEMENT_SERIAL:
#define DECLARE_SERIAL(class_name) /
DECLARE_DYNCREATE(class_name) /
Friend Crichive & AFXAPI operator>>(Carichive & ar,class_name * &pObj);
#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 & APXAPI operator >> (CArchive &ar,class_name * &pOb) /
{
pOb=(class_name *)ar.ReadObject(RUNTIME_CLASS(class_name));/
return ar;
}/
为了在每一个对象被处理(读或写)之前,能够处理琐屑的工作,诸如判断是否是第一次出现,记录版本号码,记录文件名等工作,CRuntimeClass需要两个函数LoadStore:
Struct CRuntimeClass
{
//Attributes
LRCSTR m_lpnzClassName;
Int m_nObjectSize;
UINT m_wSchema; //schema number of the loaded class
CObject *(PASCAL * m_pfnCreateObject)();//NULL=>abstract class
CRuntimeClass *m_pBaseClass;
CObject * CreateObject();
Void Store(CArchive & ar) const
Static Cruntime * PASCAL Load(CArchive &ar,UINT * pwsSchemaNum);
//CruntimeClass objects linked together in the simple list
static CRuntimeClass *pFirstClass;
CRuntimeClass * m_pNextClass;
}
Load函数是从文件中读一个类名称,Store函数是把类名称写入文件中:
CRuntimeClass * PASACL 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=pFirstClassName;pClass!=NULL;pClass=pClass->m_pNextClass)
{
if(Istrcmp(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));
}
为了让整个Serialization机制运行起来,必须做这样的类声明:
class CScribDoc : public CDocument
{
DECLARE_DYNCREATE(CScribDoc )

};
class CStroke : public CObject
{
DECLARE_SERIAL(CStroke)
Public:
Void serialize(Carichive &);

};
class CRectangle : public CObject
{
DECLARE_SERIAL(CRectangle)
Public:
Void Serialize(CArchive &);

};
class CCirecle : public CObject
{
DECLARE_SERIAL(CCirecle)
Public:
Void Serialize(CArchive &);

};
在执行文件(.cpp)中进行如下的操作:
IMPLEMENT_DYNCREATE( CScribDoc,CDocument )

IMPLEMENT_SERIAL( CStroke,CObject ,2 )
IMPLEMENT_ SERIAL ( CRectangle, CObject,1 )
IMPLEMENT_ SERIAL ( CCircle, CObject ,1 )
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: