系列化入门读物
这个指南描述如何轻松地系列化一个简单的对象。
这篇文章包含三个部分。
PART1 介绍基本的系列化
PART2 解释如何有效地读取无效数据和支持版本。
PART3 描述如何对复杂的对象进行系列化。
系列化入门读物-PART2
在Part 1中我们看到了如何通Carchive并利serialize()方法对简单的对象进行系列化,其代码如下:int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
// Write employee name and id
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
else {
// Read employee name and id
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
}
}
CATCH_ALL (pException)
{
nStatus = ERROR;
}
END_CATCH_ALL
return (nStatus);
}
这段代码有一个问题,就是当我们进行访问的数据并不是我们预期的?如果这个文件中的数据并不是一个Cstring并在其后跟着一个int,我们的serialize()方法将会出错。但这个还好,如果你认可错误并返回一个指定的状态如INVALID_DATAFILE。我们可以通过对象中的标志来来检查我们访问的是否是有效的文件。
Object signatures(对象标志)
对象标志实际上是一个用来识别对象的字符串。我们在Cfoo中加入一个标志: class CFoo
{
...
// Methods
public:
...
CString getSignature();
// Data members
...
protected:
static const CString Signature; // object signature
};
// Static constants
const CString CFoo::Signature = "FooObject";
下一步我们把serialize()修改成在系列化对象的数据成员时先对标志进行系列化。如果发现一个无效的标志或者没有发现标志,表示我们正在尝试读一个并不包含Cfoo对象的文件。下面是读取一个带标志的对象的逻辑图:
下面是代码:
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
bool bSignatureRead = false;
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
// Write signature
(*pArchive) << getSignature();
// Write employee name and id
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
else {
// Read signature - complain if invalid
CString strSignature;
(*pArchive) >> strSignature;
bSignatureRead = true;
if (strSignature.Compare (getSignature()) != 0) {
return (INVALID_DATAFILE);
}
// Read employee name and id
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
}
}
CATCH_ALL (pException)
{
nStatus = bSignatureRead ? ERROR : INVALID_DATAFILE;
}
END_CATCH_ALL
return (nStatus);
}
你需要保证你的所有对象都有相同的标志,实际标志是什么并不是很重要。如果你开发一组产品进,使用相近的对像标志将对你很有帮助。另外,开发者不要误解为对不同的对象也使用相同的标志。如果你要使你的数据文件很难做回逆设计,你需你使用与对象名字没有明显联系的标志。
Versioning(版本)
在产品的生命周期你要对其进行更新,你可能会发现你必需对CFOO的进行添加或删除成员。如果你完全发行一个Cfoo新的版,当尝试对旧的版本的对象进行存取时将会失败。这个是明显不能接受的。任何一个新版的Cfoo必需可以对其以前版本的对象进行系列化,换名话说,就是Cfoo的serialize()方法要可以对以前的可兼容的版本进行操作。我们很容易就可以实现对象的版本化,正如我们对对象加入一个标志一样,我们加入一个整型常量来表示版本号。
class CFoo
{
...
// Methods
public:
...
CString getSignature();
int getVersion();
// Data members
...
protected:
static const CString Signature; // object signature
static const int Version; // object version
};
对象版本在Foo.cpp中进行定义。
// Static constants
const CString CFoo::Signature = "FooObject";
const int CFoo::Version = 1;
下一步,我们把对版本的系列化加入到标志系列化之后且在成员数据系列化之前。如果遇到新的版本或者读取一个不支持的版本,在这些情况下,我们简单地返回UNSUPPORTED_VERSION状态。
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
bool bSignatureRead = false;
bool bVersionRead = false;
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
// Write signature and version
(*pArchive) << getSignature();
(*pArchive) << getVersion();
// Write employee name and id
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
else {
// Read signature - complain if invalid
CString strSignature;
(*pArchive) >> strSignature;
bSignatureRead = true;
if (strSignature.Compare (getSignature()) != 0) {
return (INVALID_DATAFILE);
}
// Read version - complain if unsupported
int nVersion;
(*pArchive) >> nVersion;
bVersionRead = true;
if (nVersion > getVersion()) {
return (UNSUPPORTED_VERSION);
}
// Read employee name and id
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
}
}
CATCH_ALL (pException)
{
nStatus = bSignatureRead && bVersionRead ? ERROR : INVALID_DATAFILE;
}
END_CATCH_ALL
return (nStatus);
}
在版本1中我们的Cfoo包含两个数据成员(m_strName和m_nId)。如果我们在版本2中加入第三个成员(int m_nDept),我们需要决定在读取旧版本时m_nDept初始化成什么值。在这个例子中,我们初始化为-1,这意味着职工的部门代码是未知的。
class CFoo
{
...
// Data members
public:
CString m_strName; // employee name
int m_nId; // employee id
int m_nDept; // department code (-1 = unknown)
};
我们同样需要在Foo.cpp中加入版本标志并设为2。
const int CFoo::Version = 2;
最后,我们需要修改serialize()中的读取数据部分,因为当读取旧版本时我们要对m_nDept进行初始化。注意文件保存时始终保存为最新版的。
int CFoo::serialize
(CArchive* pArchive)
{
...
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
...
// Write employee name, id and department code
(*pArchive) << m_strName;
(*pArchive) << m_nId;
(*pArchive) << m_nDept;
}
else {
...
// Read employee name and id
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
// Read department code (new in version 2)
if (nVersion >= 2) {
(*pArchive) >> m_nDept;
}
else {
m_nDept = -1; // unknown
}
}
}
CATCH_ALL (pException)
{
nStatus = bSignatureRead && bVersionRead ? ERROR : INVALID_DATAFILE;
}
END_CATCH_ALL
return (nStatus);
}