new[] 和 delete[]

在使用 new 表达式时,C++ 并不允许申请数组类型的空间:

using Arr = int[4]; // 定义类型别名 Arr 为 int[4]
Arr* p{new Arr};    // 错误:不能 new 一个数组

特别地,C++ 提供了一个称为 new[] 表达式的东西来实现相关的操作:

运算符名称作用
new T[n]new[] 运算符获取 n 个连续的 T 类型存储空间
delete[] pdelete[] 运算符释放 new[] 表达式申请到的内存(见下文)

new[] 表达式的写法是 new T[n],它可以申请 n 个连续的 T 类型的空间并初始化,最后将其中首个空间的地址作为表达式的结果值。所以,new[] 的返回值类型为 T* 类型的地址:

int* ptr{new int[4]};

这里,ptr 指向刚刚申请到的 4 个连续的 int 存储空间中的首个 int。所以你可以通过指针的算术运算来获取这四个空间:

int* ptr{new int[4]};
*ptr = 1;        // 第一个
*(ptr + 1) = 2;  // 第二个
*(ptr + 2) = 3;  // 第三个
*(ptr + 3) = 4;  // 第四个

由于我们说过,*(a + b) 严格等价于 a[b],所以还可以 数组一样访问:

int* ptr{new int[4]};
ptr[0] = 1;  // 第一个
ptr[1] = 2;  // 第二个
ptr[2] = 3;  // 第三个
ptr[3] = 4;  // 第四个

不过仍然注意,这里的 ptr 仍是一个指针而非一个数组:

sizeof(ptr); // 等价于 sizeof(int*),而非 sizeof(int[4])

这样来看,new[] 表达式和数组没有直接关系。所以这时并不限制申请的内存大小 n 是常量,只要是一个非负整数即可:

unsigned int n;
cin >> n;
int* ptr{new int[n]};

而且 ptr 可以出现在赋值号左侧。(除非你很清楚你在做什么,否则不要这样:ptr = nullptr; 是合法语句,但是这样写会导致内存泄漏。)

new[] 表达式也可在其后添加初始化器:

ptr = new int[4]{1, 2, 3, 4};

这是聚合初始化,会用列表中的值依次初始化获得的一系列内存;其余内存零初始化。在提供初始化器时,大小可以省略(尽管一般不这样做)。

new[] 表达式申请到的内存需要通过 delete[] 表达式来释放:

int* ptr{new int[4]};
// [...]
delete[] ptr;

这里,你甚至可以把 delete[] 整体当做一个关键字,它和 delete 是不同的。delete 只会释放 ptr 指向的一块内存,而 delete[] 会释放 ptr 及后面的总共 n 块连续内存。

危险

new[] 完不 delete[] 会导致严重的内存泄漏问题。new[] 完只 delete 是不够的,因为还有 n - 1 块后续内存未被释放。

new[] 和 delete[] 表达式是 C++ 中实现动态大小“数组”的唯一方式。而且,你无法通过这种方式创建在“两个维度上动态”的“数组”。

不同于 new 表达式,new[] 表达式允许 T 是数组类型,比如:int(*ptr)[5]{new int[n][5]};,声明指针 ptr 指向 nint[5] 类型数组的第一个。所以,这里面仍然只有一个“维度”是动态分配的,即 new[n][M]M 必须是常量。

允许 new T[0]。尽管这样不会分配任何可使用的内存,但对其进行 delete[] 仍是必需的。

最近更新:
代码未运行