
博主名称:deM0one ⭐️C++专栏: C++
文章目录
- [C++ 迭代器与容器操作](#C++ 迭代器与容器操作)
-
- 一、引言:为什么要学习迭代器
-
- [1.1 从 STL 说起](#1.1 从 STL 说起)
- [1.2 阅读本文你将掌握(今日学习内容)](#1.2 阅读本文你将掌握(今日学习内容))
- 二、迭代器简介
-
- [2.1 迭代器是什么](#2.1 迭代器是什么)
- [2.2 为什么需要迭代器](#2.2 为什么需要迭代器)
- [2.3 用 begin() 与 end() 遍历](#2.3 用 begin() 与 end() 遍历)
- [2.4 size 与 capacity](#2.4 size 与 capacity)
- [三、范围 for:另一种访问方式](#三、范围 for:另一种访问方式)
-
- [3.1 基本用法](#3.1 基本用法)
- [3.2 修改行为:值拷贝](#3.2 修改行为:值拷贝)
- [3.3 加引用才能修改](#3.3 加引用才能修改)
- [3.4 适用范围与特点](#3.4 适用范围与特点)
- [四、auto 关键字](#四、auto 关键字)
-
- [4.1 作用:自动推导类型](#4.1 作用:自动推导类型)
- [4.2 用 typeid 查看类型](#4.2 用 typeid 查看类型)
- [4.3 使用规则与限制](#4.3 使用规则与限制)
- [4.4 优缺点总结](#4.4 优缺点总结)
- 五、迭代器的类型
-
- [5.1 迭代器一共有四种](#5.1 迭代器一共有四种)
- [5.2 正向迭代器](#5.2 正向迭代器)
- [5.3 反向迭代器](#5.3 反向迭代器)
- [5.4 常量迭代器](#5.4 常量迭代器)
- [六、const 对象与 const 迭代器](#六、const 对象与 const 迭代器)
-
- [6.1 为什么 const 对象要用 const 迭代器](#6.1 为什么 const 对象要用 const 迭代器)
- [6.2 代码演示](#6.2 代码演示)
- [6.3 C++98 与 C++11 的接口变化](#6.3 C++98 与 C++11 的接口变化)
- [七、reserve 与 reverse 的区分](#七、reserve 与 reverse 的区分)
-
- [7.1 一句话区分](#7.1 一句话区分)
- [7.2 reserve 的作用](#7.2 reserve 的作用)
- [7.3 观察 capacity 的变化(扩容实验)](#7.3 观察 capacity 的变化(扩容实验))
- [7.4 reserve 的特性与平台差异](#7.4 reserve 的特性与平台差异)
- 八、综合示例代码
- 九、总结
C++ 迭代器与容器操作
本文档基于学习笔记整理,目标读者为 C++ 初学者,内容覆盖:迭代器、范围 for、auto 关键字、迭代器类型、const 迭代器、reserve 与 reverse。
一、引言:为什么要学习迭代器
1.1 从 STL 说起
- C++ 标准模板库(STL)有六大组件,迭代器是其中之一
- 容器(如 vector、string)负责存储数据,迭代器负责访问容器中的数据
- 认识常见类型:
vector<int>::iterator、string::iterator
1.2 阅读本文你将掌握(今日学习内容)
- 什么是迭代器,如何用 begin() / end() 遍历容器
- 范围 for 与迭代器的关系及区别
- auto 的用法与限制
- 迭代器的四种类型
- const 对象应该使用哪种迭代器
- reserve 与 reverse 的区别
二、迭代器简介
2.1 迭代器是什么
- 迭代器是一种访问和遍历容器中元素的方式
- 它很像指针,但不是指针 ,只是用法完全跟指针类似:
- 解引用
*it取元素 ++it移动到下一个元素- 用
it != end()判断是否遍历结束
- 解引用
2.2 为什么需要迭代器
- 提供了一种通用的访问方式:所有容器都可以用迭代器访问
- 提供统一接口、统一访问方式,屏蔽不同容器的内部差异
2.3 用 begin() 与 end() 遍历
begin():指向第一个元素end():指向最后一个元素的下一个位置(不指向有效元素)
cpp
#include <iostream>
#include <string>
using namespace std;
void test_string2()
{
string s2("hello world");
string::iterator it = s2.begin();
while (it != s2.end())
{
cout << *it;
++it;
}
cout << endl;
}
2.4 size 与 capacity
size():当前有效字符的个数capacity():当前容量,注意字符串末尾的\0也占容量- 图示关系:
hello world\0,begin()指向 h,end()指向\0之后
三、范围 for:另一种访问方式
3.1 基本用法
- 自动迭代,逐个取出元素:
cpp
for (auto ch : s2) // 字符赋值,自动迭代
cout << ch;
cout << endl;
for (char ch : s2) // 显式写出类型
cout << ch;
cout << endl;
3.2 修改行为:值拷贝
- 通过迭代器是可以修改容器内容的
- 但范围 for 默认按值拷贝:循环体内修改
ch不会改变容器本身的值,要修改的话我们应当将类型设为引用类型
3.3 加引用才能修改
cpp
for (auto& ch : s2) // 加引用
ch = 'x'; // 才能真正修改容器中的字符
3.4 适用范围与特点
- 范围 for 适用于容器和数组
- 特点:写法简单,代码更简洁
四、auto 关键字
4.1 作用:自动推导类型
- 当类型名称过长时,用
auto让编译器自动推导,简化代码:
cpp
map<string, string> dict;
// map<string, string>::iterator mit = dict.begin();
auto mit = dict.begin(); // 等价写法
4.2 用 typeid 查看类型
typeid(变量).name()可以帮我们查看变量的实际类型

4.3 使用规则与限制
- 必须初始化 ,否则编译报错,例如:
// auto e;→ error C3531:"e":类型包含"auto"的符号必须具有初始值设定项 - 不能定义数组
- 不能作为参数传参
- 可以做返回值,但要谨慎使用
4.4 优缺点总结
- 优点:简化长类型名,代码更简洁
- 缺点:一定程度上牺牲了可读性,使代码变复杂
五、迭代器的类型
5.1 迭代器一共有四种
| 迭代器类型 | 方向 | 可否修改 |
|---|---|---|
| iterator | 正向 | 可读可写 |
| const_iterator | 正向 | 只读 |
| reverse_iterator | 反向 | 可读可写 |
| const_reverse_iterator | 反向 | 只读 |
- 按方向分:正向迭代器、反向迭代器
- 按权限分:普通迭代器、常量迭代器
5.2 正向迭代器
- 从前往后遍历:
begin()到end()
5.3 反向迭代器
- 从后往前遍历,能够倒着遍历 :
rbegin()到rend()
cpp
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
cout << *rit;
++rit;
}
cout << endl;
5.4 常量迭代器
- 只能读取数据,不能修改数据
六、const 对象与 const 迭代器
6.1 为什么 const 对象要用 const 迭代器
- 普通迭代器可读可写
- const 对象的成员不可修改,因此必须使用 const 迭代器访问
6.2 代码演示
cpp
const string s3("hello world");
// string::const_iterator cit = s3.begin();
auto cit = s3.begin(); // 自动推导为 const_iterator
while (cit != s3.end())
{
cout << *cit;
++cit;
}
cout << endl;
// string::const_reverse_iterator rcit = s3.rbegin();
auto rcit = s3.rbegin(); // 自动推导为 const_reverse_iterator
while (rcit != s3.rend())
{
cout << *rcit;
// *rcit += 2; // 编译报错:const 迭代器不能修改
}
cout << endl;
6.3 C++98 与 C++11 的接口变化
- 以
std::string::begin为例:- C++98:
iterator begin(); - C++11:新增重载
const_iterator begin() const;
- C++98:
- 因此 const 对象调用 begin() 会得到 const_iterator
七、reserve 与 reverse 的区分
7.1 一句话区分
reserve:提前分配(预留)内存空间reverse:逆置(反转)容器中元素的顺序- 两者是完全不同的概念,注意不要混淆
7.2 reserve 的作用
- 提前开好空间,避免频繁扩容带来的重新分配
7.3 观察 capacity 的变化(扩容实验)
cpp
void TestPushBack()
{
string s;
s.reserve(100); // 预留 100 个空间
size_t sz = s.size();
size_t cap = s.capacity();
cout << "capacity changed: " << sz << " " << cap << endl;
// 不断插入,观察 capacity 何时变化
for (int i = 0; i < 199; ++i)
{
s.push_back('c');
if (sz != s.size())
{
sz = s.size();
cap = s.capacity();
cout << "capacity changed: " << sz << " " << cap << endl;
}
}
}
7.4 reserve 的特性与平台差异
- 可以扩容
- 缩容是平台相关的,行为不确定
- 可以确定的是:
reserve一定不会修改内容 - VS 平台:
- 传入小于
size的值:容量不变 - 传入小于
capacity的值:容量不变
- 传入小于
- Linux 平台:
- 传入小于
capacity的值:会缩容,最多缩到size - 传入比
capacity大的值:会尝试按对齐规则扩容
- 传入小于
八、综合示例代码
将本文内容串起来:正向、反向、常量迭代器 + auto + 范围 for 的综合演示。
cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> vec = {1, 2, 3, 4, 5};
// 正向迭代器 + auto
for (auto it = vec.begin(); it != vec.end(); ++it)
cout << *it << " ";
cout << endl;
// const 对象使用 const 迭代器
const vector<int>& const_vec = vec;
for (auto cit = const_vec.begin(); cit != const_vec.end(); ++cit)
cout << *cit << " ";
cout << endl;
// 范围 for:加引用可修改
for (auto& v : vec)
v *= 2;
for (const auto& v : vec)
cout << v << " ";
cout << endl;
return 0;
}
九、总结
- 通过迭代器,我们可以方便地遍历和管理容器中的数据
auto关键字的引入使代码更简洁易读,但要注意初始化、数组、传参等限制reserve和reverse各有特定用途,需要根据实际需求选择- 注意区分不同类型的迭代器及其适用场景(正向/反向、普通/常量),确保程序的正确性和效率
- const 对象必须使用 const 迭代器;范围 for 默认按值拷贝,修改容器要加引用
任何的建议和批评欢迎指出,我们共同进步!