vector——一种不用提前定长、可以随时用
push_back 添加元素、用 .size() 取长度的容器,
并搞清楚它和数组相比,好在哪里、又有多相似。
vector——一种不用提前定长、可以随时用
push_back 添加元素、用 .size() 取长度的容器,
并搞清楚它和数组相比,好在哪里、又有多相似。
数组声明的那一刻,长度就永远固定了:
int scores[5]; // 5 个人的分数,多一个都装不下
// 如果这次只有 3 个人报名?如果下次来了 8 个人? vector 不是内置在语言里的,用之前要
#include <vector>(和 string 类似)。
声明语法是 std::vector<类型> 变量名,尖括号里写清楚
要装的元素类型:
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores; // 声明一个空的、能装 int 的 vector
std::vector<int> prices = {10, 20, 30}; // 也可以像数组一样带初始值
std::cout << prices[0] << std::endl; // 下标访问,和数组一模一样
return 0;
} prices[0] 这种写法,vector 和数组用法一致——
下标同样从 0 开始,同样用中括号。会用数组,就已经会用 vector 一大半了。
vector 同样成立——
prices[10] 对一个只有 3 个元素的 vector 来说照样越界,
使用 [] 时 C++ 不会自动帮你检查。
vector 能变的是长度,
不是“越界访问会不会出事”这条规矩。
prices.at(0) 和 prices[0] 都能读取第一个元素;
区别是 .at() 会检查下标,越界时抛出 std::out_of_range,
更容易发现问题。初学阶段可以记成:[] 简洁,
.at() 更安全但多一次检查。
std::vector<int> 声明好之后,只能装 int——
这一点和数组一样死板,变的只是“能装多少个”,不是“能装什么”。
scores.push_back(90); 的意思是"在 scores 的末尾,
新增一个值为 90 的格子"。每调用一次 push_back,长度就自动
加一——这是数组做不到的事。
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores;
scores.push_back(90); // 装进第 1 个
scores.push_back(85); // 再装进第 2 个,长度自动变成 2
scores.push_back(70); // 再装一个,长度自动变成 3
std::cout << scores[0] << " " << scores[1] << " " << scores[2] << std::endl;
return 0;
}
在 push_back.cpp 基础上,再多调用几次 push_back,
塞进你自己想要的数字,然后打印新加的那些元素,确认它们的下标接着往后排。
数组的长度得自己心里记住(或者另外声明一个变量存着);vector
自带 .size(),随时能问它“你现在装了几个?”,配合循环遍历特别方便:
#include <cstddef>
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores = {90, 85, 70, 60, 95};
std::cout << "一共 " << scores.size() << " 个分数:" << std::endl;
for (std::size_t i = 0; i < scores.size(); i++) {
std::cout << scores[i] << std::endl;
}
return 0;
} i < 5)换成了
i < scores.size()——上节课学的求和、找最大值那套算法,
原封不动搬到 vector 上都能用。
.size() 返回的是专门表示大小和下标的无符号类型,
通常写作 std::size_t,并不是普通的 int。
让循环变量也使用 std::size_t,可以避免有符号与无符号整数比较的编译警告。
如果只想依次拿到每个元素,不关心它是第几个,可以用更简洁的
范围 for。每一轮,score
会自动取得 scores 中的下一个元素:
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores = {90, 85, 70, 60, 95};
for (int score : scores) {
std::cout << score << std::endl;
}
return 0;
}
如果连“要输入几个数据”都不知道,正好是 while 的主场:
不断读入,直到用户输入某个“结束信号”(这里用 -1)为止:
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores;
std::cout << "不断输入分数,输入 -1 结束:" << std::endl;
int x;
while (std::cin >> x && x != -1) {
scores.push_back(x);
}
std::cout << "一共录入了 " << scores.size() << " 个分数。" << std::endl;
return 0;
} std::cin >> x && x != -1 有两道关:
先确认成功读到了一个整数,再确认它不是结束信号。用户输入非数字或发送文件结束信号时,
循环也会安全停止,不会卡在失败状态里不断运行。
vector 靠 push_back 边输入边变长,正好解决这个问题。
声明一个 std::vector<int>,用 push_back 塞进
5 个你喜欢的数字,用循环遍历打印每一个,并打印 .size() 的结果。
仿照 dynamic_input.cpp,让用户不断输入正整数,
直到输入 0 结束,把每次输入的数存进 vector,
最后打印“一共输入了几个数”。
对练习 16-1 里的 vector,求出并打印所有元素的总和。
提示:把第 15 讲的累加器套路搬过来,循环条件换成
.size() 即可。
| 能力 | 用到的工具 |
|---|---|
| 声明一个动态容器 | std::vector<类型> 变量名; |
| 往末尾添加元素 | .push_back(值) |
| 取当前长度 | .size() |
| 读写某个元素 | [下标],或用会检查范围的 .at(下标) |
| 不关心下标地遍历 | 范围 for |
记一句话:确定装多少个、追求简单,用数组;不确定装多少个、可能要动态增加,用 vector。
string,其实骨子里也很像一个“字符的数组”——
下节课复习巩固字符串常用操作:用 .length() 取长度、用下标访问单个字符、
遍历字符串查找特定字符。