新手指南-序列化篇之三
2008-04-08 23:05
429 查看
序列化初步之三(翻译)
http://www.codeproject.com/cpp/serialization_primer3.asp
在前两部分,我们学习了在通常情况下,如何为序列化提供有力的支持。在这一部分,我们将学习序列化任何一种对象时的特殊的规则。这里有四个常用的参考例子。每个例子都由前一个构成。Ø 序列化一个简单类Ø 序列化一个派生类Ø 序列化一个同源的聚集类Ø 序列化一个异源的聚集类我们的Searialize()方法将返回以下状态码之一:l Successl InvalidFormatl UnsupportedVersionl ReadErrorl WriteError序列化一个简单类一个“简单类”的定义是:一个即没有父类,也没有聚集类的对象。序列化一个简单的类需要以下步骤: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申明。这里有一些关于类工厂的参考文章:Generic Class Factory by Robert A. T. Káldy
The Factory Method (Creational) Design Pattern by Gopalan Suresh Raj
Abstract Factory Pattern by Mark Grand
虽然文章是复杂多样的,但在这之后的基本思想是一样的。一个类工厂和类没有多大差别,它提供一个适当的指定的静态方法(例如:create()),这个方法提供一个特殊类型的对象。通过工厂的create()方法,你可以隐藏令人厌恶的if申明。稍微整理一下代码:... // Construct object of appropriate type ISerializable* pObject = MyClassFactory::create (strSignature); ASSERT (pObject != NULL); ...
http://www.codeproject.com/cpp/serialization_primer3.asp
在前两部分,我们学习了在通常情况下,如何为序列化提供有力的支持。在这一部分,我们将学习序列化任何一种对象时的特殊的规则。这里有四个常用的参考例子。每个例子都由前一个构成。Ø 序列化一个简单类Ø 序列化一个派生类Ø 序列化一个同源的聚集类Ø 序列化一个异源的聚集类我们的Searialize()方法将返回以下状态码之一:l Successl InvalidFormatl UnsupportedVersionl ReadErrorl WriteError序列化一个简单类一个“简单类”的定义是:一个即没有父类,也没有聚集类的对象。序列化一个简单的类需要以下步骤: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申明。这里有一些关于类工厂的参考文章:Generic Class Factory by Robert A. T. Káldy
The Factory Method (Creational) Design Pattern by Gopalan Suresh Raj
Abstract Factory Pattern by Mark Grand
虽然文章是复杂多样的,但在这之后的基本思想是一样的。一个类工厂和类没有多大差别,它提供一个适当的指定的静态方法(例如:create()),这个方法提供一个特殊类型的对象。通过工厂的create()方法,你可以隐藏令人厌恶的if申明。稍微整理一下代码:... // Construct object of appropriate type ISerializable* pObject = MyClassFactory::create (strSignature); ASSERT (pObject != NULL); ...