数据结构——线性结构(8)——动态数组与Vector

为什么要删除数组占用的内存?

我们先来看下面的一段代码:

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容器的基本操作!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值