串行化Serialization.docx

上传人:b****6 文档编号:3479100 上传时间:2022-11-23 格式:DOCX 页数:15 大小:24.40KB
下载 相关 举报
串行化Serialization.docx_第1页
第1页 / 共15页
串行化Serialization.docx_第2页
第2页 / 共15页
串行化Serialization.docx_第3页
第3页 / 共15页
串行化Serialization.docx_第4页
第4页 / 共15页
串行化Serialization.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

串行化Serialization.docx

《串行化Serialization.docx》由会员分享,可在线阅读,更多相关《串行化Serialization.docx(15页珍藏版)》请在冰豆网上搜索。

串行化Serialization.docx

串行化Serialization

串行化(Serialization)

作者:

阿荣

下载例子源代码

串行化是微软提供的用于对对象进行文件I/O的一种机制,该机制在框架(Frame)/文档(Document)/视图(View)模式中得到了很好的应用。

很多人对什么是串行化、怎么使对象具有串行化能力和如何使用串行化功能等问题都不甚明了。

本文试图对串行化做一个简单的解释。

由于本人对串行化功能使用的也不多,不足之处敬请谅解。

MFC框架/文档/视图结构中的文件读写

CFile是MFC类库中所有文件类的基类。

所有MFC提供的文件I/O功能都和这个类有关。

很多情况下,大家都喜欢直接调用CFile:

:

Write/WriteHuge来写文件,调用CFile:

:

Read/ReadHuge来读文件。

这样的文件I/O其实和不使用MFC的文件I/O没有什么区别,甚至和以前的ANSIC的文件I/O也没有多少差别,所差别的不外乎是调用的API不同而已。

在开始学习C++的时候,大家一定对cin/cout非常熟悉,这两个对象使用非常明了的<<和>>运算符进行I/O,其使用格式为:

//示例代码1

inti;

cin>>i;

//heredosomethingtoobjecti

cout<

使用这种方式进行I/O的好处时,利用运算符重载功能,可以用一个语句完成对一系列的对象的读写,而不需要区分对象具体的类型。

MFC提供了类CArchive,实现了运算符<<和>>的重载,希望按照前面cin和cout的方式进行文件I/O。

通过和CFile类的配合,不仅仅实现了对简单类型如int/float等的文件读写,而且实现了对可序列化对象(SerializableObjects,这个概念后面描述)的文件读写。

一般情况下,使用CArchive对对象进行读操作的过程如下:

//示例代码2

//定义文件对象和文件异常对象

CFilefile;

CFileExceptionfe;

//以读方式打开文件

if(!

file.Open(filename,CFile:

:

modeRead,&fe))

{

fe.ReportError();

return;

}

//构建CArchive对象

CArchivear(&file,CArchive:

:

load);

ar>>obj1>>obj2>>obj3...>>objn;

ar.Flush();

//读完毕,关闭文件流

ar.Close();

file.Close();

使用CArchive对对象进行写操作的过程如下:

//示例代码3

//定义文件对象和文件异常对象

CFilefile;

CFileExceptionfe;

//以读方式打开文件

if(!

file.Open(filename,CFile:

:

modeWrite|CFile:

:

modeCreate,&fe))

{

fe.ReportError();

return;

}

//构建CArchive对象

CArchivear(&file,CArchive:

:

load);

ar<

ar.Flush();

//写完毕,关闭文件流

ar.Close();

file.Close();

可见,对于一个文件而言,如果文件内对象的排列顺序是固定的,那么对于文件读和写从形式上只有使用的运算符的不同。

在MFC的框架/文档/视图结构中,一个文档的内部对象的构成往往是固定的,这种情况下,写到文件中时对象在文件中的布局也是固定的。

因此CDocument利用其基类CObject提供的Serilize虚函数,实现自动文档的读写。

当用户在界面上选择文件菜单/打开文件(ID_FILE_OPEN)时,CWinApp派生类的OnFileOpen函数被自动调用,它通过文档模板创建(MDI)/重用(SDI)框架、文档和视图对象,并最终调用CDocument:

:

OnOpenDocument来读文件,CDocument:

:

OnOpenDocument的处理流程如下:

//示例代码4

BOOLCDocument:

:

OnOpenDocument(LPCTSTRlpszPathName)

