📄 11.2.1 集合类cptrarray.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 + -