王朝网络
分享
 
 
 

怎样给串行化类分配版本号(可配置版本模式)

王朝vc·作者佚名  2006-01-17
宽屏版  字体: |||超大  

怎样给串行化类分配版本号(可配置版本模式)

作者:阿豪(kof)

下载源代码

编写可串行化类时,MFC用你指定的模式号制定一个粗略的版本控制方式。在向档案写数据时,

MFC用模式标记该类的实例;而在读回数据时,MFC将档案中的记录的模式号和应用程序中使用着的

该类对象的模式号做比较,如果两模式号不匹配,则MFC发送一个CArchiveException,其m_cause

等于CArchiveException::badSchema。没有得到处理的该类异常会促使MFC显示一个对话框,提示

“非预期的文件格式”。如果每次修改对象的串行化存储格式时都能做到增加模式号,那么就不怕

这种无心的操作—试图把磁盘中存的老版本对象读入内存里的新版本对象了。

有一个问题经常会突然在使用可串行化类的应用程序中出现,这就是向下兼容。换句话说,就

是如何并行化在老版本应用程序中创建的对象。如果对象的持久存储格式随应用程序版本的更新发

生了变化,这时你可能希望新版本应用程序对两种格式都能读。但是一旦MFC发现不配套的模式号,

它就发送异常。鉴于MFC的结构特点,最好按照MFC的方式处理异常并终止串行化过程。

可视化模式也就产生了。

可视化模式只是包含VERSIONABLE_SCHEMA标志的模式号。标志告诉MFC

应用程序针对某一类能够处理多种串行化的数据格式。这种模式禁止CArchiveException,并允许应

用程序对不同的模式号有判断地响应。使用了可视化模式的应用程序可以提供用户希望的向下兼容性。

如果要编写一个具有MFC可视化模式支持的可串行类,一般需要两步:

将IMPLEMENT_SERLAL宏中的模式号与值VERSIONABLE_SCHEMA相或。

如果从档案加载对象时需要调用CArchive::GetObjectSchema,则要修改类的Serialize函数,

并相应地调整其并行化例程。GetObjectSchema返回要进行并行化对象的模式号。

调用GetObjectSchema时要注意几个规则。首先,只有对象在被并行化时才能调用。其次,必须在

读取档案对象数据之前调用。再者,它只能调用一次。如果GetObjectSchema在调用Serialize前后调

用两次,则返回-1。我们先看个例子,这是版本1的CLine类:

class CLine : public CObject

{

DECLARE_SERIAL (CLine)

protected:

CPoint m_ptFrom;

CPoint m_ptTo;

public:

CLine () {} // Required!

CLine (CPoint from, CPoint to) { m_ptFrom = from; m_ptTo = to; }

void Serialize (CArchive& ar);

};

这是Serialize函数:

void CLine::Serialize (CArchive& ar)

{

CObject::Serialize (ar);

if (ar.IsStoring ())

ar << m_ptFrom << m_ptTo;

else // Loading, not storing

ar >> m_ptFrom >> m_ptTo;

}

在实现类的过程中出现的语句:IMPLEMENT_SERIAL (CLine, CObject, 1)

这个类就可以串行化了。目前版本号为1,如果后来又给CLine添加了一个持久性数据成员,则要把版

本号增加到2,这样主结构就能根据程序的不同版本区别串行化到磁盘的CLine对象了。否则,磁盘上

的版本为1的CLine就可能被读入内存中版本为2的CLine,从而可能造成严重后果。

假定在应用程序的第2版本中,你要修改CLine类,想添加一个成员变量,用来保存线的颜色。下

面是修改后的类的声明:

class CLine : public CObject

{

DECLARE_SERIAL (CLine)

protected:

CPoint m_ptFrom;

CPoint m_ptTo;

COLORREF m_clrLine; // Line color (new in version 2)

public:

CLine () {}

CLine (CPoint from, CPoint to, COLORREF color)

{ m_ptFrom = from; m_ptTo = to; m_clrLine = color }

void Serialize (CArchive& ar);

};