{

if(IsModified())

TRACE0("Warning:

OnOpenDocumentreplacesanunsaveddocument.\n");

CFileExceptionfe;

CFile*pFile=GetFile(lpszPathName,

CFile:

:

modeRead|CFile:

:

shareDenyWrite,&fe);

if(pFile==NULL)

{

ReportSaveLoadException(lpszPathName,&fe,

FALSE,AFX_IDP_FAILED_TO_OPEN_DOC);

returnFALSE;

}

DeleteContents();

SetModifiedFlag();//dirtyduringde-serialize

CArchiveloadArchive(pFile,CArchive:

:

load|CArchive:

:

bNoFlushOnDelete);

loadArchive.m_pDocument=this;

loadArchive.m_bForceFlat=FALSE;

TRY

{

CWaitCursorwait;

if(pFile->GetLength()!

=0)

Serialize(loadArchive);//loadme

loadArchive.Close();

ReleaseFile(pFile,FALSE);

}

CATCH_ALL(e)

{

ReleaseFile(pFile,TRUE);

DeleteContents();//removefailedcontents

TRY

{

ReportSaveLoadException(lpszPathName,e,

FALSE,AFX_IDP_FAILED_TO_OPEN_DOC);

}

END_TRY

DELETE_EXCEPTION(e);

returnFALSE;

}

END_CATCH_ALL

SetModifiedFlag(FALSE);//startoffwithunmodified

returnTRUE;

}

同样,当用户选择菜单文件/文件保存(ID_FILE_SAVE)或者文件/另存为...(ID_FILE_SAVEAS)时,通过CWinApp:

:

OnFileSave和CWinApp:

:

OnFileSaveAs最终调用CDocument:

:

OnSaveDocument,这个函数处理如下:

//示例代码5

BOOLCDocument:

:

OnSaveDocument(LPCTSTRlpszPathName)

{

CFileExceptionfe;

CFile*pFile=NULL;

pFile=GetFile(lpszPathName,CFile:

:

modeCreate|

CFile:

:

modeReadWrite|CFile:

:

shareExclusive,&fe);

if(pFile==NULL)

{

ReportSaveLoadException(lpszPathName,&fe,

TRUE,AFX_IDP_INVALID_FILENAME);

returnFALSE;

}

CArchivesaveArchive(pFile,CArchive:

:

store|CArchive:

:

bNoFlushOnDelete);

saveArchive.m_pDocument=this;

saveArchive.m_bForceFlat=FALSE;

TRY

{

CWaitCursorwait;

Serialize(saveArchive);//saveme

saveArchive.Close();

ReleaseFile(pFile,FALSE);

}

CATCH_ALL(e)

{

ReleaseFile(pFile,TRUE);

TRY

{

ReportSaveLoadException(lpszPathName,e,

TRUE,AFX_IDP_FAILED_TO_SAVE_DOC);

}

END_TRY

DELETE_EXCEPTION(e);

returnFALSE;

}

END_CATCH_ALL

SetModifiedFlag(FALSE);//backtounmodified

returnTRUE;//success

}

从前面两段代码可以看出,文件读和文件写的结构基本相同,并且最终都调用了CObject:

:

Serialize函数完成对文档自己的读和写(参见注释中的saveme和loadme)。

对于用AppWizard自动生成的MDI和SDI,系统自动生成了这个函数的重载实现,缺省的实现为:

//示例代码6

voidCMyDoc:

:

Serialize(CArchive&ar)

{

if(ar.IsStoring())

{

//TODO:

addstoringcodehere

}

else

{

//TODO:

addloadingcodehere

}

}

如果一个对VC非常熟悉的人,喜欢手工生成所有的代码(当然这是非常浪费时间也是没有必要的),那么他提供的CDocument派生类也应该实现这个缺省的Serialize函数,否则,系统在文件读写时只能调用CObject:

:

Serialize,这个函数什么都不做,当然也无法完成对特定对象的文件保存/载入工作。

当然,用户也可以截获ID_FILE_OPEN等菜单,实现自己的文件读写功能,但是这样的代码将变得非常烦琐,也不容易阅读。

回到CMyDoc:

:

Serialize函数。

这个函数通过对ar对象的判断,决定当前是在读还是在写文件。

由于AppWizard不知道你的文档是干什么的,所以它不会给你添加实际的文件读写代码。

假设你的文档中有三个对象m_Obj_a,m_Obj_b,m_Obj_c,那么实际的代码应该为:

//示例代码7

voidCMyDoc:

:

Serialize(CArchive&ar)

{

if(ar.IsStoring())

{

ar<

}

else

{

ar>>m_Obj_a>>m_Obj_b>>m_Obj_c;

}

}

可串行化对象(SerializableObject)

要利用示例代码7中的方式进行文件I/O的一个基本条件是:

m_Obj_a等对象必须是可串行化的对象。

一个可串行化对象的条件为:

∙这个类从CObject派生)

