MFC深入浅出--永久保存

来源:互联网 发布:C语言中的循环语句包括 编辑:程序博客网 时间:2024/04/27 07:26
 
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 )
 
 
原创粉丝点击