当前位置: 首页 > news >正文

《C++初阶之STL》【auto关键字 + 范围for循环 + 迭代器】

【auto关键字 + 范围for循环 + 迭代器】目录

  • 前言:
  • --------------- auto关键字 ---------------
    • 1. 什么是auto?
    • 2. 使用关键字auto时需要注意什么?
    • 3. 怎么使用auto关键字?
  • --------------- 范围for循环 ---------------
    • 1. 什么是范围for循环?
    • 2. 怎么使用范围for循环?
    • 3. 范围for循环有什么优势?
  • --------------- 迭代器 ---------------
    • 1. 什么是迭代器?
    • 2. 迭代器有哪些?
      • 2.1:按“功能强弱”进行划分
      • 2.2:按“读写权限+遍历方向”进行划分
    • 3. 怎么使用迭代器?

在这里插入图片描述

往期《C++初阶》回顾:

/------------ 入门基础 ------------/
【C++的前世今生】
【命名空间 + 输入&输出 + 缺省参数 + 函数重载】
【普通引用 + 常量引用 + 内联函数 + nullptr】
/------------ 类和对象 ------------/
【类 + 类域 + 访问限定符 + 对象的大小 + this指针】
【类的六大默认成员函数】
【初始化列表 + 自定义类型转换 + static成员】
【友元 + 内部类 + 匿名对象】
【经典案例:日期类】
/------------ 内存管理 ------------/
【内存分布 + operator new/delete + 定位new】
/------------ STL ------------/
【泛型编程 + STL简介】

前言:

Hi~ 小伙伴们大家好呀 (●’◡’●)!这周末我们就要进入 “三伏天” 的入伏阶段啦🔥,一年中最热、最潮湿的时期即将正式开启,大家一定要注意做好防暑措施哦~🌞💦

今天我们要学习的【auto 关键字 + 范围 for 循环 + 迭代器】内容📚,主要是为后面学习 STL 容器打基础的哟 (。・ω・。)ノ♡
虽然这部分知识相对简单(◕‿◕✿),可大家也不能掉以轻心,要认真掌握呀(ง •_•)ง

--------------- auto关键字 ---------------

1. 什么是auto?

:在进行string类的模拟实现之前,我们要先来学习一下C++的两个小语法

  1. 关键字auto
  2. 范围for循环

方便后面我们进行模拟实现。

在 C语言 和 C++ 中,auto 的含义有所不同:

C 语言中的 auto

auto: 是 C 语言的 存储类型说明符,用于声明具有 自动存储期的局部变量

  • 具有自动存储期的变量在进入声明它的程序块时被创建,在该程序块活动时存在,退出程序块时被撤销。

  • 在函数内部定义的变量,若未声明为其他存储类型(staticregisterextern ),默认就是自动变量,所以实际中auto关键字常被省略 。例如int a = 0;auto int a = 0; 是等价的。

  • 另外

    • 注意一:用auto声明变量时可不进行初始化。
    • 注意二:当省略数据类型时,auto修饰的变量默认为int型 。

C++中的 auto

  • C++98 和 C++03 标准:与 C 语言中 auto 的含义一致,用于声明自动变量,但因即使不使用 auto 声明,变量也拥有自动生命期,所以该用法多余且极少使用 。

  • C++11 及以后标准auto 被重新定义为自动推断变量类型的 类型指示符

    • 使用 auto 定义变量时必须进行初始化。

    • 在编译阶段,编译器会根据初始化表达式来推导 auto 实际代表的类型,此时 auto 只是一个类型声明时的 “占位符” 。

      auto num = 10; // num会被推导为int类型
      auto str = std::string("hello"); // str会被推导为std::string类型
      

在 C++ 后续标准中,auto 的功能进一步扩展:

C++14

  • auto可用于推导普通函数的返回类型

    • 例如auto func() { return 42; } ,编译器会根据return语句推导出函数返回类型为int
  • auto可作为泛型 Lambda 表达式的参数类型提高代码复用性