∙该类实现了Serialize函数

∙该类在定义时使用了DECLARE_SERIAL宏

∙在类的实现文件中使用了IMPLEMENT_SERIAL宏

∙这个类有一个不带参数的构造函数,或者某一个带参数的构造函数所有的参数都提供了缺省参数

这里,可串行化对象条件中没有包括简单类型,对于简单类型,CArchive基本都实现了运算符<<和>>的重载,所以可以直接使用串行化方式进行读写。

从CObject类派生

串行化要求对象从CObject派生,或者从一个CObject的派生类派生。

这个要求比较简单,因为几乎所有的类(不包括CString)都是从CObject派生的,因此对于从MFC类继承的类都满足这个要求。

对于自己的数据类,可以指定它的基类为CObject来满足这个要求。

实现Serialize函数

Serialize函数是对象真正保存数据的函数,是整个串行化的核心。

其实现方法和CMyDoc:

:

Serialize一样,利用CArchive:

:

IsStoring和CArchive:

:

IsLoading判断当前的操作,并选择<<和>>来保存和读取对象。

使用DECLARE_SERIAL宏

DECLARE_SERIAL宏包括了DECLARE_DYNAMIC和DECLARE_DYNCREATE功能,它定义了一个类的CRuntimeClass相关信息,并实现了缺省的operator>>重载。

实现了该宏以后,CArchive就可以利用ReadObject和WriteObject来进行对象I/O,并能够在事先不知道类型的情况下从文件中读对象。

使用IMPLEMENT_SERIAL

DECLARE_SERIAL宏和IMPLEMENT_SERIAL宏必须成对出现,否则DECLARE_SERIAL宏定义的实体将无法实现,最终导致连接错误。

缺省构造函数

这是CRuntimeClass:

:

CreateObject对对象的要求。

特殊情况

∙只通过Serialize函数对对象读写,而不使用ReadObject/WriteObject和运算符重载时,前面的可串行化条件不需要,只要实现Serialize函数即可。

∙对于现存的类,如果它没有提供串行化功能,可以通过使用重载友元operator<<和operator>>来实现。

例子

假设需要实现一个几何图形显示、编辑程序,支持可扩展的图形功能。

这里不想讨论具体图形系统的实现,只讨论图像对象的保存和载入。

基类CPicture

每个图形对象都从CPicture派生,这个类实现了串行化功能,其实现代码为:

//头文件picture.h

#if!

defined(__PICTURE_H__)

#define__PICTURE_H__

#if_MSC_VER>1000

#pragmaonce

#endif//_MSC_VER>1000

constintTYPE_UNKNOWN=-1;

classCPicture:

publicCObject

{

intm_nType;//图形类别

DECLARE_SERIAL(CPicture)

public:

CPicture(intm_nType=TYPE_UNKNOWN):

m_nType(m_nType){};

intGetType()const{returnm_nType;};

virtualvoidDraw(CDC*pDC);

voidSerialize(CArchive&ar);

};

#endif

//cpp文件picture.cpp

#include"stdafx.h"

#include"picture.h"

#ifdef_DEBUG

#definenewDEBUG_NEW

#undefTHIS_FILE

staticcharTHIS_FILE[]=__FILE__;

#endif

voidCPicture:

:

Draw(CDC*pDC)

{

//基类不实现绘图功能,由派生类实现

}

voidCPicture:

:

Serialize(CArchive&ar)

{

if(ar.IsLoading())

{

ar<

}else{

ar>>m_nType;

}

}

注意:

由于CRuntimeClass要求这个对象必须能够被实例化,因此虽然Draw函数没有任何绘图操作,这个类还是没有把它定义成纯虚函数。

对象在CDocument派生类中的保存和文件I/O过程

为了简化设计,在CDocument类派生类中,采用MFC提供的模板类CPtrList来保存对象。

该对象定义为:

protected:

CTypedPtrListm_listPictures;

