17.3.3 序列化元素类(1)
所有形状类都是可序列化的,因为它们都派生于其基类CElement,而CElement又派生于CObject。把CObject指定为CElement的基类的原因仅仅是为了获得对序列化的支持。确保实际上为每个形状类定义了默认构造函数。反序列化过程要求定义此构造函数。
现在可以为每个形状类添加对序列化的支持,这要在类定义和实现中添加适当的宏,然后给每个类的Serialize()函数成员添加代码,以序列化该函数的数据成员。可以首先从基类CElement开始,其中需要对类定义做如下修改:
- class CElement: public CObject
- {
- DECLARE_SERIAL(CElement)
- protected:
- CPoint m_StartPoint; // Element position
- int m_PenWidth; // Pen width
- COLORREF m_Color; // Color of an element
- CRect m_EnclosingRect; // Rectangle enclosing an element
- public:
- virtual ~CElement();
- virtual void Draw(CDC* pDC, std::shared_ptr<CElement> pElement=nullptr) {}
- virtual void Move(const CSize& aSize){} // Move an element
- virtual void Serialize(CArchive& ar) override; // Serialize object
- // Get the element enclosing rectangle
- const CRect GetEnclosingRect() const
- {
- return m_EnclosingRect;
- }
- protected:
- // Constructors protected so they cannot be called outside the class
- CElement();
- CElement(const CPoint& start, COLORREF color, int penWidth = 1);
- // Create a pen
- void CreatePen(CPen& aPen, std::shared_ptr<CElement> pElement)
- {
- if(!aPen.CreatePen(PS_SOLID, m_PenWidth,
- (this == pElement.get()) SELECT_COLOR : m_Color))
- {
- // Pen creation failed
- AfxMessageBox(_T(“Pen creation failed.”), MB_OK);
- AfxAbort();
- }
- }
- };
其中添加了DECLARE_SERIAL()宏以及虚函数Serialize()的声明。Application Wizard已经创建了一个默认的构造函数,在这个类中把它修改成了protected类型,只要它显式地出现在类定义中,它的访问规范是什么就无关紧要。它可以是public、protected或private类型,但是序列化仍然有效。如果忘记在一个类中包括默认构造函数,那么在编译IMPLEMENT_SERIAL()宏时,将出现一条错误消息。
应当为每个派生类CLine、CRectangle、CCircle、CCurve和CText都添加DECLARE_SERIAL()宏,其参数为相关类名。另外,还应当把Serialize()函数的重载声明添加为每个类的public成员。
在文件Elements.cpp中,必须在开始处添加下列宏:
- IMPLEMENT_SERIAL(CElement, CObject, VERSION_NUMBER)
在文件Element.h的其他静态常量的后面,添加下列行,可以定义静态常量VERSION_NUMBER:
- static const UINT VERSION_NUMBER = 1001; // Version number for serialization
然后在为其他形状类的.cpp文件中添加这个宏时,就可以使用该常量。例如对于CLine类,应当添加下列行:
- IMPLEMENT_SERIAL(CLine, CElement, VERSION_NUMBER)
对于其他形状类也可以采用类似的方法。在修改与文档有关的类时,只需要在文件Element.h中修改VERSION_NUMBER的定义,这个新版本号在所有的Serialize()函数中都将适用。
形状类中的Serialize()函数
现在可以针对每个形状类实现Serialize()成员函数。首先从CElement类开始,在Elements.cpp中添加如下定义:
- void CElement::Serialize(CArchive& ar)
- {
- CObject::Serialize(ar); // Call the base class function
- if (ar.IsStoring())
- { // Writing to the file
- ar << m_StartPoint // Element position
- << m_PenWidth // The pen width
- << m_Color // The element color
- << m_EnclosingRect; // The enclosing rectangle
- }
- else
- { // Reading from the file
- ar >> m_StartPoint // Element position
- >> m_PenWidth // The pen width
- >> m_Color // The element color
- >> m_EnclosingRect; // The enclosing rectangle
- }
- }