怎么用CArray类删除最后一个元素?
array也就是数组,要删除数组中的某一个元素,由于数组的连续性,不可能直接删除掉对应的存储空间,所以唯一的方法就是用数组后续元素覆盖要删除的元素。即从要删除元素下一个元素开始,一直到数组结束,每个元素均向前移动一位。
以下是删除一个数组中某个元素的模板函数:
template int remove_from_array(T *a, int n, int &l)//从数组a中删除第n个元素。l为数组原始长度,引用传参,所以可以将新的长度带回主调函数。如果成功,函数返回0,否则函数返回-1。 { if(a == NULL || n=l)return -1; int i; for(i = n; i < l-1; i ++) { a[i] = a[i+1];//依次用后续一个元素覆盖前一个元素,直到数组尾。 } l--; return 0; }
CArray的 用法
需要包含的头文件
CArray类支持与C arrays相似的数组,但是必要时可以动态压缩并扩展。数组索引从0开始。可以决定是固定数组上界还是允许当添加元素时扩展当前的边界。内存对上界是连续地分配空间,甚至一些元素可为空。
和C arrays一样,CArray索引元素的访问时间是不变的,与数组大小无关。
提示:
在使用一个数组之前,使用SetSize建立它的大小和为它分配内存。如果不使用SetSize,则为数组添加元素就会引起频繁地重新分配和拷贝。频繁地重新分配和拷贝不但没有效率,而且导致内存碎片。
如果需要一堆数组中的个别数据,必须设置CDumpContext对象的深度为1或更大。
此类的某成员函数调用全局帮助函数,它必须为CArray的大多数使用而定制。请参阅宏和全局量部分中的“类收集帮助器”。
当从一个CArray对象中移去元素时,帮助函数DestructElements被调用。
当添加元素时,帮助函数ConstructElements被调用。
数组类的派生与列表的派生相似。
MFC提供了一套模板库,来实现一些比较常见的数据结构如Array,List,Map。CArray即为其中的一个,用来实现动态数组的功能。CArray是从CObject派生,有两个模板参数,第一个参数就是CArray类数组元素的变量类型,后一个是函数调用时的参数类型。有一个类 class Object,要定义一个Object的动态数组,那么可以用以下两种方法:
CArray Var1;
CArray Var2;
Var2的效率要高。
先了解一下CArray中的成员变量及作用。TYPE* m_pData; // 数据保存地址的指针
int m_nSize; // 用户当前定义的数组的大小
int m_nMaxSize; // 当前实际分配的数组的大小
int m_nGrowBy; // 分配内存时增长的元素个数
构造函数,对成员变量进行了初始化。
CArray::CArray()
{
m_pData = NULL;
m_nSize = m_nMaxSize = m_nGrowBy = 0;
}
SetSize成员函数是用来为数组分配空间的。SetSize的函数定义如下:
void SetSize( int nNewSize, int nGrowBy = -1 );
nNewSize 指定数组的大小
nGrowBy 如果需要增加数组大小时增加的元素的个数。
对SetSize的代码,进行分析。
void CArray::SetSize(int nNewSize, int nGrowBy)
{
if (nNewSize == 0)
{
// 第一种情况
// 当nNewSize为0时,需要将数组置为空,
// 如果数组本身即为空,则不需做任何处理
// 如果数组本身已含有数据,则需要清除数组元素
if (m_pData != NULL)
{
//DestructElements 函数实现了对数组元素析构函数的调用
//不能使用delete m_pData 因为我们必须要调用数组元素的析构函数
DestructElements(m_pData, m_nSize);
//现在才能释放内存
delete[] (BYTE*)m_pData;
m_pData = NULL;
}
m_nSize = m_nMaxSize = 0;
}
else if (m_pData == NULL)
{
// 第二种情况
// 当m_pData==NULL时还没有为数组分配内存
//首先我们要为数组分配内存,sizeof(TYPE)可以得到数组元素所需的字节数
//使用new 数组分配了内存。注意,没有调用构造函数
m_pData = (TYPE*) new BYTE[nNewSize * sizeof(TYPE)];
//下面的函数调用数组元素的构造函数
ConstructElements(m_pData, nNewSize);
//记录下当前数组元素的个数
m_nSize = m_nMaxSize = nNewSize;
}
else if (nNewSize <= m_nMaxSize)
{
// 第三种情况
// 这种情况需要分配的元素个数比已经实际已经分配的元素个数要少
if (nNewSize > m_nSize)
{
// 需要增加元素的情况
// 与第二种情况的处理过程,既然元素空间已经分配,
// 只要调用新增元素的构造函数就Ok
ConstructElements(&m_pData[m_nSize], nNewSize-m_nSize);
}
else if (m_nSize > nNewSize)
{
// 现在是元素减少的情况,我们是否要重新分配内存呢?
// No,这种做法不好,后面来讨论。
// 下面代码释放多余的元素,不是释放内存,只是调用析构函数
DestructElements(&m_pData[nNewSize], m_nSize-nNewSize);
}
m_nSize = nNewSize;
}
else
{
//这是最糟糕的情况,因为需要的元素大于m_nMaxSize,
// 意味着需要重新分配内存才能解决问题
// 计算需要分配的数组元素的个数
int nNewMax;
if (nNewSize < m_nMaxSize + nGrowBy)
nNewMax = m_nMaxSize + nGrowBy;
else
nNewMax = nNewSize;
// 重新分配一块内存
TYPE* pNewData = (TYPE*) new BYTE[nNewMax * sizeof(TYPE)];
//实现将已有的数据复制到新的的内存空间
memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));
// 对新增的元素调用构造函数
ConstructElements(&pNewData[m_nSize], nNewSize-m_nSize);
//释放内存
delete[] (BYTE*)m_pData;
//将数据保存
m_pData = pNewData;
m_nSize = nNewSize;
m_nMaxSize = nNewMax;
}
}
下面是ConstructElements函数的实现代码template
AFX_INLINE void AFXAPI ConstructElements(TYPE* pElements, int nCount)
{
// first do bit-wise zero initialization
memset((void*)pElements, 0, nCount * sizeof(TYPE));
for (; nCount--; pElements++)
::new((void*)pElements) TYPE;
}
ConstructElements是一个模板函数。对构造函数的调用是通过标为黑体的代码实现的。可能很多人不熟悉new 的这种用法,它可以实现指定的内存空间中构造类的实例,不会再分配新的内存空间。类的实例产生在已经分配的内存中,并且new操作会调用对象的构造函数。因为vc中没有办法直接调用构造函数,而通过这种方法,巧妙的实现对构造函数的调用。
再来看DestructElements 函数的代码template
AFX_INLINE void AFXAPI DestructElements(TYPE* pElements, int nCount)
{
for (; nCount--; pElements++)
pElements->~TYPE();
}
DestructElements函数同样是一个模板函数,实现很简单,直接调用类的析构函数即可。
如果定义一个CArray对象 CArray myObject ,对myObject就可象数组一样,通过下标来访问指定的数组元素。
CArray[]有两种实现,区别在于返回值不同。
template
AFX_INLINE TYPE CArray::operator[](int nIndex) const
{ return GetAt(nIndex); }
template
AFX_INLINE TYPE& CArray::operator[](int nIndex)
{ return ElementAt(nIndex); }
前一种情况是返回的对象的实例,后一种情况是返回对象的引用。分别调用不同的成员函数来实现。
TYPE GetAt(int nIndex) const
{ ASSERT(nIndex >= 0 && nIndex < m_nSize);
return m_pData[nIndex]; }
TYPE& ElementAt(int nIndex)
{ ASSERT(nIndex >= 0 && nIndex < m_nSize);
return m_pData[nIndex]; }
除了返回值不同,其它都一样.
CArray arrInt;
arrInt.SetSize(10);
int n = arrInt.GetAt(0);
int& l = arrInt.ElementAt(0);
cout << arrInt[0] <<endl;
n = 10;
cout << arrInt[0] <<endl;
l = 20;
count << arrInt[0] << endl;
结果会发现,n的变化不会影响到数组,而l的变化会改变数组元素的值。实际即是对C++中引用运算符的运用。
CArray下标访问是非安全的,它并没有超标预警功能。虽然使用ASSERT提示,但下标超范围时没有进行处理,会引起非法内存访问的错误。
Add函数的作用是向数组添加一个元素。下面是它的定义: int CArray::Add(ARG_TYPE newElement).Add函数使用的参数是模板参数的二个参数,也就是说,这个参数的类型是我们来决定的,可以使用Object或Object&的方式。熟悉C++的朋友都知道,传引用的效率要高一些。如果是传值的话,会在堆栈中再产生一个新的对象,需要花费更多的时间。
template
AFX_INLINE int CArray::Add(ARG_TYPE newElement)
{
int nIndex = m_nSize;
SetAtGrow(nIndex, newElement);
return nIndex;
}
它实际是通过SetAtGrow函数来完成这个功能的,它的作用是设置指定元素的值。
template
void CArray::SetAtGrow(int nIndex, ARG_TYPE newElement)
{
if (nIndex >= m_nSize)
SetSize(nIndex+1, -1);
m_pData[nIndex] = newElement;
}
SetAtGrow的实现也很简单,如果指定的元素已经存在,就把改变指定元素的值。如果指定的元素不存在,也就是 nIndex>=m_nSize的情况,就调用SetSize来调整数组的大小
首先定义
CArray arryPChar;
这里以定义char*的为例子。
接下来我们来熟悉CArray这个类里的函数。
INT_PTR GetCount() const;
获得当前这个数组有多少个元素。
void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1);
设置数组的大小。
TYPE& GetAt(INT_PTR nIndex);
void SetAt(INT_PTR nIndex, ARG_TYPE newElement);
获得/设置序列的元素
INT_PTR Add(ARG_TYPE newElement);
在数组的末尾添加一个元素,数组的长度加1。如果之前使用SetSize是nGrowBy大于1,则内存按照nGrowBy增加。函数返回newElement的数组元素索引
void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1);
从指定的nIndex位置开始,删除nCount个数组元素,所有元素自动下移,并且减少数组的上限,但是不释放内存。这里我们自己手动的申请的就必须自己释放。new对应delete相信大家都知道的。
void RemoveAll();
从数组中移除素有的元素,如果数组为空,该行数也起作用。
INT_PTR Append(const CArray& src);
将同个类型的一个数组A附加到本数组的尾部,返回A第一数组元素在本数组的索引。
void InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount = 1);
void InsertAt(INT_PTR nStartIndex, CArray* pNewArray);
在指定的nIndex或者nStartIndex位置插入nCount个newElement数组元素或者pNewArray数组
下面是我应用的实例:
view plaincopy to clipboardprint?
CArray arrPChar;
//初始化元素
arrPChar.SetSize(10);
for (int i=0;i<10;i++)
{
char *aChar=new char[10];
strcpy_s(aChar,10,"hello arr");
arrPChar.SetAt(i,aChar);
}
//在数组的末尾插入一个元素
char *bChar = new char[10];
strcpy_s(bChar,10,"asdfefdsd");
arrPChar.Add(bChar);
//在索引2的位置插入一个元素,即在第三位插入一个元素
char *cChar=new char[5];
strcpy_s(cChar,5,"aidy");
arrPChar.InsertAt(2,cChar);
for (int j=0;j<arrPChar.GetCount();j++)
{
TRACE("%d,%s
",j,arrPChar.GetAt(j));
}
//删除数组里的所有元素,要释放内存,如果单单Remove的话则内存不会被释放
//这里因为使用RemoveAll的话内存无法被释放,所以没有给实例。
int count = arrPChar.GetCount();
for (int k=0; k<count; k++)
{
char *dChar=arrPChar.GetAt(0);
arrPChar.RemoveAt(0);
delete dChar;
}
你忘记了在C和C++中数组下标从0开始了吧^_^
GetCount()得到CArray中存在元素的个数。
GetSize()得到CArray实际分配的内存容量(多少个元素),往往比元素个数多几个,用以添加元素时避免频繁的内存申请。因此计算元素个数时应该用GetCount()
m_array.RemoveAt(m_array.GetSize() - 1);,
up
mfc+怎么没有addarray函数?
MFC是Microsoft Foundation Class库的缩写,是微软公司为Visual C++开发者提供的一组C++类库。在MFC中并没有预定义的”如果您想在MFC中添加一个数组,您可以使用CArray类。CArray是MFC中提供的一个模板类,可用于存储和管理数组。您可以使用CArray类的Add()函数来添加元素到数组中。如下示例代码:C...
VC6.0 CArray 多层应用问题
你不提供赋值构造函数的话,C++编译器将会默认按位拷贝,而Add(line)却是需要深拷贝,那么CArray将会无法工作。如果是这种情况,一般用指针,在Carray外部保证成员的正确性,(很多结构或类不是自己写的,很难避免以上问题出现)以上可以修改为 CArray<CAirLine , CAirLine &> m_lines;CAirLine line = ...
CArray<int,int>&r 请问表示的是什么意思啊?
CArray是个模板类,其中成员变量、函数可用到模板形参表中的任一类型 template<class FirstType,class SecondType> class CArray { public:FirType elem1;SecType elem2;};则声明CArray<int,int>就相当于 class CArray { public:int elem1;int elem2;};CArray<int,int>&r就是一个CArray类型...
Visual C++ 中CArry属于哪一个类
CArray 是从CObject继承来的。这是MFC里边的一个类,查MSDN就可以查得到。需要包含的头文件 <afxtempl.h> CArray类支持与C arrays相似的数组,但是必要时可以动态压缩并扩展。数组索引从0开始。可以决定是固定数组上界还是允许当添加元素时扩展当前的边界。内存对上界是连续地分配空间,甚至一些元素可为...
C++中CArray作为形参类型的问题
看信息的后半句,no copy constructor available for class 'CArray<int,int>'编译器并不会为CArray<int,int>生成拷贝构造函数.解决的方法有两种,一是把参数改成指针类型,二是你自己从CArray派生一个类并实现拷贝构造函数.
c++用类构建一个电话本
可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。同样,其他的 MFC container...
本人小白,想在MFC程序中用数组存储数据,应该如何定义数组以及 操作代码...
基本数据当然是用CString类啦。。就是字符串~用_T("")宏来赋值,在unicode的项目里也可以使用。然后你include <vector> 代码里就可以用std::vector<CString> XXX来当数组使用了。使用方法很简单,直接使用XXX[0],XXX[1],XXX[2]取数组元素,往里放XXX.pushback(CString);下面给你写个例子 这里...
C++做一个简单类
这样做:class CArrayInt\/\/类 { public:CArrayInt(int size);int operator[](int index);int GetMax();~CArrayInt();private:int m_iSize;\/\/数组大小 int* m_pData;\/\/数组数据 };CArrayInt::CArrayInt(int size){ m_iSize = size;\/\/ m_pData = new int[m_iSize];} CArray...
VC中如何动态定义多个数组,CArray (int*,int*) idata;但是,我需要的是...
最合理的是二维链表 其次是二维指针 最后,如果数据量不大,那么二维数组也是可以的,限制总最大范围。
CArray需要释放吗
CArray本身是不需要释放的,但如果其内存储了其它new出来的对象指针,那么这些new出来的对象,就需要你自己手动去释放了,否则会有内存泄漏!当然,如果它存储的是些基本数据类型(如int),也就不需要释放了!
陆扶长富: m_array.RemoveAt(m_array.GetCount()-1); 这句是对的!GetSize()这句获得的是数组的内存分配大小,为了快速的Grow,一般会比实际的元素数量多点(一般为多3个).GetCount()获得的是数组中元素的实际个数.你的调用出错,估计是因为当前数组中还没有元素.GetCount()返回0,减去1后变成了-1.最好对GetCount()的返回值判断一下,如果大于0,再调用这句 m_array.RemoveAt(m_array.GetCount()-1);
攀枝花市15212625161: 算法与数据结构,单链表,删除最后一个元素.下面c语言程序是我自己写的,然后中运行也是没问题的,的确 - ?
陆扶长富: 不知道你说的删除是什么意思,是将整个节点都free还是只是把data置为Null.从你给的代码来看,并没有free掉最后一个节点.因为当从while循环跳出来后,指针p已经是指向最后一个了,所以p->next是Null,所以你最后一行free(p->next)实际上没有起作用的.如果你想真正删除最后一个节点,将while循环的条件改为while(p->next&&p->next->next)即可,同时注意最后一个元素的链表删除的情况.
攀枝花市15212625161: php中怎么删除数组的第一个元素和最后一个元素 - ?
陆扶长富: 使用 array_shift 删除数组的第一个元2113素,例如:$user=array('apple','banana','orange');$result=array_shift($user); print_r($result); print_r($user);array_pop() 弹出并返回 array 数组的最后一个单元5261,并将数组 array 的长度减一4102....
攀枝花市15212625161: MFC中的CArray函数中,有一个RemoveAll()操作. - ?
陆扶长富: RemoveAll清除所有元素,释放掉元素占据的内存,在添加新的元素之前,当然不能进行任何读取操作.
攀枝花市15212625161: 定义一个单链表数据结构,实现在链表中出入删除和获取第一个元素或最后一个元素的的方法 - ?
陆扶长富: void Dlete(book*head,int num){book*l;if(head->num==num){l=head;head=head->next;::head =head;delete l;cout<<"操作成功"<return; } while(head) { if (head->next==NULL) { cout<<"找不到要删除的编号"< return; } if (head->next->...
攀枝花市15212625161: python定义一个列表的操作类,包括列表元素的添加和取值,列表合并,删除并且返回最后一个元素. - ?
陆扶长富: 很奇怪的需求... list对象本身就有这些功能.. 添加 append 取值 list[i] 合并 extends 最后一个值删除 pop 返回最后一个值 list[-1:]
攀枝花市15212625161: 怎样快速去掉数值后面的元? 我用EXCEI求和 数值后面有元求不了?怎样快速去掉后面的元呢? - ?
陆扶长富: CTRL+F然后点上面的替换在上面一个框内输入 元下面不输入然后点全部替换就好了!
攀枝花市15212625161: C语言中,如何删去一维数组中的元素,再将剩下的表示出来? - ?
陆扶长富: 例6-2 将10个整数存入数组,删除数组中的某个元素.例如,数组中有1,2,3,4,5,6,7,8,9,10共10个元素,删除第5个元素后,数组中剩下9个元素1,2,3,4,6,7,8,9,10. 首先定义一个长度为的int型数组,用来存放10个整数.用循环输入数组的每个...
攀枝花市15212625161: 线性表 删除第一个,最后一个,任意一个元素 的顺序存储和链式存储的算法 - ?
陆扶长富: 顺序e69da5e887aa3231313335323631343130323136353331333264656132存储就是数组:删除第一个 for(int i = 0; i<size-1; i++){ //size 为数组大小 a[i] = a[i+1]; } a[size - 1] = -1; size --; 删除最后一个 a[size - 1] = -1; size --; 删除任意一个 if(...
攀枝花市15212625161: JAVA中的List的使用 - ?
陆扶长富: List([]内的内容可省略),与数组类似: 实例化:List[] list = new ArrayList[](); 获得集合内元素个数:list.size(); 添加元素: 默认添加:list.add(e); 指定下标添加(添加后下标后的元素向后挪一位):list.add(index,e); 删除元素: 返回是...