串行化是什么?我们在编程中常常遇到这类问题,如何将对象保存到永久存储介质上(一般是硬盘),当程序下一次启动时在从永久存储介质上读取该对象。一般,要整个存储对象是不可能的,也没有必要,因为一个对象不仅有成员变量,也有成员方法,那我们要如何做到这一点,这就是这篇文章所讲的技术——串行化!
串行化是将对象的所有特征保存起来,以便下次读取该特征时可以将该对象还原出来。
一个对象要串行化,对应的类必须支持可串行化,支持可串行化的类必须有5个要求:(假设我们创建了CGraph类)
1.该类必须从CObject类继承;
2.重载Serialize(CArchive& ar)函数;
3.在类的声明处使用DECLARE_SERIAL(CGraph)声明该类可串行化;
4.定义一个不带参数的构造函数;
5.在源文件中使用IMPLEMENT_SERIAL(CGraph,CObject,1)实现该类可串行化;(1代表了版本号,写入对象和读取对象的版本号必须一致)
下面以一个实例讲解:
1.新建单文档工程,命名为Serialize;
2.在工程中添加新类CGraph,其基类为CObject,如下
class CGraph : public CObject //1.从CObject继承
{
public:
DECLARE_SERIAL(CGraph) //3.声明可串行化
CGraph(); //4.一个不带参数的构造函数,下面会介绍其用处
CGraph(CPoint OriginPoint,CPoint NewPoint,int GraphType);
void Serialize(CArchive& ar); //2.重载Serialize函数
virtual ~CGraph();
private:
CPoint OriginPoint;
CPoint NewPoint;
int GraphType;
public:
void OnDraw(CDC* pDC);
};
在该类的源文件中加入
IMPLEMENT_SERIAL(CGraph,CObject,1) //5.实现该类可串行化
3.在CGraph类中的带参数的构造函数中加入:
//OriginPoint,NewPoint,GraphType都是要存储和使用的成员变量
CGraph::CGraph(CPoint OriginPoint,CPoint NewPoint,int GraphType)
{
this->OriginPoint=OriginPoint;
this->NewPoint=NewPoint;
this->GraphType=GraphType;
}
在Serialize()中加入:
void CGraph::Serialize(CArchive& ar)
{
if(ar.IsStoring())
{
ar<<OriginPoint<<NewPoint<<GraphType;//存储成员变量
}
else
{
ar>>OriginPoint>>NewPoint>>GraphType;//打开成员变量
}
}
在自定义函数OnDraw()中加入:
//在视图类中以一定格式显示数据
void CGraph::OnDraw(CDC* pDC)
{
switch(GraphType)
{
case 1:
pDC->MoveTo(OriginPoint);
pDC->LineTo(NewPoint);
break;
case 2:
pDC->Rectangle(CRect(OriginPoint,NewPoint));
break;
default:
break;
}
}
4.在CSerializeView类中处理直线和矩形的绘制和显示:
void CSerializeView::OnLButtonDown(UINT nFlags, CPoint point)
{
// 记录鼠标的起点
OriginPoint=point;
CView::OnLButtonDown(nFlags, point);
}
void CSerializeView::OnLButtonUp(UINT nFlags, CPoint point)
{
// 鼠标释放时处理两点间的图形
CClientDC dc(this);
CBrush* pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//透明画刷
CBrush* pOldBrush=dc.SelectObject(pBrush);
switch(GraphType)
{
case 1:
dc.MoveTo(OriginPoint);
dc.LineTo(point);
break;
case 2:
dc.Rectangle(CRect(OriginPoint,point));
break;
default:
break;
}
CSerializeDoc* pDoc = GetDocument();//得到文档指针,所有数据存在文档的m_ObArray中
//必须在堆中建立数据,或其他长久保持数据的方法
CGraph* pGraph=new CGraph(OriginPoint,point,GraphType);
pDoc->m_ObArray.Add(pGraph);
dc.SelectObject(pOldBrush);
CView::OnLButtonUp(nFlags, point);
}
void CSerializeView::OnLine()
{
// 直线菜单命令
GraphType=1;
}
void CSerializeView::OnRentangle()
{
// 矩形菜单命令
GraphType=2;
}
在该类的OnDraw()函数中加入:
void CSerializeView::OnDraw(CDC* pDC)
{
CSerializeDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CBrush* pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush* pOldBrush=pDC->SelectObject(pBrush);
for(int i=0;i<pDoc->m_ObArray.GetSize();i++)
((CGraph*)pDoc->m_ObArray.GetAt(i))->OnDraw(pDC);//调用CGraph类的显示函数
pDC->SelectObject(pOldBrush);
// TODO: 在此处为本机数据添加绘制代码
}
5,最后在CSerializeDoc类中的Serialize()加入:
void CSerializeDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
int len=m_ObArray.GetSize();
ar<<len;
for(int i=0;i<len;i++)
ar<<m_ObArray.GetAt(i);//存储CGraph对象,相当于调用CGraph::Serialize(CArchive& ar)的ar<<;
}
else
{
m_ObArray.RemoveAll();
int len;
ar>>len;
CGraph* pGraph;
for(int i=0;i<len;i++)
{//这里只定义指针,便可以接收对象数据,正是有不带参数的构造函数的功劳
//在执行ar>>pGraph;时自动为批pGraph分配内存,每次都重新分配,便可以打开所有对象数据了
ar>>pGraph;//打开CGraph对象,相当于调用CGraph::Serialize(CArchive& ar)的ar>>;
m_ObArray.Add(pGraph);
}
}
}
其中m_ObArray是CObArray类变量,它和CPtrArray很相识。
这样,便可以完成存储和加载对象的功能了。