1.C++的动态数组(Dynamically-allocated Single-Dimension arrays)

当无法使用静态分配的数组时 — 因为你在编译时不知道合适的大小,或该数组太大而无法合理地放入运行时堆栈,或该数组的生命周期比创建它的函数更长。动态分配数组涉及到使用new运算符,类似于其他对象的动态分配,不同之处在于数组需要我们另外指定大小。

动态分配数组是使用new表达式完成的,该表达式会动态分配足够的内存来存储整个数组,然后返回指向数组第一个单元格的指针。

int* a = new int[10];

表达式 new int[10] 在堆上分配一个足够大的内存块来存储 10 个整数,并返回一个指向第一个的指针。第二个的位置直接跟在第一个之后,第三个直接跟在第二个之后,依此类推,所有的单元格都是相同的类型。所以给定一个指向第一个单元格的指针和一个索引,在幕后,可以使用以下计算来计算任何给定单元格的索引:

单元格 i 的地址 = (单元格 0 的地址) + (sizeof(int) * i)

注意,计算具有大索引的单元格的地址并不比使用小索引更消耗性能。这个计算只是一个乘法和加法;如果我们假设给定地址的内存访问需要恒定时间,那么访问数组中的任何单元都需要恒定时间。(在实践中,内存访问时间可能会因缓存等影响而有所不同,尽管你可以合理地认为主内存访问花费恒定时间。)

有趣的是,返回的指针类型并未指定有关数组的任何内容。当我们动态分配一个int数组时,我们得到的是一个int,即一个指向int的指针。数组在一般情况下,实现为指针,以他们的第一个单元,它是由我们来了解是否特定int指向一个单一的int或int数组。

一旦有了指向数组的指针,就可以像访问静态分配的数组一样访问它的每一个数字:

int* a = new int[10];
a[3] = 4;
std::cout << a[3] << std::endl;

a[3]相当于假想表达*q,其中q是一个指向int占用三个单位的一个点。换句话说,a[3]给你提供的是这个单元格中的int(从理论上讲,是对它的引用),而不是指向这个单元格的指针。

当完成动态分配的数组时,需要释放它,就像您处理任何其他动态分配的对象一样。但是,重要的是要注意你要使用不同的运算符delete[]来执行此操作。像delete一样,你给delete[]一个指向数组的指针,它会为你释放整个数组(以及它所有单元格中的所有对象)。

delete[] a;

由你决定哪些指针指向数组,并在需要时使用delete[]。在指向数组的指针上使用delete而不是delete[ ],或在指向单个值的指针上使用delete[]而不是delete,会导致“undefined behavior”。就像我们之前看到的使用delete一样,指针a不受影响,尽管它现在指向未分配的内存,因此它现在就不会被再使用。

2.C++动态分配二维数组-使用常规方式(即不使用除iostream外的内置库)

动态二维数组的分配

int** test = new int*[rows];
    for(int i = 0; i < rows; i++)
        test[i] = new int[columns];

释放动态分配二维数组占用的空间

for(int i = 0; i < rows; i++)
        delete []test[i];
    delete []test;

3.C++动态分配二维数组-使用Vector(需要额外的库vector)

注意使用这种方法你需要在文件头

#include <vector>

使用vector动态分配二维数组

std::vector<std::vector<int>> test(rows);
    for (i = 0; i < test.size(); i++)
        test[i].resize(columns);

使用swap()方法清空vector占用的内存空间

// vector的clear方法无法清空其占用的内存
//可以使用swap来使一个空的vector来替换掉原来test里有的内容。
vector<T>().swap(test);