⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 11.2.1 集合类cptrarray.txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
字号:
11.2.1 集合类 CPtrArray
前面已经介绍过 MFC提供的一个集合类: CStringArray,它可以用来存储 CString类型的对象,而且
它的容量是可以动态增加的。这里再为读者介绍一个集合类: CPtArray,它支持 void类型的指针
数组,该类的成员函数与 CObArray类的相应函数类似,只是 CObArray类的成员函数中使用 CObject
指针作为参数或返回值类型的地方,在 CPtArray类中都用 void类型的指针代替。因此在程序中,
我们可以利用 CptArray对象存储多个对象的地址,如果想要增加一个成员,可以调用其 Add方法,
来增加一个 void指针所指向的对象:如果想取得这个集合类中的某个元素,可以调用其 GetAt方法:
如果想获得这个集合类的元素数目,可以调用其 GetSize方法。
下面,我们首先为 Graphic程序的 CGraphicView类增加一个 CPtrArray类型的私有成员变量: 
m_ptrArray。然后在每次绘图操作结束后,就构造一个相应的 CGraph对象,并将该对象的地址保
存到 m_ptrArray对象中。也就是在 CGraphicView类的 OnLButtonUp函数中添加下述如例 11-3
所示代码中加灰显示的代码。 
例 11-3 

void CGraphicView::OnLButtonUp(UINT nFlags , CPoint point) 
// TODO: Add your rnessage handler code here and/or call default CClientDC dc(this); CPen 
pen(m_nLineStyle,m_nLineWidth,rn_clr); dc.SelectObject(&pen); CBrush 食 
pBrush=CBrush::FrornH andle((HBRUSH)GetStockObject(NULL_ 
BRUSH) ) ; dc.SelectObject(pBrush) ; 
switch(rn_nDrawType) 
case 1: 
dc.SetPixel(point, m_clr); 
break; 
case 2: 
dc.MoveTo(rn-ptOrigin) ; 
dc.LineTo(point); 
break; 

case 3: 
dc.Rectangle(CRect(rn-ptOrigin, point)) ; 
break; 

case 4: 
dc.Ellipse(CRect(rn-ptOrigin, point)) ; 
break; 


CGraph graph(m_nDrawType , rn-ptOrigin, point); 
m_ptrArray.Add(&graph) ; 

CView: 	: OnLButtonUp(nFlags , point); 
因为在上述如例 11-3所示的CGraphicView类的OnLButtonUp函数中使用了CGraph类,所以在
GraphicView.cpp文件的前部应添加下述语旬,以便将CGraph类的定义包含进来: 
#include . Graph.h" 
接下来就可以在OnDraw函数中将保存的图形元素再次显示出来,具体代码如例 11-4所示。
例 11-4 

void CGraphicView : : OnDraw(CDC* pDC) 
CGraphicDoc* pDoc = GetDocurnent() ; 
ASSERT_VAL工o (pDoc) ; 
// TODO : add draw code for native data here 

CBrush *pBrush=CBrush: : FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)) ; 
pDC->SelectObject(pBrush) ; 
for(int i=0 ;i<m-ptrArray .GetSize();i++) 
{ 
switch(((CGraph*)m_ptrArray .GetAt(i))->m_nDrawType) 
case 1: 
pDC->SetPixel( ((CGraph*)m_ptrArray . GetAt(i))->m-ptEnd, RGB (0 , 0 , 0)); break; 
case 2: 
pDC->MoveTo(((CGraph* )m-ptrArray . GetAt(i))->m-ptOrigin) ; 
pDC ->LineTo(((CGraph* )m-ptrArray . GetAt(i))->m-ptEnd) ; break; 
case 3: 
pDC->Rectangle(CRect(((CGraph*)rn-ptrArray .GetAt(i)) ->m_ptOrigin, ( (CGraph*)m-ptrArray.GetAt(i) ) ->m-pt End) ) ; 
break; 
case 4: 
pDC->Ellipse(CRect(((CGraph*)m-ptrArray .GetAt(i))->m-ptOrigin, ((CGraph*)m-ptrArray . GetAt(i) )->m-ptEnd) ) ; 
break; 
在上述如例 11-4所示代码中,首先创建一个透明的画刷 (pBrush),并将该画刷选入设备描述表中。
接着利用一个 for循环,将集合类对象 m_ptrArray)中保存的图形对象取出来,然后利用
switch/case语句根据所保存的绘图类型来绘制相应的图形。因为集合类的 GetAt方法返回的是
void*类型,所以应将其强制转换为CGraph类型的指针。 
Build并运行Graphic程序,并利用相应菜单项在窗口中绘制一些图形,但是当窗口尺寸发生变化时,
会发现这些图形仍消失了,井未如我们所期望的那样在窗口中再次出现。这时可能出现问题的地方
有两处,一是在OnDraw函数中绘制时没有取出相应的图形元素,另一个地方是在保存图形元素时出
现了问题。我们可以检查一下代码,会发现前一种可能不存在,而在保存图形元素时,即在上述如
例 11-3所示CGraphicView类的OnLButtonUp函数中,定义的CGraph类型的对象: graph是一个局部变
量,当调用CPtrArray类的Add方法后,这个局部对象的地址就被保存到 m_ptrArray集合类对象中,
这一过程如图 1 1.5所示。 
图 11.5将局部CGraph对象在校中的地址保存到集合类对象中的过程

