数据结构——线性结构(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容器的基本操作!

原创粉丝点击