new[] 和 delete[]
在使用 new 表达式时,C++ 并不允许申请数组类型的空间:
特别地,C++ 提供了一个称为 new[] 表达式的东西来实现相关的操作:
运算符 | 名称 | 作用 |
---|---|---|
new T[n] | new[] 运算符 | 获取 n 个连续的 T 类型存储空间 |
delete[] p | delete[] 运算符 | 释放 new[] 表达式申请到的内存(见下文) |
new[] 表达式的写法是 new T[n]
,它可以申请 n
个连续的 T
类型的空间并初始化,最后将其中首个空间的地址作为表达式的结果值。所以,new[] 的返回值类型为 T*
类型的地址:
这里,ptr
指向刚刚申请到的 4 个连续的 int
存储空间中的首个 int
。所以你可以通过指针的算术运算来获取这四个空间:
int* ptr{new int[4]};
*ptr = 1; // 第一个
*(ptr + 1) = 2; // 第二个
*(ptr + 2) = 3; // 第三个
*(ptr + 3) = 4; // 第四个
由于我们说过,*(a + b)
严格等价于 a[b]
,所以还可以 像 数组一样访问:
不过仍然注意,这里的 ptr
仍是一个指针而非一个数组:
这样来看,new[] 表达式和数组没有直接关系。所以这时并不限制申请的内存大小 n
是常量,只要是一个非负整数即可:
而且
ptr
可以出现在赋值号左侧。(除非你很清楚你在做什么,否则不要这样:ptr = nullptr;
是合法语句,但是这样写会导致内存泄漏。)
new[] 表达式也可在其后添加初始化器:
这是聚合初始化,会用列表中的值依次初始化获得的一系列内存;其余内存零初始化。在提供初始化器时,大小可以省略(尽管一般不这样做)。
new[] 表达式申请到的内存需要通过 delete[] 表达式来释放:
这里,你甚至可以把 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
指向n
个int[5]
类型数组的第一个。所以,这里面仍然只有一个“维度”是动态分配的,即new[n][M]
中M
必须是常量。
允许
new T[0]
。尽管这样不会分配任何可使用的内存,但对其进行delete[]
仍是必需的。