C++17

  • 引入模板参数推导,允许使用 auto 指定函数模板参数类型时,编译器可根据实参推导模板参数类型
  • 引入结构化绑定,允许使用 auto 解构数组结构体tuple方便访问复合数据类型元素

总结:auto 在 C++ 中的应用,尤其是在编写模板代码或处理复杂类型时,能大大简化代码编写,提高编程效率 。

2. 使用关键字auto时需要注意什么?

在 C++ 中使用auto关键字时,需要注意以下几点:

1. 必须初始化:

  • auto 必须通过初始化表达式推导类型,否则会导致编译错误。

    auto x;       // 错误:未初始化,无法推导类型auto x = 10;  // 正确:根据10推导为int
    

2. 推导规则可能与预期不符:

(1)忽略顶层const引用

  • auto 会忽略初始化表达式的顶层const引用属性,除非显式指定

    const int a = 10;
    auto b = a;      // b的类型是int(忽略顶层const)
    auto& c = a;     // c的类型是const int&(保留const)int x = 10;
    int& ref = x;
    auto y = ref;    // y的类型是int(忽略引用)
    

(2)数组函数会退化为指针

  • 当初始化表达式是数组函数时,auto 会将其推导为指针类型除非使用decltype(auto)

    int arr[5] = {1, 2, 3, 4, 5};
    auto ptr = arr;  // ptr的类型是int*(数组退化为指针)
    

3. 声明指针或引用时的语法差异

  • 指针类型:使用auto声明指针时,autoauto*等价(*可加可不加),因为编译器会根据初始化表达式自动推导为 指针类型

    int* p = new int(10);auto ptr1 = p;   // ptr1类型为int*
    auto* ptr2 = p;  // ptr2类型也为int*(与ptr1等价)
    
  • 引用类型:声明引用时必须显式使用&,否则auto会推导为 值类型(非引用)

    int x = 20;auto& ref = x;   // 正确:ref为int&(引用)
    auto val = x;    // 错误:val为int(值类型,非引用)
    

4. 同一行声明多个变量时类型必须一致

当在同一行使用 auto 声明多个变量时,所有变量的类型必须完全一致,否则会编译报错。

  • 因为:编译器仅对第一个变量的类型进行推导,其他变量强制使用该类型。

    //错误示例:
    auto a = 10, b = 3.14;  // 错误:a推导为int,b推导为double(类型不一致)
    auto* p1 = &a, p2 = &b; // 若a和b类型不同,p2可能为不同类型的指针//正确示例:
    auto a = 10, b = 20;    // 正确:a和b均为int
    auto* p1 = &a, p2 = &b; // 正确:p1和p2均为int*(假设a和b为int)
    

5. 不能作为函数参数,但可作为返回值(谨慎使用)

  • 作为函数参数auto 无法用于函数参数的类型声明。

    • 因为:函数参数需要明确的类型。

      // 错误示例:
      void func(auto x);  // 错误:auto不能作为函数参数类型
      
  • 作为函数返回值:C++14 允许auto作为函数返回类型(需通过return语句推导唯一类型),但需注意:

    • 函数体必须可见不能在头文件中声明后在源文件中定义

    • 若存在多个return语句,推导的类型必须一致

    auto add(int a, int b) // C++14及以后
    {  return a + b;  // 返回类型推导为int
    }
    

3. 怎么使用auto关键字?

代码示例1