因为线的颜色是持久属性(也就是说,保存到档案中的红线在读出时依旧是红的。),所以你向修改

CLine::Serialize,使它在串行化m_ptFrom和m_ptTo之外还能串行化m_clrLine。这意味着要把CLine

的模式号增加到2。使用原类时按以下方式调用MFC的IMPLEMENT_SERIAL宏:

IMPLEMENT_SERIAL(CLine,CObject,1)

但是在修改后的类中,应该这样调用IMPLEMENT_SERIAL:

IMPLEMENT_SERIAL(CLine,CObject,2|VERSIONABLE_SCHEMA)

更新后的程序在读取CLine对象时,如果对象的模式号是1,MFC也不会发送CArchive异常,因为模式号

中有VERSIONABLE_SCHEMA标志。但是它会了解到:由于模式号从1变为2,两个模式实际上是不同的。

现在工作只做到一半。最后一步是修改CLine::Serialize,使它根据GetObjectSchema不同的返回值并行化CLine。原Serialize函数如下:

void CLine::Serialize (CArchive& ar)

{

CObject::Serialize (ar);

if (ar.IsStoring ())

ar << m_ptFrom << m_ptTo;

else // Loading, not storing

ar >> m_ptFrom >> m_ptTo;

}

新函数如下:

void CLine::Serialize (CArchive& ar)

{

CObject::Serialize (ar);

if (ar.IsStoring ())

ar << m_ptFrom << m_ptTo << m_clrLine;

else {

UINT nSchema = ar.GetObjectSchema ();

switch (nSchema) {

case 1: // Version 1 CLine

ar >> m_ptFrom >> m_ptTo;

m_clrLine = RGB (0, 0, 0); // Default color

break;

case 2: // Version 2 CLine

ar >> m_ptFrom >> m_ptTo >> m_clrLine;

break;

default: // Unknown version

AfxThrowArchiveException (CArchiveException::badSchema);

break;

}

}

}

明白它是怎样工作的吗?CLine对象写到档案上时,它的格式总是CLine的第2个版本。但是读取

CLine时,根据GetObjectSchema返回值的不同,它又被当做CLine版本1或版本2读回。如果模式号为1,则对象按老方式读取,并把m_clrLine设置为默认值。如果模式号为2,则对象所以数据成员,包括

m_clrLine,都要从档案中读取出来。其他模式号会导致CArchiveException,表示不能识别版本号(如

果发生异常,可能是因为程序错了或档案坏了。)如果将来还要修改CLine,则要把模式号增加到3并给

新模式添加一个case程序段。

明白了串行化类分配版本号,我们现在看个例子吧:

这是我不久前编的一个程序,只是为了学习MFC而编的,没有什么实际意义,而且网络上有很多这

样的程序。编的不好,不要见笑,哈哈^_^自己先笑了。

在squareDoc.h文件中:

class CSquareDoc : public CDocument

{

protected: // create from serialization only

CSquareDoc();

DECLARE_SERIAL(CSquareDoc)//注意这句

// Attributes

public:

// Operations

public:

........//省略的部分程序代码,具体看例子

}

在squareDoc.cpp文件中:

#include "stdafx.h"

#include "square.h"

#include "squareDoc.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

........//省略部分程序代码,具体看例子

/////////////////////////////////////////////////////////////////////////////

// CSquareDoc

IMPLEMENT_SERIAL(CSquareDoc, CDocument,2|VERSIONABLE_SCHEMA)//注意这句

BEGIN_MESSAGE_MAP(CSquareDoc, CDocument)

//{{AFX_MSG_MAP(CSquareDoc)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

........//省略部分程序代码,具体看例子

void CSquareDoc::Serialize(CArchive& ar)

