序列化一个简单类
一个“简单类”的定义是:一个即没有父类,也没有聚集类的对象。序列化一个简单的类需要以下步骤:
1.
序列化对象签名和版本
2.
序列化对象成员(如果有)
在下面的例子中,类
Point
包含两个
int
型变量,表示点的坐标。对象的签名和版本定义成静态成员(
m_strSignature
和
m_nVersion
),所以每个
Point
类的实例都共享它们。
int Point::serialize
(CArchive* pArchive)
{
ASSERT (pArchive != NULL);
// Step 1: Serialize signature and version
int nVersion;
try {
if (pArchive->IsStoring()) {
(*pArchive) << Point::m_strSignature;
(*pArchive) << Point::m_nVersion;
} else {
CString strSignature;
(*pArchive) >> strSignature;
if (strSignature != Point::m_strSignature)
return (Status::InvalidFormat);
(*pArchive) >> nVersion;
if (nVersion > Point::m_nVersion;)
return (Status::UnsupportedVersion);
}
// Step 2: Serialize members
if (pArchive->IsStoring()) {
(*pArchive) << m_nX;
(*pArchive) << m_nY;
} else {
(*pArchive) >> m_nX;
(*pArchive) >> m_nY;
}
}
catch (CException* pException) {
// A read/write error occured
pException->Delete();
if (pArchive->IsStoring())
return (Status::WriteError);
return (Status::ReadError);
}
// Object was successfully serialized
return (Status::Success);
}
序列化一个派生类
派生类是指派生于一个简单类,并且不是一个聚集类的类。序列化一个派生类需要以下步骤:
1.
序列化对象签名和版本
2.
序列化对象的基类
<<
额外的步骤
3.
序列化对象的成员(如果有)
在下面的例子中,类
ColoredPoint
从
Point
类派生,并且增加一个叫做
m_nColor
的
int
型变量,表示点的颜色。如同所有序列化的类一样,
ColoredPoint
类也定义一个静态签名和版本。
int ColoredPoint::serialize
(CArchive* pArchive)
{
ASSERT (pArchive != NULL);
// Step 1: Serialize signature and version
int nVersion;
try {
if (pArchive->IsStoring()) {
(*pArchive) << ColoredPoint::m_strSignature;
(*pArchive) << ColoredPoint::m_nVersion;
} else {
CString strSignature;
(*pArchive) >> strSignature;
if (strSignature != ColoredPoint::m_strSignature)
return (Status::InvalidFormat);
(*pArchive) >> nVersion;
if (nVersion > ColoredPoint::m_nVersion;)
return (Status::UnsupportedVersion);
}
// Step 2: Serialize the base class
int nStatus = Point::serialize (pArchive);
if (nStatus != Status::Success)
return (nStatus);
// Step 3: Serialize members
if (pArchive->IsStoring())
(*pArchive) << m_nColor;
else
(*pArchive) >> m_nColor;
}
catch (CException* pException) {
// A read/write error occured
pException->Delete();
if (pArchive->IsStoring())
return (Status::WriteError);
return (Status::ReadError);
}
// Object was successfully serialized
return (Status::Success);
}
序列化一个同源的聚集类
同源聚集类常常用来存储数量不定的多个同类型对象。序列化同源聚集类需要以下步骤:
1.
序列化对象签名和版本
2.
序列化对象的基类(如果有)
3.
序列化聚集对象的数量
<<
额外的步骤
4.
序列化每个聚集对象
<<
额外的步骤
5.
序列化对象的其它成员(如果有)
在下面的例子中,类
ColoredPointList
是一个
ColoredPoint
对象的聚集。为了简单,
ColoredPointList
类使用
CPtrArray
来存储对象。如同所有序列化类一样,
ColoredPointList
类也定义了一个静态签名和版本。以下是
ColoredPointList
类的示例:
class ColoredPointList
{
// Construction/destruction
public:
ColoredPointList::ColoredPointList();
virtual ColoredPointList::~ColoredPointList();
// Attributes
public:
static const CString m_strSignature;
static const int m_nVersion;
// Operations
public:
int serialize (CArchive* pArchive);
// Members
protected:
CPtrArray m_coloredPoints;
}
下面是我们如何序列化它:
int ColoredPointList::serialize
(CArchive* pArchive)
{
ASSERT (pArchive != NULL);
int nStatus = Status::Success;
// Step 1: Serialize signature and version
int nVersion;
try {
if (pArchive->IsStoring()) {
(*pArchive) << ColoredPointList::m_strSignature;
(*pArchive) << ColoredPointList::m_nVersion;
} else {
CString strSignature;
(*pArchive) >> strSignature;
if (strSignature != ColoredPointList::m_strSignature)
return (Status::InvalidFormat);
(*pArchive) >> nVersion;
if (nVersion > ColoredPointList::m_nVersion;)
return (Status::UnsupportedVersion);
}
// Step 2: Serialize base class (if any)
//
// Nothing to do since ColoredPointList isn't derived from anything.
// But if it was derived from BaseColoredPointList, we'd do:
//
// nStatus = BaseColoredPointList::serialize (pArchive);
// if (nStatus != Status::Success)
// return (nStatus);
// Step 3: Serialize number of items in collection
int nItems = 0;
if (pArchive->IsStoring())
(*pArchive) << m_coloredPoints.GetSize();
else
(*pArchive) >> nItems;
// Step 4: Serialize each object in collection
for (int nObject=0; (nObject < nItems); nObject++) {
// 4a: Point to object being serialized
ColoredPoint* pColoredPoint = NULL;
if (pArchive->IsStoring())
pColoredPoint = (ColoredPoint *) m_coloredPoints.GetAt (nObject);
else
pColoredPoint = new ColoredPoint();
ASSERT (pColoredPoint != NULL);
// 4b: Serialize it
nStatus = pColoredPoint->serialize (pArchive);
if (nStatus != Status::Success)
return (nStatus);
if (!pArchive->IsStoring())
m_coloredPoints.Add (pColoredPoint);
}
// Step 5: Serialize object's other members (if any)
//
// Nothing to do since ColoredPointList doesn't have any other
// members. But if it contained an int (m_nSomeInt) and a Foo
// object (m_foo), we'd do:
//
// if (pArchive->IsStoring())
// (*pArchive) << m_nSomeInt;
// else
// (*pArchive) >> m_nColor;
//
// nStatus = m_foo::serialize (pArchive);
// if (nStatus != Status::Success)
// return (nStatus);
}
catch (CException* pException) {
// A read/write error occured
pException->Delete();
if (pArchive->IsStoring())
return (Status::WriteError);
return (Status::ReadError);
}
// Object was successfully serialized
return (Status::Success);
}
序列化一个异源聚集类
异源聚集类常常用来存储数量不定的多个不同类型对象。序列化一个异源聚集类需要以下步骤:
1.
序列化对象签名和版本
2.
序列化对象的基类(如果有)
3.
序列化聚集对象的数量
4.
对于每一个聚集对象
a.
序列化对象签名
<<
额外的步骤
b.
序列化对象
5.
序列化对象的其它成员(如果有)
你会注意到在序列化中一个仅有的额外步骤是
4(a)
,在序列化每个对象自身前,将先序列化每个对象的签名。这样以后读取数据就比较方便。当序列化一个同源聚集类时,我们处理同类型的对象(前一个例子中的
ColoredPoint
)。为了读取一个
ColoredPoint
类,我们在堆中构造它,并且调用它的
Searialize()
方法。
ColoredPoint* pColoredPoint = new ColoredPoint();
nStatus = pColoredPoint->serialize (pArchive);
当我们处理异源聚集类时,在序列化对象之前,我们需要知道我们读取的对象的类型。这个信息来自于对象的签名。由于我们在序列化对象时已经保存了对象的签名,因此读取时我们可以根据对象的类型构造合适的对象。
// Read object signature
CString strSignature;
pArchive >> strSignature;
// Construct object of appropriate type
ISerializable* pObject = NULL;
if (strSignature == ColoredPoint::m_strSignature)
pObject = new ColoredPoint();
else
if (strSignature == Line::m_strSignature)
pObject = new Line();
else
if (strSignature == Rectangle::m_strSignature)
pObject = new Rectangle();
else
return (Status::InvalidFormat);
ASSERT (pObject != NULL);
// Read it back in
nStatus = pObject->serialize (pArchive);
在上面的代码片段中,
ColoredPoint
,
Line
和
Rectangle
都是(最终)派生于一个公共的基类
ISerializable
。这个类除了纯虚函数外没有比一个抽象基类更多的内容(换句话说,是一个“接口”
)。
ISerializable
定义方法
getSignature()
,
getVersion
和
Serialize()
。
class ISerializable
{
// Construction/destruction
public:
ISerializable::ISerializable()
{ }
virtual ISerializable::~ISerializable()
{ }
// Operations
public:
// Get the object's signature
virtual CString getSignature() = 0;
// Get the object's version
virtual int getVersion() = 0;
// Serialize the object
virtual int serialize (CArchive* pArchive) = 0;
}
好,让我们序列化异源聚集类。在下面的例子中,类
ShapeList
是可变数量的
ColoredPoint
,
Line
和
Rectangle
对象的聚集,它们都派生于
ISerializable
。你可以将这些类看做是“
ISerializable
接口的实现”。
int ShapeList::serialize
(CArchive* pArchive)
{
ASSERT (pArchive != NULL);
int nStatus = Status::Success;
// Step 1: Serialize signature and version
int nVersion;
try {
if (pArchive->IsStoring()) {
(*pArchive) << ShapeList::m_strSignature;
(*pArchive) << ShapeList::m_nVersion;
} else {
CString strSignature;
(*pArchive) >> strSignature;
if (strSignature != ShapeList::m_strSignature)
return (Status::InvalidFormat);
(*pArchive) >> nVersion;
if (nVersion > ShapeList::m_nVersion;)
return (Status::UnsupportedVersion);
}
// Step 2: Serialize base class (if any)
//
// Nothing to do since ShapeList isn't derived from anything.
// But if it was derived from BaseShapeList, we'd do:
//
// nStatus = BaseShapeList::serialize (pArchive);
// if (nStatus != Status::Success)
// return (nStatus);
// Step 3: Serialize number of items in collection
int nItems = 0;
if (pArchive->IsStoring())
(*pArchive) << m_shapes.GetSize();
else
(*pArchive) >> nItems;
// Step 4: Serialize each object in collection
for (int nObject=0; (nObject < nItems); nObject++) {
// 4a: First serialize object's signature
CString strSignature;
if (pArchive->IsStoring())
(*pArchive) << pObject->getSignature();
else
(*pArchive) >> strSignature;
//
// 4b: Then serialize object
//
// 4b (1): Point to object being serialized
ISerializable* pObject = NULL;
if (pArchive->IsStoring())
pObject = (ISerializable *) m_shapes.GetAt (nObject);
else {
if (strSignature == ColoredPoint::m_strSignature)
pObject = new ColoredPoint();
else
if (strSignature == Line::m_strSignature)
pObject = new Line();
else
if (strSignature == Rectangle::m_strSignature)
pObject = new Rectangle();
else
return (Status::InvalidFormat);
}
ASSERT (pObject != NULL);
// 4b (2): Serialize it
nStatus = pObject->serialize (pArchive);
if (nStatus != Status::Success)
return (nStatus);
if (!pArchive->IsStoring())
m_shapes.Add (pColoredPoint);
}
// Step 5: Serialize object's other members (if any)
//
// Nothing to do since ShapeList doesn't have any other
// members. But if it contained an int (m_nSomeInt) and
// a Foo object (m_foo), we'd do:
//
// if (pArchive->IsStoring())
// (*pArchive) << m_nSomeInt;
// else
// (*pArchive) >> m_nColor;
//
// nStatus = m_foo::serialize (pArchive);
// if (nStatus != Status::Success)
// return (nStatus);
}
catch (CException* pException) {
// A read/write error occured
pException->Delete();
if (pArchive->IsStoring())
return (Status::WriteError);
return (Status::ReadError);
}
// Object was successfully serialized
return (Status::Success);
}
类工厂
你可以用一个基于签名的“类工厂”来提供一个新的对象实例以替换在代码片段中丑陋的
if
申明。这里有一些关于类工厂的参考文章:
虽然文章是复杂多样的,但在这之后的基本思想是一样的。一个类工厂和类没有多大差别,它提供一个适当的指定的静态方法(例如:
create()
),这个方法提供一个特殊类型的对象。通过工厂的
create()
方法,你可以隐藏令人厌恶的
if
申明。稍微整理一下代码:
...
// Construct object of appropriate type
ISerializable* pObject = MyClassFactory::create (strSignature);
ASSERT (pObject != NULL);
...