HOWTO · C++
如何在 C++ 中创建向量的向量
本文介绍如何在 C++ 中创建指定行列数的向量的向量,并填充和修改其中的元素。
本页内容
向量的向量是一个外层 std::vector,其中每一行都是一个 std::vector 对象。若要创建行数和列数已知的二维结构,可以用行数构造外层向量,并用列数和初始值构造每个内层向量。下面的示例会演示如何创建指定尺寸的网格、填入样例值,以及更新其中的元素。当程序需要每一行都是一个向量,且行数或列数由程序决定时,这种结构很实用。
使用大小和值构造函数在 C++ 中创建向量的向量
大小和值构造函数会明确指定两个维度以及初始内容。例如,std::vector<std::vector<int>> grid(rows, std::vector<int>(columns, 0)); 会创建 rows 个内层向量,每个向量包含 columns 个初始值为零的整数。内层向量先构造一次,再作为用于初始化外层向量各行的值。这是同时指定大小和初始值的定尺寸构造,而不是默认构造。
下面的示例创建四行、每行六个整数。向量索引从零开始:[2][2] 表示第三行第三列;嵌套的 .at() 调用则访问第四行第四列。.at() 会分别检查每一层索引是否小于对应向量的大小;如果索引越界,就会报告越界访问。已知索引有效时,方括号写法更简洁。
#include <iostream>
#include <vector>
int main() {
constexpr int LENGTH = 4;
constexpr int WIDTH = 6;
std::vector<std::vector<int>> grid(LENGTH, std::vector<int>(WIDTH, 0));
grid[2][2] = 12;
std::cout << grid[2][2] << '\n';
grid.at(3).at(3) = 99;
std::cout << grid[3][3] << '\n';
return 0;
}
12
99
输出由这两次赋值决定。行索引和列索引应分别对应各自的维度:外层向量用于选择行,该行的内层向量用于选择列。如果尺寸并非固定值,也可以从程序数据中取得行数以及每一行的大小。
使用 rand 函数在 C++ 中为向量的向量填入样例值
网格创建好后,可以用嵌套循环遍历每个整数并为它赋一个样例值。下面的程序以当前时间作为 rand 的种子,并使用 % 100 生成 0 到 99 之间的值。每次运行打印出的值都可能不同,因此没有一份固定输出可供照抄。示例包含了 rand 和 srand 所需的 <cstdlib>,以及 std::time 所需的 <ctime>。
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
int main() {
constexpr int LENGTH = 4;
constexpr int WIDTH = 6;
std::vector<std::vector<int>> grid(LENGTH, std::vector<int>(WIDTH, 0));
std::srand(static_cast<unsigned int>(std::time(nullptr)));
for (auto& row : grid) {
for (int& value : row) {
value = std::rand() % 100;
std::cout << value << ' ';
}
std::cout << '\n';
}
return 0;
}
这里生成的只是用于演示和小型非敏感任务的样例数据。rand 不适合用于加密场景。取余运算也可能使结果分布产生偏差,除非生成器可能产生的值的数量能够被 100 整除。对于模拟或应用程序中需要更好分布特性的场景,应选择符合需求的随机数工具,不要把这个简短示例当作通用随机数生成器。
使用嵌套范围 for 循环修改 C++ 向量的向量中的每个元素
外层范围 for 循环遍历每一行,内层循环遍历该行中的每个整数。在 int& value 中,& 表示 value 是对已存储元素的引用,因此赋值会修改向量中的元素。如果不使用引用,循环变量就是副本;修改它不会改变向量中保存的整数。下面的程序会分别打印网格在每个元素乘以三之前和之后的内容。
#include <iostream>
#include <vector>
void print(const std::vector<std::vector<int>>& grid) {
for (const auto& row : grid) {
for (int value : row) {
std::cout << value << ' ';
}
std::cout << '\n';
}
}
int main() {
std::vector<std::vector<int>> grid{{1, 2, 3}, {4, 5, 6}};
std::cout << "Before:\n";
print(grid);
for (auto& row : grid) {
for (int& value : row) {
value *= 3;
}
}
std::cout << "After:\n";
print(grid);
return 0;
}
每个内层向量管理自己的一组整数,外层向量则存储这些行向量。同一行中的整数在内存中连续,但整个嵌套结构并不是一个连续的二维内存块。这只是对存储布局的说明,并不意味着可以笼统地比较速度:性能取决于具体操作和工作负载。当各行天然独立,或需要使用普通向量操作时,嵌套向量很实用。如果任务明确要求所有元素位于同一连续内存块中,应选择专为这种布局设计的表示方式。