C++STL容器string--迭代器,string的接口,string的遍历,访问方式


博主名称: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>::iteratorstring::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\0begin() 指向 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;
  • 因此 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 关键字的引入使代码更简洁易读,但要注意初始化、数组、传参等限制
  • reservereverse 各有特定用途,需要根据实际需求选择
  • 注意区分不同类型的迭代器及其适用场景(正向/反向、普通/常量),确保程序的正确性和效率
  • const 对象必须使用 const 迭代器;范围 for 默认按值拷贝,修改容器要加引用

任何的建议和批评欢迎指出,我们共同进步!

相关推荐
小小龙学IT3 小时前
C++ std::vector 底层实现深度解析:内存布局、扩容策略、移动语义与迭代器失效
开发语言·c++·算法
码匠许师傅3 小时前
【C++ 面试真题】聊聊 volatile 和 mutable
开发语言·c++·面试
(Charon)3 小时前
【C++】线程安全队列(二):生产者消费者模型与 condition_variable 阻塞等待
c语言·开发语言·c++
10岁的博客4 小时前
【C++题解】最多共线点问题:小飞拿蛋糕游戏的最优策略
c++
東隅已逝,桑榆非晚5 小时前
类和对象(中)
c++·笔记
wuminyu5 小时前
JDK21解决虚拟线程IO阻塞原理剖析
java·linux·c语言·jvm·c++
MC皮蛋侠客5 小时前
TDengine C++ 系列(4):时序查询——窗口聚合、插值、连接与时序函数
大数据·c++·tdengine
junuo5 小时前
10_C++内存分区与动态内存管理
c++·编程语言
欧特克_Glodon6 小时前
OpenCV计算机视觉开发入门与实践<二>:图像噪声与图像处理
c++·图像处理·opencv·计算机视觉