除动态分配单个变量外,我们亦可动态分配变量数组。与固定数组须在编译期确定长度不同,动态分配允许在运行期指定数组长度(即长度无需为 constexpr)。
作者注:
以下课程将使用动态分配的 C 风格数组,这是最常见的动态数组形式。
虽然可以动态分配 std::array,但通常更推荐使用非动态分配的 std::vector。
动态分配数组
要动态分配数组,应使用数组形式的 new 与 delete(常写作 new[] 与 delete[]):
#include <cstddef>
#include <iostream>
int main()
{
std::cout << "请输入一个正整数: ";
std::size_t length{};
std::cin >> length;
```cpp
int* array{ new int[length]{} }; // 使用数组 new。注意 length 无需为常量!
std::cout << "我已分配了一个长度为 " << length << " 的整数数组\n";
array[0] = 5; // 将第 0 个元素设为 5
delete[] array; // 使用数组 delete 释放数组
// 由于 array 立即离开作用域,无需再置为 nullptr
return 0;
}
由于我们分配的是数组,C++ 会调用数组版 new(即 new[]),尽管语法上 [] 并非紧挨 new 关键字。
动态数组长度类型为 std::size_t。若使用非常量 int,需通过 static_cast 转换,否则编译器会报窄化转换警告。
注意:动态数组内存来源不同于固定数组,故数组长度可以非常大。您可以尝试分配长度为 1 000 000(甚至 100 000 000)的数组而不会出错。因此,需要大量内存的 C++ 程序通常采用动态分配。
## 动态释放数组
释放动态数组时必须使用数组版 delete,即 delete[]。
这告知 CPU 需清理多个变量而非单个变量。初学者最常犯的错误之一便是对动态数组使用 delete 而非 delete[]。对数组使用标量 delete 会导致未定义行为,如数据损坏、内存泄漏、崩溃等。
常见问题:“数组 delete[] 如何知道要释放多少内存?”
答:数组 new[] 会记录分配量,供数组 delete[] 正确释放。遗憾的是,该长度对程序员不可见。
## 动态数组与固定数组几乎相同
在 《C 风格数组退化》中,您已了解固定数组保存首元素地址,并可退化为指向首元素的指针。退化后,数组长度不可用(sizeof 亦无法求得大小),除此之外差异甚微。
动态数组本质上是指向首元素的指针,因此同样不知长度,功能与退化后的固定数组一致,区别仅在于程序员需用 delete[] 手动释放。
## 初始化动态分配数组
若要将动态数组初始化为 0,语法如下:
```cpp
int* array{ new int[length]{} };
C++11 之前,无法便捷地将动态数组初始化为非零值(初始化列表仅适用于固定数组),需手动循环赋值:
int* array = new int[5];
array[0] = 9;
array[1] = 7;
array[2] = 5;
array[3] = 3;
array[4] = 1;
自 C++11 起,可使用初始化列表:
int fixedArray[5] = { 9, 7, 5, 3, 1 }; // C++11 之前初始化固定数组
int* array{ new int[5]{ 9, 7, 5, 3, 1 } }; // C++11 起初始化动态数组
// 为避免写两次类型,可用 auto
auto* array{ new int[5]{ 9, 7, 5, 3, 1 } };
注意:语法中数组长度与初始化列表之间无 = 运算符。
为保持一致,固定数组亦可使用统一初始化:
int fixedArray[]{ 9, 7, 5, 3, 1 };
char fixedArray[]{ "Hello, world!" };
显式指定数组大小可选。
调整数组大小
动态分配允许在分配时设定长度,但 C++ 未提供内置机制以调整已分配数组的大小。若要扩容,只能重新申请新数组,复制元素后释放旧数组。然而,这一过程易出错,尤其当元素为类类型时(类对象的创建有特殊规则)。
因此,建议直接使用 std::vector 而非手动实现。
测验
问题 #1
编写程序,完成以下功能:
- 询问用户欲输入多少个姓名;
- 动态分配 std::string 数组;
- 提示用户逐条输入姓名;
- 调用 std::sort 排序姓名(参见 《使用选择排序对数组排序》与 《指针运算与下标》);
- 输出排序后的姓名列表。
std::string 已支持通过运算符 < 与 > 比较,无需手动实现。
输出示例:
How many names would you like to enter? 5
Enter name #1: Jason
Enter name #2: Mark
Enter name #3: Alex
Enter name #4: Chris
Enter name #5: John
Here is your sorted list:
Name #1: Alex
Name #2: Chris
Name #3: Jason
Name #4: John
Name #5: Mark
提示
- 使用 std::getline() 读取含空格的姓名(参见 5.7《std::string 简介》)。
- 对指针形式的数组调用 std::sort 时,需手动计算首尾迭代器:
std::sort(array, array + arrayLength);