前言
在前面的学习中,我们已经掌握了 C++ 的基础语法和编程概念。本文将深入探讨 C++ 标准库的使用,并详细介绍迭代器、auto 关键字以及范围 for 循环等相关知识。
前言 在前面的学习中,我们已经掌握了 C++ 的基础语法和编程概念。将深入探讨 C++ 标准库的使用,并详细介绍迭代器、auto 关键字以及范围 for 循环等相关知识。 一、STL 简介 1.1 什么是 STL STL(Standard Template Library,标准模板库)是 C++ 标准库的核心组成部分。它不仅提供了可复用的组件库,更是一个集成了高效数据结构与算法的软件框架。 1…

在前面的学习中,我们已经掌握了 C++ 的基础语法和编程概念。本文将深入探讨 C++ 标准库的使用,并详细介绍迭代器、auto 关键字以及范围 for 循环等相关知识。
STL(Standard Template Library,标准模板库)是 C++ 标准库的核心组成部分。它不仅提供了可复用的组件库,更是一个集成了高效数据结构与算法的软件框架。
由于历史原因,string 类型先于 STL 出现,STL 后来由惠普实验室开发并开源,因此人们通常不将 string 归入 STL 范畴。

迭代器(Iterator)是 C++ STL 中最精妙的设计之一。如果把 STL 的容器比作各种不同类型的仓库(数组、链表、树),那么迭代器就是一把万能钥匙,它让你不用关心仓库内部的具体构造,就能以统一的方式访问里面的每一个元素。
迭代器的行为在视觉和操作上都非常像普通的 C 语言指针,你可以对它进行解引用(获取数据)和移动(指向下一个数据)。
例如:vector<int>::iterator it
注:这里的
iterator是通过内置类型(一般来说是指针)进行typedef重命名,即泛化后的产物。
迭代器的一般操作如下所示:
*it(解引用):获取迭代器当前指向的元素的值。++it 或 it++(递增):让迭代器移动到容器中的下一个元素。== 和 !=(比较):判断两个迭代器是否指向同一个位置。提示:对于迭代器,我们可以初步简单认为其是一个高级版本的指针。
一般而言,容器提供以下两个函数返回特定位置的迭代器:
begin():返回指向容器中第一个元素的迭代器。end():返回指向容器中最后一个元素之后位置的迭代器,它不指向任何实际存在的元素,可以理解为一个'越界哨兵'。因此,在 C++ 中,容器和算法使用迭代器时都严格遵循左闭右开区间这一基本原则,即 [begin, end)。
提示:
- 当
begin() == end()时,可以直接用来判断容器是否为空。end() - begin()的大小即为容器中的元素个数。
以 vector 为例,展示经典的迭代器遍历方式:
#include <iostream>
#include <vector>
using namespace std;
int main() {
// vector 容器可简单理解为顺序表
vector<int> numbers = {10, 20, 30, 40, 50};
// 声明一个迭代器 it,类型为 vector<int>::iterator
vector<int>::iterator it = numbers.begin();
// 从 begin() 开始,一直循环直到等于 end()
while (it != numbers.end()) {
cout << *it << " "; // 使用 *it 获取当前指向的值
++it;
}
// 预计输出: 10 20 30 40 50
return 0;
}
不同的数据结构底层内存分布不同,因此它们提供的迭代器能力也不同,这是连接数据结构与算法的关键:
it + 5(直接跳到 5 个位置后)。底层是连续内存的容器拥有它,比如 std::vector 和 std::deque。只有拥有这种迭代器的容器,才能使用 std::sort 进行快速排序。++) 和向后 (--) 移动,但不能跨越式跳跃。比如基于链表实现的 std::list。++),比如单向链表 std::forward_list。
在现代 C++11 以后,auto 绝对是提升开发效率和代码可读性的利器。它能帮你省去大量敲击键盘的时间,并有效避免类型拼写错误。
使用 auto 声明的变量,其类型不是在运行时决定的,而是在编译代码时,由编译器根据赋予变量的初始值自动推断出来的。
代码示例:
#include <iostream>
#include <vector>
using namespace std;
int main() {
auto a = 10; // 编译器推导 a 为 int
auto b = 3.14; // 编译器推导 b 为 double
auto c = "Hello"; // 编译器推导 c 为 const char*
return 0;
}
提示:
- 正因为是根据初始值推导,所以使用
auto声明变量时,必须同时进行初始化。- 写
auto x;是无法通过编译的。
拯救冗长、复杂的类型声明
在学习 STL 和算法时,经常会遇到嵌套极深的数据结构。如果没有 auto,代码会写得非常痛苦且容易出错。
代码示例:
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
map<string, string> dict = {
{"apple", "苹果"},
{"orange", "橙子"},
{"pear", "梨"}
};
// 过于冗余的写法
map<string, string>::iterator it1 = dict.begin();
// 通过 auto 进行简化
auto it2 = dict.begin();
return 0;
}
使用 auto 时,最容易出错的地方在于它对引用和 const 限定符的推导规则:auto 默认会'剥离'顶层 const 和引用。
代码示例:
int x = 10;
int& rx = x;
const int cx = 10;
auto a = rx; // a 的类型是 int(引用属性被丢弃),属于值拷贝
auto b = cx; // b 的类型是 int(顶层 const 被丢弃)
详情解析:
auto a = rx;:rx是x的引用(别名)。当你写下auto a = rx;时,编译器会认为你想用rx的值(即 10)来初始化新变量a。由于没有显式写出&,编译器将a推导为普通的int,并在内存中分配新空间进行值拷贝。auto b = cx;:cx是被const修饰的整数(顶层 const)。编译器会认为你想把cx的值(10)赋给新变量b。cx本身的只读属性不影响新变量b,因此b被推导为普通的int。
代码示例:保留引用和 const 的写法
int x = 10;
int& rx = x;
const int cx = 10;
auto& c = rx; // c 是 int&,修改 c 会改变 x
const auto d = cx; // d 是 const int,常用于只读访问
因此,C++ 规定:auto 默认只推导最纯粹的底层类型。如果你想要引用(&)或者只读(const),必须显式写出。
在 C++20 之前,普通函数的参数不能用 auto,而需要使用模板。
// C++20 之前非法的写法
// void func(auto a) {}
核心机制:编译器会通过阅读函数体内的 return 语句,反向推导出函数的返回类型。
auto multiply(int a, double b) {
return a * b; // 编译器推导返回类型为 double
}
return 语句的类型必须一致。编译器不会做隐式类型转换。auto check_number(int x) {
if (x > 0) {
return 1; // int
} else {
return 1.5; // 编译报错!double 与 int 冲突
}
}
解决办法:强制统一类型,例如将 return 1; 改为 return 1.0;。
auto 依然会丢弃引用和 const。默认情况下,auto 作为返回值永远是'值拷贝'。int global_var = 100;
auto get_ref() {
int& ref = global_var;
return ref; // 实际返回普通 int(值拷贝),引用属性被丢弃
}
解决办法:显式写成 auto& 或 const auto&。
int global_var = 100;
auto& get_ref() {
int& ref = global_var;
return ref; // 正确返回引用
}
范围 for 循环是 C++11 引入的重要特性,极大简化了遍历数组和容器(如 std::vector, std::string, std::map 等)的代码,提高了可读性并减少了越界错误。
for (元素类型 元素变量名 : 遍历对象) {
// 循环体
}
参数说明:
auto 使用)。每次循环将容器元素拷贝给变量,修改变量不影响原数据。适用于基础数据类型且无需修改的场景。
vector<int> nums = {1, 2, 3};
for (int x : nums) {
cout << x << " "; // 输出 1 2 3
}
加上 &,变量直接引用容器内元素,修改即修改原数据。适用于需要修改容器内容的场景。
vector<int> nums = {1, 2, 3};
for (int& x : nums) {
x *= 2; // 原数组变为 {2, 4, 6}
}
加上 const 和 &,避免拷贝开销,同时保证数据只读。适用于遍历复杂对象(如 string、结构体、自定义类)且只需读取的场景,是最推荐的写法。
vector<string> words = {"Hello", "World"};
for (const string& word : words) {
cout << word << endl;
}
实际开发中通常直接使用 auto 推导:
for (auto x : container):按值拷贝for (auto& x : container):按引用,可修改for (const auto& x : container):按常量引用,只读且高效示例:
vector<string> words = {"Hello", "World"};
for (const auto& word : words) {
cout << word << endl;
}
范围 for 是编译器提供的语法糖,底层会自动转化为使用迭代器的循环:
// 原始代码
for (auto& x : container) { /*...*/ }
// 编译器实际转化为类似如下代码:
auto _begin = container.begin();
auto _end = container.end();
for (; _begin != _end; ++_begin) {
auto& x = *_begin;
/*...*/
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online