4.3.1 创建动态数组
数组类型的变量有三个重要的限制:数组长度固定不变,在编译时必须知道长度,数组只在定义它的块语句内存在。
虽然数组长度是固定的,但动态分配的数组不必在编译时知道其长度,可以(通常也是)在运行时才确定数组长度。与数组变量不同,动态分配的数组将一直存在,直到程序显式释放它为止。
每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆。C语言程序使用一对标准库函数malloc和free在自由存储区中分配存储空间,而C++语言则使用new和delete表达式实现相同的功能。
1. 动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针。
int* i = new int[10];
new表达式需要指定指针类型以及在方括号中给出的数组维数,该维数可以是任意的复杂表达式。创建数组后,new将返回指向数组第一个元素的指针。在自由存储区中创建的数组对象是没有名字的,程序员只能通过其地址间接地访问堆中的对象。
2. 初始化动态分配的数组动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数实现初始化;如果数组元素是内置类型,则无初始化。
int* i = new int[10]; //uninitinalized
string* j = new string[10]; //initinalized
也可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化。
int* i = new int[10](); //initinalized
for(int *j = i, *last = i + 10; j!= last; j++)
{
cout << *j << endl;
}
对于动态分配的数组,其元素只能初始化为元素类型的默认值,而不能像数组变量那样,用初始化列表为数组元素提供各不相同的初值。
3. const对象的动态数组
如果我们在自由存储区中创建的数组存储了内置类型的const对象,则必须为这个数组提供初始化。
const int *arr = new const int[10]();
C++允许定义类类型的const数组,但该类类型必须提供默认构造函数。
const string *i = new const string[10];
因为已创建的常量元素不允许修改——因此这样的数组实际上用处不大。
4. 允许动态分配空数组
调用new动态创建长度为0的数组是合法的。
const string *i = new const string[0];
用new动态创建长度为0的数组时,new返回的有效的非零指针。该指针与new返回的其他指针不同,不能进行解引用操作,因为它毕竟没有指向任何元素。
5. 动态空间的释放
动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。如果不再需要使用动态创建的数组,程序员必须显式地将其占用的存储空间返还给程序的自由存储区。C++语言为指针提供delete []表达式释放指针所指向的数组空间。
const string *i = new const string[0];
delete [] i;
理论上,回收数组时,缺少空括号对,至少会导致运行时少释放了内存空间,从而产生内存泄漏(memory leak)。对于某些系统和/或元素类型,有可能会带来更严重的运行时错误。因此,在释放动态数组时千万别忘了方括号对。
6. 动态数组的使用
通常是因为在编译时无法知道数组的维数,所以才需要动态创建该数组。
const char *i = "12345";
int len = strlen(i) + 1;
char *j = new char[len];
strncpy(j, i, len);
for(char *k = j, *l = j+len; k != l; k ++)
{
cout << *k << endl;
}
标准库函数strlen返回的是字符串的长度,并不包括字符串结束符,在获得的字符串长度上必须加1以便在动态分配时预留结束符的存储空间。