数据结构——线性结构(8)——动态数组与Vector
来源:互联网 发布:卫星影像下载软件 编辑:程序博客网 时间:2024/06/15 11:05
为什么要删除数组占用的内存?
我们先来看下面的一段代码:
const int INIT_CAPACITY = 1000000;class Demo {public: Demo(); // constructor string at(int i);private: string *bigArray;};Demo::Demo(){ bigArray = new string[INIT_CAPACITY]; for (int i=0;i<INIT_CAPACITY;i++) { bigArray[i] = "Lalalalalalalalala! "; }}string Demo::at(int i){ return bigArray[i];}
我们现在来看看我们的构造函数,每次调用我们的构造函数,我们需要使用多少内存呢?
首先这是一个字符串数组,每个数组中有20个char字符,加上字符串的开销30B,所以应该这样计算:
100000 x (20+30)B = 50MB
假若我们执行下面的main函数:
int main(){ for (int i=0;i<10000;i++){ Demo demo; //我们这时候准备实例化10000个这样的对象 cout << i << ": " << demo.at(1234) << endl; } return 0;}
在执行40次或者40次以后,我们的内存占用将达到甚至超过2GB!!!而我们一般的电脑内存也不过比这大一点点而已。。。
动态数组
为了说明动态数组为什么那么有用或者说多必要,让我们回顾一下我们上个专栏学过的一个STL的容器:vector!C++抽象编程——STL(4)——vector类
我们可以看看这个vector有什么特点:
1. 它能储存int类型的数据(当然,作为容器它可以存放的任意类型的数据,我们只要在尖括号里包含对应的数据类型就好了)
2. 它有很多可用而且方便的函数,比如size,empty,insert。
3. 我们可以任意添加我们想要添加元素的个数(数组是一定要有固定长度的)
4. 它能自动回收内存。
寄居蟹原理(Hermit Crabs)
寄居蟹是个非常有趣的动物,它们生活在在海底发现的空的贝壳里,寄居蟹常常吃掉贝壳等软体动物,把人家的壳占为己有,这就是寄居蟹的名字的由来。这是他们的生活方式:
1. 自然生长,直达它自身的壳不足以容纳它自身
2. 寻找下一个更大的贝壳
3. 将它的所有东西搬到新的贝壳里
4. 将旧的贝壳丢弃在海底
5. 更新它们的居住地址
6. 更新它的容量
现在,我们来模拟这个过程,对数组空间已满进行处理。当我们的数组中耗尽空间时,我们要分配一个比我们旧数组大的新数组,这样我们可以存储新的数据并且数据还在不断增长。这些“可生长的数组”,我们采取五个步骤实现。在这之前我们要先确定,如果我们要扩展数组,我们需要多少新的内存?答案是,旧数组的两倍。这是最高效的做法
我们在C++算法学习——经典的抽象设计——buffer(3)中首次用到了expand函数:
void EditorBuffer::expandCapacity(){ char * oldArray = array; capacity *= 2; array = new char[capacity]; for(int i = 0; i < length; i++){ array[i] = oldArray[i]; //复制 } delete[] oldArray; }
它反映了动态数组扩容的5步曲:
- 创建一个新的大小的数组(通常是旧数组大小的两倍)
- 将旧数组元素复制到新数组
- 删除旧数组(了解这里发生了什么的是关键!)
- 将旧数组指向旧数组的指针变量指向新数组(它是一个指针!)
- 更新数组的容量变量
追踪vector的扩容过程
这里还有问题,那就是我们什么时候需要扩容?(废话,当然是数组满了的时候) 那么怎么知道它什么时候满了呢?那就需要我们自己去追踪了。
假设我们的Vector的元素指针指向如下图的内存。 capacity= 5,count = 5(已满)。
要扩大容量,我们遵守我们的5步规则(黑体部分):
1. 申请一个可容纳10个元素的内存空间。假设为int型
2. 这里有个问题,我们这个内存空间是可选的吗?如果是,那么我们当然是最好紧跟着旧数组这样:
但是,这是不可选的,因为你申请的是容量为10的空间,而且是未使用过的,而上图前五个,你是已经在使用了。
3. 所以我们的内存空间是系统给我们的。
4. 申请后用我们定义的数组指针指向我们的这段内存。那么就是这句代码:
int *newElements = new int[capacity*2];
5. 复制旧数组
for(int i = 0; i < count; i++){ newElement[i] = element[i];}
6. 删除原始的数组(意味着你不再能合法访问那段内存了)
delete []element;
7.改变指针指向
element = newElement;
下一篇,我们就来实现一下STL中的vector容器的基本操作!
- 数据结构——线性结构(8)——动态数组与Vector
- 【数据结构】数据结构与算法(一)——线性结构
- 小白学数据结构——一、线性结构(数组&链表)
- 数据结构实战——线性结构之动态数组表示法
- 数据结构——线性结构(9)——Vector的实现过程详解
- [数据结构]线性结构——线性表
- 数据结构——线性结构
- 数据结构—向量(vector)-数组
- 数据结构——第二讲、线性结构(8)
- 数据结构与算法——线性表顺序存储结构
- 数据结构与算法 —— 动态数组
- 线性结构————数据结构
- [数据结构]线性结构——队列
- [数据结构]线性结构——栈
- [数据结构]线性结构——串
- [数据结构]线性结构——队列
- 【数据结构】线性结构——初始化
- 【数据结构】线性结构——判空
- android压力测试命令monkey
- 深度学习与神经网络学习笔记(四)
- 最小二乘法拟合圆公式推导及vc实现
- List接口
- Redis实用技巧之点赞系统设计
- 数据结构——线性结构(8)——动态数组与Vector
- 实验三顺序队列
- Java 封装类型和基本类型区别
- 开发中数据库常见的设计问题
- Matlab的图像
- 1004. Counting Leaves (树)
- PAT 1003. Universal Travel Sites (35)
- 维纳滤波运动模糊图像复原
- SpringCloud(第 046 篇)注解式Schedule配置定时任务,不支持任务调度