友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第28部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
43 43 69 72 63 6C 65 ;〃CCircle〃
55 00 66 00 77 00 ;CPoint & radius
07 00 ;class name string length
43 53 74 72 6F 6B 65 ;〃CStroke〃
02 00 ;DWordArray size
28 00 35 00 ;point
28 00 35 00 ;point
0A 00 ;class name string length
43 52 65 63 74 61 6E 67 6C 65 ;〃CRectangle〃
11 00 22 00 33 00 44 00 ;CRect
07 00 ;class name string length
43 43 69 72 63 6C 65 ;〃CCircle〃
55 00 66 00 77 00 ;CPoint & radius
还算堪用。但如果考虑到屏幕卷动的问题,以及打印输出的问题,应该在最前端增加「文
件大小」。另外,如果这份文件有100 条线条,50 个圆形,80 个矩形,难不成我们要记
录230 个类别名称?应该有更好的方法才是。
162
…………………………………………………………Page 225……………………………………………………………
第3章 MFC 六大關鍵技術之模擬
CObList m_graphList
CObList m_graphList
图3…3 一个串行, 内含三种基本图形: 线条、圆形、矩形。
我们可以在每次记录对象内容的时候,先写入一个代码,表示此对象之类别是否曾在档
案中记录过了。如果是新类别,乖乖地记录其类别名称;如果是旧类别,则以代码表示。
这样可以节省文件大小以及程序用于解析的时间。啊,不要看到文件大小就想到硬盘很
便宜,桌上的一切都将被带到网上,你得想想网络频宽这回事。
还有一个问题。文件的「版本」如何控制?旧版程序读取新版文件,新版程序读取旧版
文件,都可能出状况。为了防弊,最好把版本号码记录上去。最好是每个类别有自己的
版本号码。
下面是新的构想,也就是Serialization 的目标:
163
…………………………………………………………Page 226……………………………………………………………
第篇 勿在浮砂築高台
20 03 84 03 ;Document Size
06 00 ;CObList elements count
FF FF ;new class tag
02 00 ;schema
07 00 ;class name string length
43 53 74 72 6F 6B 65 ;〃CStroke〃
02 00 ;DWordArray size
28 00 13 00 ;point
28 00 13 00 ;point
FF FF ;new class tag
01 00 ;schema
0A 00 ;class name string length
43 52 65 63 74 61 6E 67 6C 65 ;〃CRectangle〃
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 (lstrcmp(szClassName; pClass…》m_lpszClassName) == 0)
return pClass;
}
return NULL; // not found
}
void CRuntimeClass::Store(CArchive& ar) const
// stores a runtime class description
{
WORD nLen = (WORD)lstrlenA(m_lpszClassName);
ar
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!