{

ar.SerializeClass(RUNTIME_CLASS(CSquareDoc));

CDocument::Serialize(ar);

if (ar.IsStoring())//保存数据

{

SaveData(ar);//只按最新版本存储

}

else//提取数据

{

UINT nSchema=ar.GetObjectSchema();

switch(nSchema)

{

case 1:LoadDataV1(ar);break;//版本1

case 2:LoadDataV2(ar);break;//版本2

default:AfxThrowArchiveException(CArchiveException::badSchema);

break;

}

}

}

//保存数据

void CSquareDoc::SaveData(CArchive &ar)

{

//保存变量

ar<<iSpeed<<fenshu<<iBomb<<iMissile<<iA_bomb;

//保存地图数据

for(int i=0;i<SQUARE_AMOUNT_y;i++)

{

for(int j=0;j<SQUARE_AMOUNT_x;j++)

{

ar<<mc[j][i].CellPos.x<<mc[j][i].CellPos.y

<<mc[j][i].IsFillFlag<<mc[j][i].rgb

<<mc[j][i].size.cx<<mc[j][i].size.cy;

}

}

//保存方块数据

ar<<sq.rgb<<sq.type;

for(i=0;i<4;i++)

{

ar<<sq.ps[i].x<<sq.ps[i].y;

}

//保存预知方块数据

ar<<PreSq.rgb<<PreSq.type;

for(i=0;i<4;i++)

{

ar<<PreSq.ps[i].x<<PreSq.ps[i].y;

}

}

//提取版本1的数据

void CSquareDoc::LoadDataV1(CArchive &ar)

{

//提取变量

ar>>iSpeed>>fenshu;

//版本1中没有炸弹、导弹和原子弹

iBomb=0;iMissile=0;iA_bomb=0;

//提取地图数据(版本1中是单色)

for(int i=0;i<SQUARE_AMOUNT_y;i++)

{

for(int j=0;j<SQUARE_AMOUNT_x;j++)

{

ar>>mc[j][i].CellPos.x>>mc[j][i].CellPos.y

>>mc[j][i].IsFillFlag>>mc[j][i].size.cx

>>mc[j][i].size.cy;

mc[j][i].rgb=RGB(0,255,0);

}

}

//提取方块数据(版本1中是单色)

ar>>sq.type;

sq.rgb=RGB(0,255,0);

for(i=0;i<4;i++)

{

ar>>sq.ps[i].x>>sq.ps[i].y;

}

//提取预知方块数据(版本1中是单色)

ar>>PreSq.type;

PreSq.rgb=RGB(0,255,0);

for(i=0;i<4;i++)

{

ar>>PreSq.ps[i].x>>PreSq.ps[i].y;

}

}

//版本2

void CSquareDoc::LoadDataV2(CArchive &ar)

{

//提取变量

ar>>iSpeed>>fenshu>>iBomb>>iMissile>>iA_bomb;

//提取地图数据

for(int i=0;i<SQUARE_AMOUNT_y;i++)

{

for(int j=0;j<SQUARE_AMOUNT_x;j++)

{

ar>>mc[j][i].CellPos.x>>mc[j][i].CellPos.y

>>mc[j][i].IsFillFlag>>mc[j][i].rgb

>>mc[j][i].size.cx>>mc[j][i].size.cy;

}

}

//提取方块数据

ar>>sq.rgb>>sq.type;

for(i=0;i<4;i++)

{

ar>>sq.ps[i].x>>sq.ps[i].y;

}

//提取预知方块数据

ar>>PreSq.rgb>>PreSq.type;

for(i=0;i<4;i++)

{

ar>>PreSq.ps[i].x>>PreSq.ps[i].y;

}

}

........//省略部分程序代码,具体看例子。

结束语

你现在应该知道怎么配置版本模式了吧,是不是很简单阿!本程序在WinXP和VC++6.0下编译通过。其实这个例子中还有很多地方值的参考,你可要仔细看看,在这里我就不多说了,有什么问题欢迎提出,

大家可以讨论讨论。再次声明,本程序只是为了学习MFC的朋友使用,可以随意复制,修改,我想这种程

序不会有人作为商业目的吧。有不同意见和问题的朋友,欢迎提出,谢谢!!!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有