📄 13.4 可串行化的类.txt
字号:
13.4 可串行化的类
13.4.1
实现类对串行化的支持
下面利用 Serialize函数来保存第 11章中己定义的CGraph对象。如果要用CArchive类保存一个对象
的话,那么这个对象的类必须支持串行化。一个可串行化的类通常都有一个Serialize成员函数。如
果要使一个类可串行化,可以经过以下五个步骤来实现。
由从CObject派生类(或从CObject派生的某个类派生);
.重写Serialize成员函数:
③使用DECLARESERIAL宏(在类声明中)。该宏的声明形式如下所示:
DECLARE_SERIAL( class_name )
其中参数class name就是想要成为可串行化类的类名。
④定义不带参数的构造函数:
(!)为类在实现文件中使用IMPLEMENT SERIAL宏。该宏的声明如下所示:
IMPLEMENT_SERIAL( class_name , base_class_name, wSchema )
其中 class name参数是类的名称, base_class参数是基类的名称, wSchema是版本号。当从文件
中读取对象数据时,需要对所保存的数据版本号进行判断,如果与当前CArchive对象的版本号一致,
则加载操作成功,否则失败。
因此,为了使用CA rchive对象保存CGraph对象,首先要使CGraph类成为一个可串行化的类,再按照
上述步骤分别处理:
①修改CGraph类的定义,让其从CObject派生,即在Graph.h文件中将CGraph类的定义修改为:
class CGraph : public CObject
CI1重载Serialize函数:
在Graph.h头文件中增加该函数的声明:
void Serialize( CArchive& archive ) ;
并在Graph.cpp源文件中实现这个函数,具体代码如例 13-19所示。
void CGraph: :Serialize(CArchive& ar)
if(ar.IsStoring())
ar<<m_nDrawType<<m-ptOrigin<<m-ptEnd ;
else
ar>>m_nDrawτype>>m-ptOrigin>>m-ptEnd;
在如例13-19所示的Serialize函数中,首先判断CArchive对象: ar的状态,如果是存储,则保存
CGraph对象中图形三要素:绘制类型、绘制起点和终点:否则,就是加载数据,利用重载的提取操作
符获取数据。注意保存和提取数据的顺序。
③在声明CGraph类时,使用 DECLARE SERIAL宏,即在Graph.h头文件中,在 CGraph类定义的内部添
加下面这句宏调用的代码:
DECLARE_SERIAL(CGraph)
因为CGraph类定义一个不带参数的构造函数。因为现在CGraph类中正好有一个不带参数的构造函数,
所以不需要再定义了:
⑤为CGraph类在实现文件中使用IMPLEMENT SERIAL宏。即在Graph.cpp源文件中,在CGraph类的构造
函数前面添加下面这句宏调用的代码:
IMPLEMENT_SERIAL(CGraph, CObject , 1 )
经过以上这几个步骤之后, CGraph类就支持串行化了,然后为此类再增加一个图形绘制函数: Draw,
从而将图形数据和图形绘制封装在一个类中,这也符合面向对象的思想。增加的Draw函数的实现代
码如例 13-20所示。
19IJ 13-20
void CGraph: : Draw(CDC *pDC)
11创建透明画刷并选入设备描述表中
CBrush
*pBrush=CBrush : : FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=pDC->SelectObject(pBrush) ;
11根据绘制类型绘制相应的图形
switch(m_nDrawType)
case 1:
pDC->SetPixel(m-ptEnd ,RGB(O , O, O)) ;
break;
case 2:
pDC->MoveTo(m-ptOrigin) ;
pDC->LineTo(m-ptEnd) ;
break;
case 3:
508 I
pDC->Rectangle(CRect(m-ptOrigin , m-ptEnd)) ; break ;
case 4: pDC一>E工lipse(CRect(m-ptOrigin,m-ptEnd)) ; break ;
pDC->SelectObject(pOldBrush) ;
在新增的Draw函数中,在绘制图形之前创建一个透明的画刷 CpBrush),并且前面已经介绍过,
GetStockObject函数返回的是HGDIOBJ类型,而这里需要的是HBRUSH类型,因此需要进行强制转换。
接着,把新创建的透明画刷选入设备描述表中,并将返回的先前的画刷 CpOldBrush)保存起来。然
后,根据绘制类型绘制相应的图形:如果给制类型是1,贝IJ绘制一个点:如果是2,绘制一条直线:
如果是3,绘制一个矩形:如果是4,则绘制一个椭圆。在图形绘制完成之后,将先前的画刷 C
p01dBrush )重新选入设备描述表中。
如果读者是重新创建的一个工程,而不是在第 11章己有程序上继续添加功能,这时可以把第11章中
己有程序的菜单资源复制过来。因为对第 11章的程序来说,其所有资源都是保存在Graphic.rc这个
文件中。我们可以在VC++集成环境中,利用【文件\打开】菜单命令,打开文件打开对话框,找到这
个文件并打开,即可在 VC++编辑环境中看到该程序所有的资源。如图 13.16所示。
El 61 Graphic.rc
@臼Accelerator
ID D Bitmap
@口Oialog
ID D Icon
8 61 Menu
自·I'U.~'.!."I~Iil:fJ里!1:': I=-
ID 0 String T able
回oToolbar
因臼Version
图 13.16第 11章示例程序使用的资源
打开Menu分支,双击IDR MAINFRAME菜单资源,即可在资源编辑窗口打开这个菜单资源,在绘图子菜
单上单击鼠标右键,从弹出的快捷菜单中选择【复制】菜单项,然后在本章的工程中,切换到
ResourceView选项卡,打开IDR MAINFRAME菜单资源,在文件操作子菜单后单击鼠标右键,从弹出的
快捷菜单中选择【粘贴】菜单项,就完成了菜单资源的复制。在编程过程中,如果想复制资源的话,
都可以采用这种方式来实现,这样可以节省开发的时间。但是复制之后会有点问题:复制得到的这些
菜单项 E号可能不正确。不知道这是VC++的Bug呢,还是其他原因,笔者也没弄清楚。这时,我们只
有手工修改这些菜单项的 ID了。然后给绘图子菜单 F的四个菜单项分别添加命令响应,并添加
LBUTTONDOWN和LBUTTONUP消息的响应函数。最后,还可以复制己有的第 11章程序中相应函数的实现
代码。之后,可以调整一下代码,删除无用的代码。
"‘ I 509
第 13章文档与串市化
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -