分享
 
 
 

CArchive的版本控制功能

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

1 引言

Visual C++ 6.0语言开发环境在MFC中以CArchive类为中心提供输入输出的串行化和数据版本控制功能。一般来说,随着软件的升级,对应的输入输出也会发生改变,如何保证多个版本的软件与多种输入输出数据之间的亲和性成为问题。比如说,当用户使用1.0版的软件,而提供的输入却是2.0版软件的输出,结果会如何呢?同样2.0版的软件如何处理1.0版生成的数据呢?

的确,用户可以定义一套自己的文件格式为上述因版本不同造成的造成的麻烦提供解决方案,许多软件都是这么干,但如果你所编制的软件规模很小,功能单一,对数据格式并不在意(当然要保证输入输出正确),那么花力气指定自己的数据文件格式似乎过于繁琐。使用CArchive可以简化这类软件的编制。

2 CArchive的版本控制方式

当你使用DECLARE_SERIAL和IMPLEMENT_SERAIL宏时,你就在你的类中声明并定义了一个CRuntimeClass类的静态结构成员,该结构中的m_wSchema可以记录你指定的版本号。注意虽然按照命名约定m_wSchema似乎是个WORD,但实际它是个UINT。当串行化输出时,类信息(即类名和版本号的低字,高字是标志)被写入数据文件,而串行化输入时CArchive::ReadClass()和CArchive::ReadObject()根据这些信息以及后面的对象数据重建对象,返回对象指针。这一切均在类似pObject<<ar;的语句中发生。如果CRuntimeClass检测到数据文件中纪录的版本号与类的版本号(即类的CRuntimeClass成员中记录的版本号)不一致,根据你设置版本号时是否指定VERSIONABLE_SCHEMA标志有不同处理:如果你没有设置该标志,CArchive::ReadClass()将抛出一个CArchiveException类型的异常;如果你设置了,那么CArchive::ReadClass()将成功返回对应的CRuntimeClass类对象,而CArchive::ReadObject()将创建串行类对象并返回该对象和版本号(在 CArchive::m_nObjectShema成员中,该成员是public型,可以直接访问或调用CArchive::GetObjectSchema()返回。如果调用CArchive::GetObjectSchema(),要注意该函数只能调用一次!)。

虽然这一切工作的很好,但问题并非象看上那么简单,因为你的软件在不同版本中会有改动。由于CArchive::ReadClass()是根据类名来获取CRuntimeClass类成员的,而CArchive::ReadObject()又是根据CArchive::ReadClass()返回的CRuntimeClass类成员对象来创建串行类对象的(CRuntimeClass成员是串行类的静态成员)。因此,如果新的软件中根本没有对应的串行类,那么必然失败!

3 类的改动对版本控制的影响

版本升级需要扩展或改变类的接口,从串行类的定义来看,有多种方式类中扩展新的功能:

(1). 修改原先的类定义,同时改变类名

这时实际上原先的类已经不存在,那么CArchive无法根据数据文件创建对应的对象,因此新版本与旧版本之间完全无法兼容,如同两个完全无关的软件。不仅旧软件无法识别并处理新软件的数据文件,新软件同样无法识别并处理旧软件的数据文件。你需要自己识别处理数据文件的转换,MFC无法识别处理。

(2). 修改原先的类定义,但类名保留

此时,CArchive可以根据旧的数据文件创建对象,但在新版本串行类的CArchive::Serailize()需要根据版本好做不同的输入输出处理。由于类定义已经改变,对旧数据的处理可能同旧版本处理方法上有差异,需要小心从事。而旧版本可以识别出新版本,但一般无法处理。

(3). 从原先的类派生新类支持扩展功能

这种方式充分利用了C++语言的特性,新版本可以根据版本号对不同版本数据文件进行处理,并且依然可以生成旧版本的数据文件,兼容性非常好。但旧版本无法识别出新版的数据文件,更谈不上处理了。

比较看来,方式(1)比较粗暴,完全不考虑软件升级的兼容性问题,方式(2)和(3)均考虑到了兼容问题,但方式(2)的编程复杂,串行类要针对版本号编制处理代码,而方式(3)利用C++语言的特性比较好处理多版本数据问题,只要注意虚拟函数就可以了。但如果软件升级次数很多,从1.0不停地升到8.0,那么显然有点不舒服。另外一个重要的不足是旧版本无法识别新版本的数据文件,只能提示数据文件格式不对。

4 例子

下面的例子是升级后的代码,采用方式(3)处理不同版本问题:

// 注意:虽然使用某些MFC的代码,但为了简单没有使用