#include <iostream>
using namespace std;//可以作返回值,但建议谨慎使用(需确保返回类型明确)
auto func()
{return 3;  //返回类型被推导为int
}int main()
{cout << "============== 基础类型推导 ==============" << endl;int a = 10;auto b = a;      // b的类型推导为intauto c = 'a';    // c的类型推导为charauto d = func(); // d的类型由func1()的返回类型决定//错误示例:auto变量必须初始化,编译器无法推导未初始化变量的类型//auto e;  // 缺少初始化表达式// 打印类型信息(不同编译器输出可能不同)cout << "b的类型是:" << typeid(b).name() << endl;  //可能输出"int"cout << "c的类型是:" << typeid(c).name() << endl;  //可能输出"char"cout << "d的类型是:" << typeid(d).name() << endl;  //取决于func1()的返回类型cout << "============== 指针和引用推导 ==============" << endl;int x = 10;//知识点1:使用auto声明指针时,auto和auto*等价(*可加可不加)auto y = &x;   // y的类型推导为int*auto* z = &x;  // 显式指定指针类型,z的类型为int*//知识点2:声明引用时必须显式使用&auto& w = x;   // w的类型推导为int&cout << "y的类型是:" << typeid(y).name() << endl;  // int*cout << "z的类型是:" << typeid(z).name() << endl;  // int*cout << "w的类型是:" << typeid(w).name() << endl;  // 注意:这里输出的是int,但是其实w的类型是:int&//因为:引用类型被 “剥除”:当对引用类型使用 typeid 时,返回的是被引用对象的类型,而非引用类型本身cout << "============== 多变量声明限制 ==============" << endl;auto aa = 1, bb = 2;  // 合法:aa和bb都被推导为intcout << "aa的类型是:" << typeid(aa).name() << endl;  // intcout << "bb的类型是:" << typeid(bb).name() << endl;  // int//错误示例://auto cc = 3, dd = 4.0;  // cc是int,dd是double//auto多变量声明时,所有变量必须推导为同一类型cout << "============== 数组类型限制 ==============" << endl;//错误示例:auto不能用于声明数组类型//auto array[] = { 4, 5, 6 };return 0;
}

在这里插入图片描述

代码示例2

#include <iostream>
#include <string>
#include <map>
using namespace std;int main()
{/*------------------创建一个map字典------------------*/// 创建一个map字典,存储英文单词到中文的映射// key类型为std::string,value类型为std::stringstd::map<std::string, std::string> dict = {{ "apple", "苹果" },  // 键值对1{ "orange", "橙子" }, // 键值对2 { "pear", "梨" }      // 键值对3};/*------------------创建一个迭代器------------------*/ //原始写法(较冗长):// std::map<std::string, std::string>::iterator it = dict.begin();//使用auto自动推导迭代器类型(现代C++推荐写法):auto it = dict.begin();  // it会被自动推导为std::map<std::string, std::string>::iterator类型/*------------------使用迭代器遍历map字典------------------*/while (it != dict.end()){//1.输出当前键值对//1.1:it->first 表示key(英文单词)//1.2:it->second 表示value(中文翻译)cout << it->first << ":" << it->second << endl;//2.移动到下一个元素++it;}return 0;
}

在这里插入图片描述

--------------- 范围for循环 ---------------

1. 什么是范围for循环?

范围for循环(Range-based for loop):是 C++11 引入的一种语法糖,用于简化遍历容器序列的过程。

  • 传统的 for 循环需要显式指定循环范围,不仅代码冗长,还容易因索引越界等问题引入错误,而基于范围的 for 循环则提供了更简洁、易读的语法,避免了传统 for 循环中迭代器索引的显式使用,自动完成迭代过程。
  • 从实现原理上看,范围 for 循环是迭代器模式的语法糖。 编译器会自动将其转换为等价的迭代器遍历代码,包括迭代器的获取元素访问边界判断。这种转换在汇编层面表现为与手动编写的迭代器代码基本一致,因此不会引入额外的性能开销。

2. 怎么使用范围for循环?

范围 for循环基本语法:

for (元素类型 变量名 : 容器/序列) 
{// 使用变量名访问当前元素
}
  • declaration:定义一个变量,用于存储每次迭代时从 range 中取出的元素。
  • range:表示要遍历的范围,可以是数组、容器(std::vectorstd::list)、初始化列表等。