由于CTypedPtrList和CPtrList都没有实现Serialize函数,因此不能够通过ar<>m_listPictures来序列化对象,因此CPictureDoc的Serialize函数需要如下实现:

voidCTsDoc:

:

Serialize(CArchive&ar)

{

POSITIONpos;

if(ar.IsStoring())

{

//TODO:

addstoringcodehere

pos=m_listPictures.GetHeadPosition();

while(pos!

=NULL)

{

ar<

}

}

else

{

//TODO:

addloadingcodehere

RemoveAll();

CPicture*pPicture;

do{

try

{

ar>>pPicture;

TRACE("ReadObject%d\n",pPicture->GetType());

m_listPictures.AddTail(pPicture);

}

catch(CException*e)

{

e->Delete();

break;

}

}while(pPicture!

=NULL);

}

m_pCurrent=NULL;

SetModifiedFlag(FALSE);

}

实现派生类的串行化功能

几何图形程序支持直线、矩形、三角形、椭圆等图形,分别以类CLine、CRectangle、CTriangle和CEllipse实现。

以类CLine为例,实现串行化功能:

1.从CPicture派生CLine,在CLine类定义中增加如下成员变量:

2.CPointm_ptStart,m_ptEnd;

3.在该行下一行增加如下宏:

4.DECLARE_SERIAL(CLine)

5.实现Serialize函数

6.voidCLine:

:

Serialize(CArchive&ar)

7.{

8.CPicture:

:

Serialize(ar);

9.if(ar.IsLoading())

10.{

11.ar>>m_ptStart.x>>m_ptStart.y>>m_ptEnd.x>>m_ptEnd.y;

12.}else{

13.ar<

14.}

15.}

16.在CPP文件中增加

17.IMPLEMENT_SERIAL(CLine,CPicture,TYPE_LINE);

这样定义的CLine就具有串行化功能,其他图形类可以类似定义。

附注

本文仓促草就,不足之处在所难免。

请发现谬误者给我来信说明,谢谢。

最新评论[发表评论][文章投稿]

查看所有评论

推荐给好友

打印

罗恩大哥,你好,我在实际变成中想自动载入上次的文件有什么好的方法吗?

(yangqifengfan发表于2005-3-258:

24:

00)

 

写的不错。

(徐明刚发表于2004-10-1212:

27:

00)

 

罗恩大哥,你好,我是vc++初学者,(以前看过windows程序设计(第5版),

机工社的《c++精髓 软件工程方法》),

最近在看《深入浅出MFC》,第8章时,里边也说到了Serialize,我不是很懂,

我看你的个人专栏里边看了串行化(Serialization)这篇文章也提到了,

你能顺便给我解释一下吗Serialize

在你的示例代码7中有 ar >> m_Obj_a 

这里operator >>是不是调用

_AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)  ?

如果是,那么IMPLEMENT_SERIAL宏 展开后的

CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) 

在哪里用到?

还有为什么IMPLEMENT_SERIAL宏 展开后的

有CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) 

没有CArchive& AFXAPI operator<<(CArchive& ar, class_name* &pOb) 

在示例7中出现ar << m_Obj_a(yckyck2001发表于2004-7-1114:

03:

00)

 

我就把你当成我现在的目标了(弃卒发表于2004-6-1415:

25:

00)

 

将内存指针系列化可不是个好主意(m_listPictures这一段)(jiangsanhuo发表于2004-2-49:

04:

00)

 

最明显的错误是:

    ar >> pPicture;之前没有为pPicture分配内存:

  pPicture = new CPicture;(eeixy2000发表于2003-12-2719:

12:

00)

 

唉,希望这个文档不要被大家看成是教人家怎么绘图的:

((阿荣发表于2003-11-2013:

51:

00)

 

建议看看vckbase上面得EastDraw程序,该文章中说得所有内容都可以从那个例子中找到,并且那个例子中还有undo功能得实现。

(coyer发表于2003-11-617:

14:

00)

 

不错,建议以后多发表类似的文章!

(xiaojin发表于2003-11-423:

06:

00)

 

如果发生版本升级,那么新版本的程序能不能兼容旧版本本来就需要看代码怎么设计,这种情况下,不使用串行化一样不能达到目的。

有两个方法可以解决这个问题:

1)根据扩展名不同实现不同的Serialize功能,可以在S

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 销售营销

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1