// 向导生成的初始化代码,因此对MFC的支持是不全的!

// 声明对象的头文件

#ifndef __LCG_OBJECT_H__

#define __LCG_OBJECT_H__

#include<AfxWin.h>

// 版本1.0采用的对象

class CObject1 : public CObject

{

DECLARE_SERIAL( CObject1 )

public:

CObject1();

virtual void Serialize( CArchive& ar );

virtual BOOL IsOkVer( UINT nVer );

CString m_str;

};

// 版本(2)扩展了版本1.0的类

class CObject2 : public CObject1

{

DECLARE_SERIAL( CObject2 )

public:

CObject2();

virtual void Serialize( CArchive& ar );

virtual BOOL IsOkVer( UINT nVer );

CString m_str2; // 版本2.0才有的数据

};

#endif

// 对象的.cpp文件

#include "Object.h"

IMPLEMENT_SERIAL( CObject1, CObject, (VERSIONABLE_SCHEMA|1) )

CObject1::CObject1()

{

m_str = "CObject1 Msg\n";

}

// 1.0版的版本检查函数,版本检查函数要声明为虚拟的,

// 这样实际的对象才可能进行正确的检查,否则,即使对象正确,

// 但由于调用指针是基类的指针,检查结果不正确!

BOOL CObject1::IsOkVer( UINT nVer )

{

return nVer==1;

}

void CObject1::Serialize( CArchive& ar )

{

if( ar.IsStoring() )

{

ar<<(int)m_str.GetLength();

ar.WriteString( m_str );

}

else

{

if( !IsOkVer( ar.m_nObjectSchema ) )

{

AfxMessageBox( "CObject1得到不正确的版本!" );

return;

}

int nLen; ar>>nLen;

ar.Read( m_str.GetBuffer(nLen), nLen );

}

}

// 2.0版的对象

IMPLEMENT_SERIAL(CObject2,CObject1, (VERSIONABLE_SCHEMA|2))

CObject2::CObject2()

{

m_str2 = "CObject2 Msg\n";

}

void CObject2::Serialize( CArchive& ar )

{

CObject1::Serialize( ar );

if( ar.IsStoring() )

{

ar<<(int)m_str2.GetLength();

ar.WriteString( m_str2 );

}

else

{

if( !IsOkVer( ar.m_nObjectSchema ) )

{

AfxMessageBox( "CObject2得到不正确的版本!" );

return;

}

int nLen; ar>>nLen;

ar.Read( m_str2.GetBuffer(nLen), nLen );

}

}

BOOL CObject2::IsOkVer( UINT nVer )

{

return nVer==2;

}

// 主程序,对不同版本使用不同的对象

#include<AfxWin.h>

#include"Object.h"

#pragma comment( lib, "msvcrt.lib" )

int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hNULL,

LPSTR lpCmdLine, int nCmdShow )

{

AfxWinInit( hInst, hNULL, lpCmdLine, nCmdShow ); // MFC初始化

CFile file( "Try1.txt", CFile::modeRead );

CArchive ar( &file, CArchive::load );

CObject *pb = NULL;

CObject1 *pb1 = NULL;

CObject2 *pb2 = NULL;

char szMsg[128];

BOOL bError = FALSE;

// 如果数据文件格式不正确会触发异常!

TRY{

ar>>pb;

}

CATCH( CArchiveException, pe )

{

bError = TRUE; // 出错!

pe->GetErrorMessage( szMsg, sizeof(szMsg) ); // 获取提示信息

DELETE_EXCEPTION( pe );

AfxMessageBox( szMsg );

}

END_CATCH

if( bError )

return 0;

// 根据不同的版本调用不同的类

switch((pb->GetRuntimeClass()->m_wSchema)&

~(VERSIONABLE_SCHEMA) )

{

case 1:

pb1 =

(CObject1*)AfxDynamicDownCast( RUNTIME_CLASS(CObject1), pb );

AfxMessageBox( pb1->m_str );

break;

case 2:

pb2=

(CObject2*)AfxDynamicDownCast( RUNTIME_CLASS(CObject2), pb );

AfxMessageBox( pb2->m_str2 );

break;

default:

AfxMessageBox( "Unkown Object!" );

break;

}

delete pb;

return 0;

}

5. 结束语

上述思路和例子是一般软件版本控制的一个简单的模拟,实际中要复杂的多,甚至需要为版本控制生成专用数据,但对于那些需要快速开发的小规模的软件,上述方法可以作为一种选择。

参考文献

1 Visual C++ 6.0 MSDN

2 Visual C++ 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- 王朝網路 版權所有