范围 for循环工作原理:

  • 迭代对象
    • 对于 数组,直接遍历数组的每个元素。
    • 对于 标准库容器vectormap),使用容器的begin()end()迭代器。
    • 对于 自定义类型,需提供begin()end()成员函数或全局函数。
  • 变量类型
    • 可使用 auto 自动推导元素类型
    • 若需修改元素值,应声明为引用类型(auto&const auto&

代码片段示例:

1. 遍历数组

int arr[] = {1, 2, 3, 4, 5};
for (int num : arr) 
{std::cout << num << " ";  // 输出: 1 2 3 4 5
}

2. 遍历 vector

#include <vector>std::vector<int> vec = {1, 2, 3};
for (auto& num : vec) // 使用引用允许修改元素
{  num *= 2;
}

3. 遍历 map

#include <map>std::map<int, std::string> dict = 
{{1, "one"}, {2, "two"}
};for (const auto& pair : dict) 
{std::cout << pair.first << ": " << pair.second << "\n";
}

4. 初始化列表

for (int x : {10, 20, 30}) 
{std::cout << x << " ";  // 输出: 10 20 30
}

3. 范围for循环有什么优势?

#include <iostream>
#include <string>
#include <map>
using namespace std;int main()
{// 定义一个包含5个整数的数组int array[] = { 1, 2, 3, 4, 5 };/******************** C++98 风格的遍历 ********************//** 传统遍历方式特点:* 1. 需要手动计算数组长度* 2. 使用下标访问元素* 3. 需要维护循环变量i*///使用for循环修改数组中的元素for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){array[i] *= 2;}//使用for循环输出修改后的数组for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){cout << array[i] << " ";}cout << endl;/******************** C++11 风格的遍历 ********************//** 范围for循环特点:* 1. 自动推导元素类型(使用auto)* 2. 自动处理数组边界* 3. 代码更简洁*///使用引用修改元素(auto&)for (auto& e : array)e *= 2;  //使用值访问元素(auto)for (auto e : array)cout << e << " ";cout << endl;/******************** 字符串遍历示例 ********************/string str("hello world");/** 字符串遍历说明:* 1. auto自动推导为char类型* 2. 不需要考虑字符串长度* 3. 可以方便地处理每个字符*/for (auto ch : str){cout << ch << " ";  // 输出:h e l l o   w o r l d}cout << endl;return 0;
}

在这里插入图片描述

--------------- 迭代器 ---------------

1. 什么是迭代器?

迭代器(Iterator):是一种抽象的编程概念,用于在容器(:数组、链表、集合、映射等)中遍历元素,并访问容器中的数据,而无需暴露容器的底层实现细节。

  • 它本质上是一个 “指针 - like” 的对象,提供了一种统一的方式来操作不同类型的容器,使得代码可以不依赖于具体容器的内部结构,从而增强代码的通用性和可移植性。

迭代器的核心作用:

  1. 遍历容器元素
    迭代器可以像指针一样逐一遍历容器中的元素,支持向前或向后移动(取决于容器类型和迭代器种类)
  2. 访问容器数据
    通过迭代器,可以读取或修改容器中的元素(取决于迭代器的类型是否支持写操作)
  3. 统一容器操作接口
    C++ 标准库中的算法(如:sortfindfor_each 等)都依赖迭代器来操作容器,使得同一套算法可以适配不同类型的容器(如:vectorlistset 等)

2. 迭代器有哪些?

:迭代器可以根据不同的划分方式划分出不同的迭代器:下面的我们将介绍以下的两种划分方式。

2.1:按“功能强弱”进行划分

类型功能支持操作
输入迭代器只能读取 容器元素
单向移动(只能递增)
不支持重复读取(类似一次性指针)
++it(递增)
*it(解引用读取)
==!=(比较)
输出迭代器只能写入 容器元素
单向移动(只能递增)
不支持读取
++it(递增)
*it = value(赋值写入)
前向迭代器支持 读取和写入(若容器允许)
单向移动
多次访问 同一元素
输入迭代器
+
可保存状态(如多次解引用同一迭代器)
双向迭代器支持 前后双向 移动(递增和递减)前向迭代器
+
--it(递减)
随机访问迭代器支持 随机访问 元素(类似指针算术运算)
可直接跳跃到任意位置
双向迭代器
+
it + nit - n
it[n]
it1 - it2(计算距离)
<, <=, >, >=(比较大小)

在这里插入图片描述

2.2:按“读写权限+遍历方向”进行划分

迭代器类型说明典型获取方式
iterator可读写正向遍历 容器元素的迭代器
在容器中正常顺序地访问和修改元素
begin()end()
const_iterator只读正向遍历 容器元素的迭代器
用于在不修改容器元素的情况下,按正常顺序遍历容器
cbegin()cend()
reverse_iterator可读写反向遍历 容器元素的迭代器
用于以逆序方式访问和修改容器元素
rbegin()rend()
const_reverse_iterator只读反向遍历 容器元素的迭代器
用于在不修改容器元素的前提下,逆序遍历容器
crbegin()crend()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3. 怎么使用迭代器?

#include <iostream>
#include <string>
using namespace std;int main()
{string s("Hello, World!");cout << "原始字符串: " << s << endl << endl;/*------------------使用iterator正向遍历(可读写)------------------*/cout << "iterator 正向遍历: ";for (string::iterator it = s.begin(); it != s.end(); ++it) {cout << *it;		 //读取元素*it = toupper(*it);  //修改元素(转换为大写)}cout << endl << "修改后的字符串: " << s << endl << endl;/*------------------使用const_iterator正向遍历(只读)------------------*/cout << "const_iterator 正向遍历: ";for (string::const_iterator cit = s.cbegin(); cit != s.cend(); ++cit) {cout << *cit;  //只读访问//*cit = 'x';  //错误:不能通过const_iterator修改}cout << endl << endl;/*------------------使用reverse_iterator反向遍历(可读写)------------------*/cout << "reverse_iterator 反向遍历: ";for (string::reverse_iterator rit = s.rbegin(); rit != s.rend(); ++rit) {cout << *rit;		   //反向读取元素*rit = tolower(*rit);  //修改元素(转换为小写)}cout << endl << "再次修改后的字符串: " << s << endl << endl;/*------------------使用const_reverse_iterator反向遍历(只读)------------------*/cout << "const_reverse_iterator 反向遍历: ";for (string::const_reverse_iterator crit = s.crbegin(); crit != s.crend(); ++crit) {cout << *crit;   //反向只读访问// *crit = 'x';  //错误:不能修改}cout << endl;return 0;
}

在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/283877.html

相关文章:

  • 基于typescript严格模式以实现undo和redo功能为目标的命令模式代码参考
  • Python-TCP编程-UDP编程-SocketServer-IO各种概念及多路复用-asyncio-学习笔记
  • 从0开始学习R语言--Day49--Lasso-Cox 回归
  • 在UniApp中防止页面上下拖动的方法
  • git@github.com: Permission denied (publickey).
  • 算法竞赛备赛——【图论】求最短路径——Dijkstra
  • 排序算法—交换排序(冒泡、快速)(动图演示)
  • uniapp问题总结
  • 并发事务~
  • 一种融合人工智能与图像处理的发票OCR技术,将人力从繁琐的票据处理中解放
  • 视频安全新思路:VRM视频分片错序加密技术
  • 小架构step系列17:getter-setter-toString
  • 智能视频分析:多行业安全防控的“AI之眼”
  • 嵌入式学习-PyTorch(7)-day23
  • Flutter Android打包学习指南
  • 如何下载视频 (pc端任何视频均可下载)
  • 英伟达Cosmos研究团队开源DiffusionRenderer (Cosmos): 神经逆向与正向渲染与视频扩散模型
  • 视频码率是什么?视频流分辨率 2688x1520_25fps采用 h264格式压缩,其码率为
  • Web攻防-PHP反序列化Phar文件类CLI框架类PHPGGC生成器TPYiiLaravel
  • blender 导入的fbx模型位置错乱
  • 【3D大比拼第一集】--max,maya,c4d,blender的命令搜索功能
  • iOS App 电池消耗管理与优化 提升用户体验的完整指南
  • 【力扣 中等 C】97. 交错字符串
  • 量化环节:Cont‘d
  • 题解:CF1829H Don‘t Blame Me
  • 相位中心偏置天线的SAR动目标检测
  • 代码随想录算法训练营第二十三天
  • Apache SeaTunnel配置使用案例
  • 【Leetcode】栈和队列算法题(逆波兰表达式、二叉树层序遍历、最小栈、栈的压入弹出序列)
  • 贪心算法(排序)