当OnLButtonUp函数执行完成之后, graph这个局部对象就会发生析构,其所占内存就被回收了,也
就是说该graph对象在内存中就不存在了。虽然这时在m_ptrArray集合对象中仍保存了这个graph
对象先前在内存中的地址,但是这个对象本身已经不存在了。
这就好像有一个人去放马,累了想休息一下,于是就手拿缰绳在大树下睡着了,但是这匹马却趁主
人睡着的时候,将缰绳咬断后跑了。当主人醒来之后,虽然手上还拿着缰绳,但是马已经不在了。
这与我们这里的情况非常类似,虽然m_ptrArray集合对象仍然保存着 graph对象先前的内存地址,
但该地址处的对象已经不存在了。在上述例 11-4所示的 OnDraw函数中, GetAt函数实际上是从
m_ptrArray集合对象中取出其所保存的地址,但是原先在这个地址处的对象已经不存在了,因此
得到的并不是先前保存的那个graph对象。
为了解决这一问题,应该在如例 11-3所示CGraphicView类OnLButtonUp函数中,把定义的CGraph类
型的对象修改为CGraph指针类型的变量,修改后的代码如下所示(即如例 11-3所示代码中加灰显示
的那两行代码用下面这两行代码代替): 
CGraph *pGraph=new CGraph (m_nDrawType , m_ptOrigin , point) ; 
m_ptrArray.Add(pGraph) ; 

因为pGraph变量是在OnLButtonUp函数中定义的,所以它也是一个局部变量,系统将在Graphic程序
的战中为该变量分配一个内存。图 11.6是这时的内存示意图,图中假定 pGraph变量所在内存地址
为: OxOO12岛c4。然后利用new操作符构造一个CGraph对象,并将该对象赋给pGraph这个变量。我们
知道凡是用 new分配内存的对象均是在堆中定义
的。也就是说,现在 OxOO346708。  pGraph这个变量就保存了 CGraph对象在堆中内存的首地址: 
图 11.6将CGraph对象在堆中的地址保存到集合类对象中的过程

接下来利用集合类的 Add方法将 pGraph这个变量的值保存起来了,这个值就是 CGraph对象在堆中
的内存地址: Ox00346708。因为对在堆中分配的对象来说,如果程序中不显式地析构该对象,那么
该对象的生命周期将与应用程序的生命周期是一致的。当 OnLButtonUp函数执行完成之后, pGraph
是一个局部变量,所以它的内存要被回收,但是它所指向的对象的值,即我们用new操作符在堆中所
分配的对象的内存地址:OxOO346708 , 己经被保存到集合类的对象ffi_p位Aηay中了,所以这时仍然
可以通过这个地址索引到相应
的CGraph对象。这一过程的内存示意图如 11.7所示。 
图 11.7局部pGraph对象析构后,集合类对象仍能根据所保存的地址值索引到正确的对象

再次运行Graphic程序,并利用相应菜单项在窗口中绘制一些图形,当窗口尺寸发生变化时,将会发
现所绘制的图形始终会在窗口中显示出来。通过这个例子的学习,希望读者一定要注意对象和指针
类型之间的区别。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -