跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ STL 基础:迭代器、auto 与范围循环

C++ STL 包含容器、算法和迭代器等核心组件。迭代器作为访问容器的统一接口,遵循左闭右开区间原则。auto 关键字在 C++11 后用于编译期类型推导,简化复杂声明,但需注意引用和 const 的剥离规则。范围 for 循环是语法糖,底层基于迭代器实现,支持按值、引用及常量引用遍历,提升代码可读性与安全性。

涅槃凤凰发布于 2026/3/21更新于 2026/5/2012 浏览
C++ STL 基础:迭代器、auto 与范围循环

STL 简介

什么是 STL

STL(Standard Template Library,标准模板库)是 C++ 标准库的核心组成部分,它不仅提供了可复用的组件库,更是一个集成了高效数据结构与算法的软件框架。

STL 的六大组件

由于历史原因,string 类型先于 STL 出现,STL 后来由惠普实验室开发并开源,因此人们通常不将 string 归入 STL 范畴。

迭代器

迭代器(Iterator)是 C++ STL 中最精妙的设计之一,如果把 STL 的容器比作各种不同类型的仓库(数组、链表、树),那么迭代器就是一把万能钥匙,它让你不用关心仓库内部的具体构造,就能以统一的方式访问里面的每一个货物。

迭代器的本质:泛化的指针

迭代器的行为在视觉和操作上都非常像普通的 C 语言指针,你可以对它进行解引用(获取数据)和移动(指向下一个数据)。

例如:vector::iterator it 注:这里的 iterator 是通过内置类型(一般来说是指针)进行 typedef 重命名,即泛化后的产物 迭代器的一般操作如下所示: ① *it (解引用):获取迭代器当前指向的元素的值。 ② ++it 或 it++ (递增):让迭代器移动到容器中的下一个元素。 ③ == 和 != (比较):判断两个迭代器是否指向同一个位置。 对于迭代器,我们可以初步简单认为其是一个高级版本的指针。

核心区间的概念:左闭右开

一般而言有如下两个函数返回特定位置的迭代器: ① begin():返回指向容器中第一个元素的迭代器。 ② end():返回指向容器中最后一个元素之后位置的迭代器,它不指向任何实际存在的元素,可以理解为一个'越界哨兵'。 因此,在 C++ 中,容器和算法使用迭代器时都严格遵循左闭右开区间这一基本原则,即 [begin, end)

A. 这种设计有一个巨大的好处:当 begin() == end() 时,可以直接用来判断容器是否为空。 B. 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()) {
        // 使用 *it 获取当前指向的值
        cout << *it << " ";
        ++it;
    }
    //预计输出:10 20 30 40 50
    return 0;
}

迭代器的分类(初步了解)

不同的数据结构底层内存分布不同,因此它们提供的迭代器能力也不同,这是连接数据结构与算法的关键: ①随机访问迭代器:最强大的迭代器,支持像指针一样进行算术运算,比如 it + 5(直接跳到 5 个位置后)。 注:底层是连续内存的容器拥有它,比如 std::vector 和 std::deque,只有拥有这种迭代器的容器,才能使用 std::sort 进行快速排序。 ②双向迭代器:支持向前 (++) 和向后 (--) 移动,但不能跨越式跳跃。比如基于链表实现的 std::list。 ③单向迭代器:只能单步向前移动 (++),比如单向链表 std::forward_list。

auto 关键字

在现代 C++11 以后,auto 绝对是提升开发效率和代码可读性的'神器',它能帮你省去大量敲击键盘的时间,并有效避免类型拼写错误。

核心概念:编译期类型推导

在使用 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; 是无法通过编译的。

auto 的关键使用场景

1.拯救极其冗长、复杂的类型声明 在学习 STL 和算法时,你经常会遇到嵌套极深的数据结构。如果没有 auto,代码会写得非常痛苦且容易出错。 代码示例:简化数据类型的代码量

#include<iostream>
#include <string>
#include <map>
using namespace std;
int main() {
    map<string, string> dict = { { "apple", "苹果" },{ "orange","橙子" }, {"pear","梨"} };
    //过于冗余
    map<string, string>::iterator it = dict.begin();
    //通过 auto 进行简化
    auto it = dict.begin();
}

注意事项

引用和 const 限定符的推导规则

使用 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 被丢弃)
  1. 拆解 auto a = rx; (为什么引用属性被丢弃?) 前情回顾:int x = 10; int& rx = x; 其中 rx 是 x 的引用。 在 C++ 里,引用仅仅是个别名(就像 x 的小名)。当你使用 rx 的时候,你实际上就是在操作 x 本身,它的值是 10。 发生推导时:当你写下 auto a = rx; 编译器的内心戏是这样的:'你想用 rx 的值(也就是 10)来初始化一个新的变量 a,既然你没写 &(比如 auto& a),说明你不想让 a 也变成别名,你只是想要那个数字 10 而已。' 结果:编译器把 a 推导为普通的 int。它在内存里挖了一块全新的空间,把 10 复制了进去。
  2. 拆解 auto b = cx; (为什么顶层 const 被丢弃?) 前情回顾:const int cx = 10; cx 是一个被 const 锁住的整数。这里的 const 叫做顶层 const(Top-level const),意思是这个变量本身是不允许被修改的。 发生推导时:当你写下 auto b = cx; 编译器又开始思考了:你想把 cx 里的值(10)掏出来,放进一个新变量 b 里,虽然 cx 本身是被锁住的(不能改),但这跟你新创建的 b 有什么关系呢?' 结果:编译器把 b 推导为普通的 int,把 10 复制了进去。b 是一个自由的、可以随意修改的普通变量。 代码示例:保留引用和 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 默认只给你最纯粹的底层类型(如 int、double),给你最大的自由,如果你想要引用(&)或者只读(const),你必须自己动手明确地写出来。

不能用于函数参数(C++20 之前)

在 C++20 之前,普通函数的参数不能用 auto,而需要使用模板。 代码示例:非法的写法

// 不能做参数
void func2(auto a) {}
auto 可以做函数返回值

核心机制:编译器会通过阅读你函数体内的 return 语句,来反向推导出这个函数到底该返回什么类型。

  1. 基础用法:让编译器替你算类型
auto multiply(int a, double b) {
    return a * b; // 编译器知道 int * double 的结果是 double,所以自动推导返回类型为 double
}
  1. 避坑指南 坑点一:多个 return 语句的类型必须'完全一致' 如果你的函数里有 if-else 分支,并且包含多个 return,那么所有 return 后面的表达式类型必须一模一样。编译器不会帮你做隐式类型转换。 代码示例:if-else 返回的类型不同
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 作为返回值永远是'值拷贝'。 代码示例:返回引用 和 const 类型的值

int global_var = 100; // 你本意是想返回 global_var 的引用
auto get_ref() {
    int& ref = global_var;
    return ref;
} // 实际结果:get_ref() 返回的是一个普通的 int(发生了值拷贝),引用属性被丢弃了!

解决办法:如果你确实想返回引用,必须显式地写成 auto& 或者 const auto&

int global_var = 100; // 你本意是想返回 global_var 的引用
auto& get_ref() {
    int& ref = global_var;
    return ref;
}

范围 for 循环

范围 for 循环是 C++11 引入的一项极其重要且实用的特性,它极大地简化了遍历数组和容器(如 std::vector, std::string, std::map 等)的代码,使其更具可读性,同时也减少了数组越界等常见错误。

基本语法

范围 for 的语法非常直观:

for (元素类型 元素变量名 : 遍历对象) {
    // 循环体
}

相关参数讲解: ①元素类型:遍历对象中元素的类型(通常搭配 auto 关键字使用)。 ②元素变量名:每次循环时,用来接收当前元素的变量。 ③遍历对象:必须是一个可以确定范围的集合(如定长数组、标准库容器等)。

三种最常用的遍历方式

按值遍历

核心特征:每次循环都会将容器中的元素拷贝给变量,修改变量不会影响容器内原本的数据。 适用场景:遍历基础数据类型(如 int, char, float),且不需要修改原数据。 代码示例:遍历基础类型 int

vector<int> nums = {1, 2, 3};
for (int x : nums) {
    cout << x << " "; // 输出 1 2 3
}
按引用遍历

核心特征:加上 & 符号,变量直接引用容器内的元素,修改变量就等于修改了容器内的原数据。 适用场景:需要修改容器内元素的内容时。 代码示例:遍历基础类型 int,并修改其内容

vector<int> nums = {1, 2, 3};
for (int& x : nums) {
    x *= 2; // 原数组变为 {2, 4, 6}
}
按常量引用遍历

核心特征:加上 const 和 &,既不会发生拷贝(节省内存和时间),又保证了数据不被意外修改(只读)。 适用场景:遍历复杂对象(例如:string, 结构体,自定义类)且只需读取时,这是最推荐的写法。 代码示例:遍历复杂对象 string 类

vector<string> words = {"Hello", "World"};
for (const string& word : words) {
    cout << word << endl;
}

结合 auto 关键字

在实际开发中,我们通常不需要手动写出繁琐的类型名,直接让编译器用 auto 推导即可: 写法一:for (auto x : container) (按值拷贝) 写法二:for (auto& x : container) (按引用,可修改) 写法三:for (const auto& x : container) (按常量引用,只读且高效) 代码示例:通过使用关键字 auto 遍历容器

vector<string> words = {"Hello", "World"};
for (const auto &word : words) {
    cout << word << endl;
}

范围 for 的本质

范围 for 并不是什么神奇的魔法,它本质上是编译器提供的一层'语法糖'。 当编译器看到范围 for 时,会自动将其转化为使用迭代器 (Iterator) 的循环:

// 你的代码
for (auto& x : container) { /*...*/ }
// 编译器实际转化为类似这样的代码:
//_begin 指向头部元素的迭代器
auto _begin=container.begin();
//_end 指向尾部元素下一个位置的迭代器
auto _end=container.end();
for( ; _begin !=_end ; ++_begin ) {
    //遍历迭代器,将容器的值赋值给 x
    auto& x = *_begin;
    /*...*/
}

目录

  1. STL 简介
  2. 什么是 STL
  3. STL 的六大组件
  4. 迭代器
  5. 迭代器的本质:泛化的指针
  6. 核心区间的概念:左闭右开
  7. 代码演示:如何使用迭代器
  8. 迭代器的分类(初步了解)
  9. auto 关键字
  10. 核心概念:编译期类型推导
  11. auto 的关键使用场景
  12. 注意事项
  13. 引用和 const 限定符的推导规则
  14. 不能用于函数参数(C++20 之前)
  15. auto 可以做函数返回值
  16. 范围 for 循环
  17. 基本语法
  18. 三种最常用的遍历方式
  19. 按值遍历
  20. 按引用遍历
  21. 按常量引用遍历
  22. 结合 auto 关键字
  23. 范围 for 的本质
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 电商系统商品管理模块设计与实现
  • Android 中高级开发核心面试题与解析
  • async_simple:轻量级 C++ 异步协程框架
  • AI 编程工具深度横评:Cursor、Copilot 与 Claude Code 怎么选
  • 《大模型应用开发极简入门》:GPT-4 与 ChatGPT 应用开发指南
  • AI 大模型全面掌握指南:从理论到实践的学习路径
  • FPGA实现双线性插值缩放:代码与实现详解
  • 使用 Git 管理 Vivado 与 Vitis 工程实践
  • AI 编程工具深度对比:Cursor、Copilot、Trae 与 Claude Code
  • 基于Go语言的命令行AI对话客户端开发与部署
  • 开源电路板查看器 OpenBoardView:.brd 文件解析工具
  • trace-spring-boot-starter 全链路日志追踪实战指南
  • Java Web 开发数据库知识复习与整理
  • 前端技术演进与现代化开发实践
  • Spring Cloud 配置中心选型实战:Nacos vs Apollo vs Spring Cloud Config
  • Linux内核源代码深度解析:从设备树到电源管理的完整技术栈
  • Llama-3.2V-11B-COT 建筑图纸尺寸矛盾自动检测实战
  • 5 种主流深度生成模型对比:VAE、GAN、AR、Flow 与 Diffusion
  • 2024 年 3 月编程语言排行榜:Python 优势显著,Rust 持续上升
  • 基于 SpringBoot 与 Vue 的 Web 咖啡点单系统设计

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online