1.迭代器:用于容器中数据的遍历操作
数据结构:描述数据之间的关系及操作。数据结构通常由代码实现,也就是下面提到的动态数组等。
stl中存在一些常见的已经封装好(开箱即食)数据结构相关的模板类,例如vector(动态数组)
,list(链表)
, stack(栈)
,queue(队列)
,map(hash表/红黑树,C++中map的内部是用红黑树实现,但外在表现形式是hash表)
等容器。这些类通常都有一些最基本的操作,例如:增加,删除,修改,遍历等等。
C++为了方便统一,采用了设计模式中的迭代器模式,也就是统一的提供了一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。我们一般对这些数据结构的遍历都可以无脑使用迭代器,而不关心内部存储的差异。
1.1 普通数组与动态数组定义及遍历方式
1.1.1 数组:普通的数组, 一旦申请,不能再扩增
定义及遍历方式:
int ary[5] = { 1, 2, 3, 4, 5 };
int* pAry = new int[5];//使用new创建pAry int数组指针
//遍历方式
for (int i = 0; i < sizeof(ary); i++){
std::cout << ary[i] << std::endl;
}
1.1.2 动态数组:vector,不用指定其大小,会根据数组当前的使用情况进行动态的扩容
定义及遍历方式:
普通访问方法
:需要知道数据结构,数据是按照一定的顺序排布,但是其他数据结构可能不是,就无法使用
迭代器的方式
:C++为了方便统一,采用了设计模式中的迭代器模式,也就是统一的提供了一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。就是一种套路
//容器(数据的封装)---动态数组 不用指定其大小,会根据数组当前的使用情况进行动态的扩容
//模板类型
std::vector<int> v;
//插入数据
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
//普通访问方法,需要知道数据结构,数据是按照一定的顺序排布
//但是其他数据类型可能不是
for (int i = 0; i < v.size(); i++){
std::cout << v[i] << std::endl;
}
//使用迭代器的方式遍历数组
std::vector<int>::iterator it; //迭代器,模板类中的内部类
for (it = v.begin(); it != v.end(); it++) {
std::cout << *it << std::endl; //用*it来访问模板类的具体的值
}
vector中数据保存在堆上,以fd fd fd fd 作为上下限的标志位
1.2 链表的遍历方式
迭代器看着是比较复杂,但是需要用统一的眼光去看待问题,不仅是动态数组,链表等其他数据结构也是可以用迭代器来进行遍历。
//统一的遍历方式 链表
std::list<std::string> l;
l.push_back("hello1");
l.push_back("hello2");
l.push_back("hello3");
for (std::list<std::string>::iterator it2 = l.begin(); it2 != l.end(); it2++) {
std::cout << (*it2).c_str() << std::endl; //* it 来访问模板类的具体的值
}
迭代器中关于类型的书写是不是感觉特别麻烦呢?碰到这种一下就懵逼了!
2.类型推导auto:编译器在编译时自动推导出数据类型
在传统 C 和 C++中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
C++ 11 引入了 auto
和 decltype
这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
auto
在很早以前就已经进入了 C++,但是他始终作为一个存储类型的指示符存在,与 register
并存。在传统 C++ 中,如果一个变量没有声明为 register
变量,将自动被视为一个 auto
变量。而随着 register
被弃用,对 auto
的语义变更也就非常自然了。
替代前面提到的数据类型手写的形式,编译器在编译时自动推导出数据类型
。
//auto 类型推导关键字
for (auto it2 = l.begin(); it2 != l.end(); it2++) {
std::cout << (*it2).c_str() << std::endl; //* it 来访问模板类的具体的值
}
一些其他的常见用法:
auto i = 5; // i 被推导为 int
auto arr = new auto(10) // arr 被推导为 int *
注意:auto
不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板):
int add(auto x, auto y);
此外,auto
还不能用于推导数组类型:
#include <iostream>
int main() {
auto i = 5;
int arr[10] = {0};
auto auto_arr = arr;
auto auto_arr2[10] = arr;
return 0;
}
3.编译器进化后遍历的写法
C++模仿javascrip等脚本,对遍历这种功能进行封装,采用以下这种形式进行遍历。
for(std::string str : l){
std::cout << str.c_str() << std::endl;
}
4.再进化,类型推导的写法
for (auto str : l) {
std::cout << str.c_str() << std::endl;
}
5.学习视频地址:迭代器及类型推导auto
6.学习笔记:迭代器及类型推导auto笔记