您好,欢迎访问三七文档
第5章MFC集合类许多C++程序员都使用标准模板库(STL),因为用它很容易实现数组、链接列表、映射以及其他容器。STL语言中“容器”指的是保存数据集合的对象。但是在有STL之前,已经有MFC了。在称为“MFC集合类”的一系列类中,MFC提供了自己的数组、链接列表以及映射的实现途径。虽然在MFC应用桎序中使用STL类非常安全,但许多MFC程序员还是更喜欢用MFC的集合类,一方面原因是他们更熟悉MFC,另一方面原因是不愿意链接两个独立的类库而增加应用程序的EXE的尺寸。有MFC集合类的帮助,您根本不必从头编写一个链接列表。本章将介绍MFC集合类并深入说明它们的使用和操作。5.1数组C和C++的一个最大缺陷是数组不进行边界检查。看一下下面的代码,它反映了C和C++应用程序中最常见的一种错误:intarray[10];for(inti=0;i=10;i++)array[i]=i+1;此代码出错是由于for循环中的最后一次迭代赋值超出了数组的范围。在运行时会产生非法存取错误。,C++程序员经常通过编写数组类并在内部进行边界检查来解决此问题。下面给出的数组类具有Get和Set函数,用来检查传递给它们的下标,如果传递来的下标无效就进行断言处理:classCArray{protected:intm_nSize;//Numberofelementsinthearray.int*m_pData;//Wherethearray'selementsarestored.public:CArray(intnSize){m_nSize=nSize;m_pData=newint[nSize];}~CArray(){m_nSize=0;if(m_pData!=NULL){delete[]m_pData;m_pData=NULL;}}intGet(intnIndex){assert(nIndex=0&&nIndexm_nSize);returnm_pData[nIndex];}voidSet(intnIndex,intnVal){assert(nIndex=0&&nIndexm_nSize);m_pData[nIndex]=nVal;}};用这个简单的类作为整型数组的容器,下列代码在最后一次调用Set时产生断言提示:CArrayarray(10);for(inti=0;i=10;i++)array.Set(i,i+1);//Assertswheni==10.这样就会避免非法存取错误的发生。5.1.1MFC数组类您不必亲自编写数组类,MFC巳经提供了各种各样的数组。首先是一般的CArray类。它实际上是一个模板类,利用它可以创建任何数据类项的类型安全数组。在头文件Afxlexnpl.h中定义了CArray。其次是非模板化的数组类,分别为保存特定类型的数据而设计。这些类在Afxcoll.h中定义。表5-1中列出了非模板化的MFC数组类以及它们所保伴的数据类型。表5-1恒定类型的MFC数组类类名数据类型CByleArray8位字节(BYTE)CWordArray16位字节(WORD)CDWoKlArray32位双字(DWORD)CUInlArray无符号整型(UINT)CStringArrayCStringCPlrAmyvoid指针CObArrayCObject指针只要学会使用这些数组类中的一种,也就学会使用其他数组类,因为它们共享公用的一组成员函数。下例声明了一个包含10个UINT的数组并用数字1到10对它进行了初始化:CUIntArrayarray;array.SetSize(10);for(inti=0;i10;i++)array[i]=i+1;可以采用相同的方法来声明一个CStrings数组并用整数I到10的文本表示来初始化它:CStringArrayarray;array.SetSize(10);for(inti=0;i10;i++){CStringstring;string.Format(_T(%d),i);array[i]=string;}在两个例子中,都是用SetSize来指定数组包含10个元素;重载“[]运算符调用数组的SetAt函数,该函数将值复制到数组中指定位置处的元素中;如果数组边界非法,程序将执行断言处理。边界检査内置在SetAt代码中:ASSERT(nIndex=0&&nIndexm_nSize);在MFC源程序文件Afxcoll.inl中您可以看到此代码。可以使用InsertAt函数在不覆盖已有数组项的情况下给数组插入元素项。与SetAt不同,它只是给已存在的数组元素赋值,lnSertAt还要给新的元素分配空间,通过把数组中插入点上方的元素向上移动来完成。下列语句用数字1到4和6到10初始化一个数组并在数字4和6之间插入5:CUIntArrayarray;array.SetSize(9);for(inti=0;i4;i++)array[i]=i+1;for(i=4;i9;i++)array[i]=i+2;array.InsertAt(4,5);//Inserta5atindex4.还可以给InsertAt传递第三个参数指定元素项被插入的次数,或是在第二个参数中传递指向另一个数组对象的指针来插入整个数组。注意在本例中数组的大小为9个元素而不是10个,而在调用InsertAt时却没有执行断言处理。这是因为lnsertAt是那些便于使用的函数之一,它们在新的元素项添加到数组中时自动增大数组尺寸。动态调整数组大小将在下一节讨论。使用标准数组寻址语法可以在MFC数组中检索所要的值,下例将读取先前例子中写入CUInlArray中的UINT:for(inti=0;i10;i++)UINTnVal=array[i];使用此方法,[]运算符将调用数组的函数,该函数从数组中的指定位置取回一个值(当然要进行边界检査)。如果愿意您可以直接调用GetAt而不用通过[]运算符。要确定数组包含元素的个数,可以调用数组的GetSize函数。还可以调用GetUpperBound返回数组的上界下标,因为下标从0开始,所以其值为数组元素总数减1MFC的数组类为从数组中删除元素提供了两个函数:RemoveAt和RemoveAll。RemoveAt从数组中删除一个以上的元素项并将被删除元素项上边的所有元素项向下移动。RemoveAll清空整个数组。两个函数都将调整数组的上界从而反映出被删除的元素项个数.说明如下://Add10items.CUIntArrayarray;array.SetSize(10);for(inti=0;i10;i++)array[i]=i+1;//Removetheitematindex0.array.RemoveAt(0);TRACE(_T(Count=%d\n),array.GetSize());//9left.//Removeitems0,1,and2.array.RemoveAt(0,3);TRACE(_T(Count=%d\n),array.GetSize());//6left.//Emptythearray.array.RemoveAll();TRACE(_T(Count=%d\n),array.GetSize());//0left.Remove函数删除元素,但是如果元素是指针时它并不删除指针所指的对象。如果数组是CPtrArray或CObArray类型的,要清空数组并删除被删除指针所指的对象,就不应该写成:array.RemoveAll();要写成:intnSize=array.GetSize();for(inti=0;inSize;i++)deletearray[i];array.RemoveAll();如果对地址保存在指针数组中的对象删除失败,就会导致内存漏损。对保存指针的MFC列表和映射也是这样口5.1,2动态调整数组大小除了可以边界检査外,MFC数组类还支持动态调整大小。由于为保存数组元素而分配的内存可以根据元素的添加或删除而增大或缩小,所以没必要事先预见动态调整尺寸的数组具有多少元素。一种动态增大MFC数组的方法是调用SetSize。可以在任何需要的时候调用SetSize来分配额外的内存。假设开始时给数组设置了10个元素项,后来却发现需要20个。这时只要第二次调用SetSize给额外的项分配空间即可://Add10items.CUIntArrayarray;array.SetSize(10);for(inti=0;i10;i++)array[i]=i+1;//Add10more.array.SetSize(20);for(i=10;i20;i++)array[i]=i+1;用此方法调整数组大小时,原来的项仍旧保持它们的值。因此,在调用SetSize之后只有新项需要明确的初始化。另一种增大数组的方法是调用SetAtGrow而不是SetAt来添加元素项。例如:下列代码试图用SetAt给UINT数组添加10个元素项:CUIntArrayarray;for(inti=0;i10;i++)array.SetAt(i,i+1);此程序会在第一次调用SetAt时就执行断言处理。为什么?因为数组大小为零时(注意没有调用SetSize)不会自动增大数组来容纳新的元素。但是,将SetAt更改为SetAtGrow后,程序将顺利执行:CUIntArrayarray;for(inti=0;i10;i++)array.SetAtGrow(i,i+1);与SetAt不同,SetAtGrow会在必要时自动增大数组的内存分配空间。Add函数也是这样,它将元素项添加到数组的末尾。下一个例子的功能与上一个的相同,只是使用了Add而不是SetAtGrow来给数组添加元素:CUIntArrayarray;for(inti=0;i10;i++)array.Add(i+1);其他可以自动增大数组来容纳新的元素项的函数还包括:InsertAt,Append(将一个数组附加给另一个数组)以及Copy,顾名思义,它将一个数组复制到另一个数组中。MFC增大数组是通过分配新的内存缓冲区并将元素项从旧缓冲区中复制过去来实现的。如果由于内存不足使数组增大操作失败,则MFC会产生异常事件。为了在错误产生时捕获它们,要在try模块中封存扩大数组的调用命令,同时添加一个处理CmenoryExceptions的catch处理程序:try{CUIntArrayarray;array.SetSize(1000);//MightthrowaCMemoryException.}catch(CMemoryException*e){AfxMessageBox(_T(Error:Insufficientmemory));e-Delete();//Deletetheexceptionobject.}catch处理程序显示一个出错消息,警告用户系统内存不足。在现实世界中,要想成功地跳出这种内存不足的状态还要更进一步的处理。由于每当数组尺寸增加时都要分配新的内存,所以太频繁地增大数组会对操作产生不好的影响并有可能导致产生内存碎片。考虑如下程序片段:CUIntArrayarray;for(inti=0;i100000;i++)array.Add(i+1);这些语句看上去非常正确,但它们效率却不高,要申请分配成下上万个独立的内存。这也正是MFC让您在SetSize中可选的第二个参数内指定“增加量”的原因。下列代码更有效地初始化了一个数组,它告诉MFC在需要申请更多的内存时为10000个新的UINT分配空间:CUIntArrayarray;array.SetSize(0,10000);for(inti=0;i100000;i++)array.Add(i+1);当然,要是预先能给100000个元素项分配空间,那么程序的效率会更高一些。但更经常的是不可能事先预见到数组要保存元素的数量。如果能预计到要给数组增加许多元素却不能确定到底会需要多少空间,那么指定大的增加量
本文标题:第5章MFC集合类
链接地址:https://www.777doc.com/doc-2195893 .html