1 实现类的串行化
实现类的串行化的有5步,
①从CObject类继承
②定义一个不带参数的构造函数
③在类的声明中用宏DECLARE_RESIAL(class name)生声明
④在类定义中用宏IMPLEMENT(class name, father class name, editon number) 声明
⑤实现Serialize()函数
eg.
//Example.h
class Example:public CObject //①
{
DECLARE_SERIAL(Example) //③
public:
Example(); //②
void Serialize(CArchive& ar); //⑤ 声明 定义在cpp文件中
private:
成员1;
成员2;
..........
成员n;
}
//Example.cpp
IMPLEMENT_SERIAL(Example, CObject, 1) //④ 参数中的1代表版本号,串行化该对象写的时候是几,
//那么在串行化读的时候也必须是 几。
void Example::Serialize(CArchive& ar) //⑤ Serialize函数的实现
{
if(ar.IsStoring) //如果是保存
{
ar<<成员1<<成员2<<......<<成员n;
}
else
{
ar>>成员1>>成员2>>......>>成员n;
}
}
2 串行化对象和CDocument/CView 结构的应用的例子
①类定义 CGraph //Graph.h
class CGraph:public CObject //继承自CObject类
{
DECLARE_SERIAL(CGraph) //宏声明
public:
CGraph(); //不带参数的构造函数
CGraph(UINT m_nDrawType, CPoint m_ptOrigin, CPoint m_ptEnd) : m_nDrawType(m_nDrawType), m_ptOrigin(m_ptOrigin), m_ptEnd(m_ptEnd){}
virtual ~CGraph();
void Draw(CDC *pDC); //画图函数
void Serialize(CArchive& ar);//串行化函数
private:
UINT m_nDrawType; //画图类型
CPoint m_ptOrigin; //起始点
CPoint m_ptEnd; //终止点
};
②类实现CGraph //Graph.cpp
#include "Graph.h"
IMPLEMENT_SERIAL(CGraph, CObject, 1) //宏声明
CGraph::CGraph() //不带参数的构造函数的实现
{
m_nDrawType = 0;
m_ptOrigin = 0;
m_ptEnd = 0;
}
//Draw函数功能:根据画图类型和起始点终止点画图
void CGraph::Draw(CDC *pDC)
{
//codes 省略代码,具体可见练习程序61
}
//Serialize函数功能:串行化读写类成员变量从而实现串行化读写类
void CGraph::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar<<m_nDrawType<<m_ptOrigin<<m_ptEnd;
}
else
{
ar>>m_nDrawType>>m_ptOrigin>>m_ptEnd;
}
}
③CMy61View类相关在这个类中有一个重要的成员变量CObArray obArray,指针链表。 用来保存每次在CMy61View类中画的图(即保存CGraph对象指针),CGraph中的数据成员用来保存能唯一确定这个图的元素。另外该类还有成员变量UINT m_nDrawType, CPoint m_ptOrigin.
void CMy61View::DrawPicture() { //codes 省略180字
CGraph *pGraph = new CGraph(m_nDrawType, m_ptOrigin, point);
obArray.Add(pGraph);//对象指针加入指针链表
}
④CMy61Doc类相关
主要是这个类的两个函数:一个是Serialize(), 另一个是DeleteContents()
前者实现打开/保存文件的串行化,后者实现释放资源(内存等)
void CMy61Doc::Serialize(CArchive& ar)
{
//POSITION pos = GetFirstViewPosition();
//CMy61View *pView = (CMy61View *) GetNextView(pos); //获得View类指针方法1
CMainFrame *pMainFram = (CMainFrame *) (AfxGetApp()->m_pMainWnd);
CMy61View *pView = (CMy61View*) (pMainFram->GetActiveView()); //获得View类指针方法2 ,详见上一篇文章
if (ar.IsStoring()) //保存
{
// TODO: add storing code here
int nCount = 0;
nCount = pView->obArray.GetSize(); //取得链表中对象个数
ar<<nCount; //先把个数写入
int i = 0;
for (i = 0; i < nCount; i ++)
{
ar<<(CGraph*)(pView->obArray.GetAt(i)); //把链表中各个对象的数据写入
//此时程序实际会转去执行void CGraph::Serialize(CArchive& ar)
}
}
else //打开
{
int nCount = 0;
ar>>nCount; //先读入个数
CGraph *pGraph = NULL;
int i = 0;
for (i = 0; i < nCount; i ++)
{
ar>>pGraph;
pView->obArray.Add(pGraph); //这里应该注意:打开之前有没有关闭或者删除obArray中的元素,如果没有则会在 //原 来的基础上增加元素,因为 这里的obArray和之前写的时候用的是同一个,因为pView指针是同一个。
}
}
//pView->obArray.Serialize(ar); 也可以if和else里都不写代码,用它来完成串行化读写
}
/释放资源///
void CMy61Doc::DeleteContents()
{
// TODO: Add your specialized code here and/or call the base class
POSITION pos = GetFirstViewPosition(); //从CDocument类中获得但前View类指针
CMy61View* pView = (CMy61View*) GetNextView(pos); //从CDocument类中获得但前View类指针
int nCount = pView->obArray.GetSize();
for (int i = 0; i < nCount; i ++)
{
delete pView->obArray.GetAt(i); //删除所指内存
}
pView->obArray.RemoveAll(); //这里要注意RemoveAll函数和RemoveAt函数的区别,RemoveAt删除前面元素后,后面的 //元素会前移。因此,如果在循环里删除元素要注意这点,可以考虑从最后一个元素开始删除。
CDocument::DeleteContents();
}
NOTE:
指定File/Open 或 File/Save 时,会去执行CDoc类的Serialize()函数,这个函数会根据所要串行化读写的对象转去执行执行该对象的串行化函数。 如果是Open操作,打开后会执行View类的OnDraw()函数以便重绘打开的内容显示在View类上。