按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
11 00 22 00 33 00 44 00 ;CRect
FF FF ;new class tag
01 00 ;schema
07 00 ;class name string length
43 43 69 72 63 6C 65 ;〃CCircle〃
55 00 66 00 77 00 ;CPoint & radius
01 80 ;old class tag
02 00 ;DWordArray size
28 00 35 00 ;point
28 00 35 00 ;point
03 80 ;old class tag
11 00 22 00 33 00 44 00 ;CRect
05 80 ;old class tag
55 00 66 00 77 00 ;CPoint & radius
我希望有一个专门负责Serialization 的函数,就叫作Serialize 好了。假设现在我的Document
类别名称为CScribDoc,我希望有这么便利的程序方法(请仔细琢磨琢磨其便利性):
void CScribDoc::Serialize(CArchive& ar)
{
if (ar。IsStoring())
ar 》 m_sizeDoc;
m_graphList。Serialize(ar);
}
void CObList::Serialize(CArchive& ar)
{
if (ar。IsStoring()) {
164
…………………………………………………………Page 227……………………………………………………………
第3章 MFC 六大關鍵技術之模擬
ar pNext)
ar data;
}
else {
WORD nNewCount;
ar 》》 nNewCount;
while (nNewCount……) {
CObject* newData;
ar 》》 newData;
AddTail(newData);
}
}
}
void CStroke::Serialize(CArchive& ar)
{
m_ptArray。Serialize(ar);
}
void CDWordArray::Serialize(CArchive& ar)
{
if (ar。IsStoring()) {
ar nOldSize;
for (int i = 0; i 《 m_nSize; i++)
ar 》》 m_pData'i';
}
}
void CRectangle::Serialize(CArchive& ar)
{
if (ar。IsStoring())
ar 》 m_rect;
}
void CCircle::Serialize(CArchive& ar)
{
if (ar。IsStoring()) {
165
…………………………………………………………Page 228……………………………………………………………
第篇 勿在浮砂築高台
ar 》 (WORD&)m_center。y;
ar 》》 (WORD&)m_radius;
}
}
每一个可写到文件或可从文件中读出的类别,都应该有它自己的Serailize 函数,负责它
自己的资料读写文件动作。此类别并且应该改写》 运算子,把资料导流到
archive 中。archive 是什么?是一个与文件息息相关的缓冲区,暂时你可以想象它就是
文件的化身。当图3…3 的文件写入文件时,Serialize 函数的调用次序如图3…4 。
CMyDoc::Serialize
CObList::Serialize
如果串行元素是圆
CStroke::Serialize
CDWordArray::Serialize
如果串行元素是矩形
CRectangle::Serialize
如果串行元素是线条
CCircle::Serialize
图3…4 图3…3 的文件内容写入文件时,Serialize 函数的调用次序。
166
…………………………………………………………Page 229……………………………………………………………
第3章 MFC 六大關鍵技術之模擬
DECLARE_SERIAL / IMPLEMENT_SERIAL 宏
要将》 两个运算子多载化,还要让Serialize 函数神不知鬼不觉地放入类别声明
之中,最好的作法仍然是使用宏。
类别之能够进行文件读写动作,前提是拥有动态生成的能力,所以,MFC 设计了两个宏
DECLARE_SERIAL 和IMPLEMENT_SERIAL :
#define DECLARE_SERIAL(class_name)
DECLARE_DYNCREATE(class_name)
friend CArchive& AFXAPI operator》》(CArchive& ar; class_name* &pOb);
#define IMPLEMENT_SERIAL(class_name; base_class_name; wSchema)
CObject* PASCAL class_name::CreateObject()
{ return new class_name; }
_IMPLEMENT_RUNTIMECLASS(class_name; base_class_name; wSchema;
class_name::CreateObject)
CArchive& AFXAPI operator》》(CArchive& ar; class_name* &pOb)
{ pOb = (class_name*) ar。ReadObject(RUNTIME_CLASS(class_name));
return ar; }
为了在每一个对象被处理(读或写)之前,能够处理琐屑的工作,诸如判断是否第一次
出现、记录版本号码、记录文件名等工作,CRuntimeClass 需要两个函数Load 和Store
:
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL =》 abstract class
CRuntimeClass* m_pBaseClass;
CObject* CreateObject();
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar; UINT* pwSchemaNum);
// CRuntimeClass objects linked together in simple list
static CRuntimeClass* pFirstClass; // start of class list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
167
…………………………………………………………Page 230……………………………………………………………
第篇 勿在浮砂築高台
你已经在上一节看过Load 函数,当时为了简化,我把它的参数拿掉,改为由屏幕上获
得类别名称,事实上它应该是从文件中读一个类别名称。至于Store 函数,是把类别名
称写入文件中:
// Runtime class serialization code
CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar; UINT* pwSchemaNum)
{
WORD nLen;
char szClassName'64';
CRuntimeClass* pClass;
ar 》》 (WORD&)(*pwSchemaNum) 》》 nLen;
if (nLen 》= sizeof(szClassName) || ar。Read(szClassName; nLen) != nLen)
return NULL;
szClassName'nLen' = '0';
for (pClass = pFirstClass; pClass != NULL; pClass = pClass…》m_pNextClass)
{
if (l