📄 13.4.4 利用 cobarray类对串行化的支持保存和加载数据.txt
字号:
13.4.4 利用 CObArray类对串行化的支持保存和加载数据
CObArray类结合了 IMPLEMENT_SERIAL宏支持串行化,转储该集合类中的元素。也就是说, CObArray
这个类本身也支持串行化。下面就利用 CObArray类对串行化的支持,保存该数组中所有元素。
由于 CObArray类支持串行化,该类也应该有一个 Seria lize函数。 CGraphicDoc对象的 Serialize
函数是由框架调用的,在这个函数中,我们可以直接它的参数 ar C CArc hi ve类型〉传递给 CObArray
对象的 Serialize函数 (添加代码如例 13-24中加灰显示的那行代码所示)。注意,一定要把这句代
码调用放到 if/else ì语句的外面。
说明:首先请读者将 CGraphicDoc类的 Serialize函数中 if/else语句块内先
前添加的代码都注释起来。
例 13-24
void CGraphicDoc : : Serialize(CArchive& ar)
POSITION pos=GetFirstviewposition() ;
CGraphicView * pView=(CGraphicView*)Get N extView(pos) ;
if (ar . I sStoring())
{
// TODO : add storing code here
else
// TODO : add l oading code here pVieW->ffi_ obArray . Serialize(ar) ;
运行 Graphic程序,首先选择相应菜单绘制一些图形,接着单击【文件\保存】菜单命令,并选择一
个保存目标文件。之后关闭程序。然后再次运行程序,单击【文件\打开】菜单命令,并选择同一个
文件打开,然后在程序窗口中就可以看到刚才绘制的图形了。
下面让我们看看 MFC的源代码,了解 CObArray类实现串行化的过程。 CObArray类的 Serialize函
数的实现位于 ARRAY_O.CPP文件中,源代码如例 13-25所示。
19IJ 13-25
// Seriali zation
void CObArray : : Serialize(CArchive& ar)
ASSERT VALID(this) :
CObject::Serialize(ar);
if (ar.IsStoring ( ) )
ar.WriteCount(m_nSize) ;
for (int i = 0; i < m_nSize; i++)
ar << m_pData[i];
else
DWORD nOldSize = ar.ReadCount();
SetSize(nOldSize);
for (int i = 0; i < m_nSize; i++)
ar >>m_pData[i];
在上述例 13-25所示 CObArray类的 Serialize函数中,首先调用基类 (CObject)的 Serialize函
数,接着判断参数 CArchive对象的状态,如果是保存数据,则调用 WriteCount函数。在浏览程序
代码时,如果编码比较规范的话,对有些函数,就不用去看它是如何实现的,根据其名称大概就能
猜测到它的功能。像 WriteCount这个函数,我们一看就知道它是将数组中元素的数目写入到文件中。
接下来, CObArray类的 Serialize函数利用一个 for循环将数组中所有元素依次写入到文件中。可
以看到,这个 Serialize函数的这种调用与我们上面第一次编写的代码很类似,也是先将数组中的
元素个数写入文件,接着利用一个 for循环,依次取出数组中每一个元素,然后将它们写入文件。
同样地,当加载时,首先读取写入到文件中的元素数目,然后利用 for循环依次加载数组中每一个
元素。当然,对于每一个数组元素所代表的对象来说,本例是 CGraph类型的对象,它也会去调用
CGraph类的 Serialize函数。读者可以自己在 CObArray类的 Serialize函数处设置一个断点,然后
调试运行程序,看看其调用过程。
既然文档类是用来保存数据的,那么可以将 CObArray数组定义放到文档类中。对本例来说,也就是
为 CGraphicDoc添加 CObArray类型的成员变量: m_obArray,并将其访问权限设置为 public类型。
然后将程序中调用 CGraphicView类 m_obArray对象的地方都修改为调用 CGraphicDoc类的
m_obArray对象。这时就需要在 CGraphicView类中访问 CGraphicDoc类的成员,因此首先就要得到
文档类的指针。我们可以看到 CGraphicView类中有一个 GetDocument函数,其实现代码如例 13-26
所示。
例 13-26
CGraphicDoC* CGraphicView::GetDocurnent() // non-debug version is inline
ASSERT(rn-pDocurnent->IsKindOf(RUNTIME_CLASS(CGraphicDoC))) ;
return (CGraphicDoc*)rn-pDocurnent;
可以看到, CGraphicView类GetDocument函数的返回值就是CGraphicDoc类的指针,也就是说,视类
中已经为我们准备好了可以获得文档类指针的函数,可以直接获得指向文档类对象的指针。这就是
MFC为我们提供的文档/框架/视类结构,它将这几个类对象之间互相调用的机制都设计好了,我们只
需要遵照该机制进行调用,获得相应对象的指针即可。所以,在 CGraphicView类的函数中需要访问
文档类对象的成员时,可以直接定义一个 CGraphicDoc类型的指针,接着调用GetDocument函数,就
可以得到指向文档类对象的指针,然后利用这个指针就可以访问文档对象内部的成员了。于是,我
们修改上述例 13-21所示CGraphicView类OnLButtonUp函数的代码,将先前调用集合类对象的Add方
法的语句 (即①符号所在语句)注释起来,然后在其后添加下述语句:
CGraphicDoc食pDoc=GetDocument() ;
pDoc->~obArray.Add(pGraph);
在 CGraphicView类的 OnDraw函数中也要进行相应修改,我们可以回头看看上面例 13-23列出的
OnDraw函数的代码,可以看到, OnDraw函数中已经调用了 GetDocument函数得到文档类对象的指针。
前面的章节己经提过,在文档/视类结构中,文档是用来保存数据和加载数据的。而在窗口绘制时,
应用程序框架将调用OnDraw函数,并且认为数据应当是从文档类对象中获取到的,于是就自动添加
了获取文挡类对象指针的调用,之后就可以利用该文档类对象指针获取文档类对象的数据,然后在
视类窗口中显示这些数据。所以,我们就可以这样修改OnDraw函数的代码,将先前调用 CGraphicView
类m_obArray对象的地方均换成调用CGraphicDoc类的ffi_obArray对象,即修改后的OnDraw函数代码
如例 13-27所示。
例13-27
void CGraph工cView: : OnDraw(CDC* pDC)
CGraphicDoc* pDoc = GetDocument() ;
ASSERT_VALID(pDoc) ;
// TODO: add draw code for native data here
/ * CFont *pOldFont=pDC->SelectObject(&m_font) ; pDC->TextOut(O , O, m_ strFontName) ;
pDC->SelectObject(pOldFont) ;
食/
int nCount ;
// nCount=m_obArray .GetSize() ; nCount=pDoc->m_obArray .GetSize() ; for( i nt i=O ;
i<nCount ;工++)
// ( (CGraph*)m_obArray .GetAt(工))一>Draw (pDC) ;
((CGraph*)pDoc->m_obArray.GetAt(i) )->Draw(pDC) ;
~~~ 1517
第13
然后在上述例 13-24所示CGraphicDoc类的Serialize函数中,将先前我们自己添加的代码(即加灰显
示的那行代码〉注释起来。因为现在m_obArray变量就是CGraphicDoc类的成员变量,所以可以直接
使用。添加下面如例 13-28所示加灰显示的这条语句。
例 13-28
void CGraphicDoc : :Serialize(CArchive& ar) POSITION pos=GetFirstViewPosition();
CGraphicView *pView=(CGraphicView*)GetNextView(pos) ; if (ar . IsStoring()) / / TODO : add
storing code here else // TODO : add loading code here //pView->m_obArray .Serialize(ar) ;
m_obArray.Serialize(ar) ;
运行Graphic程序,绘制一些图形,然后保存。接着打开菜单项,选择同一个文件,可以看到先前绘
制的内容在窗口中显示出来了。
飞令·细iR眉让我们再回顾一下 DocumentJView结构:
( 1 )在 MFC中,文档类负责管理数据,提供保存和加载数据的功能。视类负责数据的显示,以及给
用户提供对数据的编辑和修改功能。
(2) MFC给我们提供 DocumentJView结构,将一个应用程序所需要的"数据处理与显示"的函数空壳都
设计好了,这些函数都是虚函数,我们可以在派生类中重写这些函数。有关文件读写的操作在
CDocument的 Serialize函数中进行,有关数据和图形显示的操作在CView的OnDraw函数中进行。我
们在其派生类中,只需要去关注Serialize和 OnDraw函数就可以了,其他的细节,我们不需要去理
会,程序就可以良好地运行。
(3)
在单击"文件\打开"菜单命令后,应用程序框架会激活"文件打开"对话框,让用户指定将要打开的文
件名,然后程序自动调用 CGraprucDoc类的 Seria1ize函数读取该文件。应用程序框架还会调用
CGraphicView类的OnDraw函数,传递一个显示DC,以便重新绘制窗口内容。
(4) MFC提供 DocumentJView结构,是希望程序员将精力放在数据结构的设计和数据显示的操作上,
而不要把时间和精力花费在对象与对象之间、模块与模块之间的通信上。
( 5 )一个文档对象可以与多个视类对象相关联,而一个视类对象只能与一个文档对象相关联。
518 I份'静,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -