1.迭代器的种类与性质
种类:
iterator
reverse_iterator
const_iterator
const_reverse_iterator
性质:
单向(只支持++运算符):forward_list/unordered_map
例unoredered_map
双向(只支持++/--运算符): list/map/set
例:list
随机(支持++/--/+/-运算符):vector/string/deque
几种迭代器之间是继承关系,前面的迭代器可以兼容后面的迭代器,后面的不能兼容前面的.
rendom->Bidirectional->forword->input/output
input和output是只能读和只能写,是定义没有容器时的迭代器.
例:vector

意义:有不少函数模板对迭代器的性质有要求
例:reverse
从兼容性上讲:reverse只对双向和随机有效.
例:find

input就说明find兼容所有容器.
2.核心功能介绍
(1)push_back和emplace_back
二者在尾插内置类型时没有任何区别,在类类型的初始化才有一点区别.

剩下就介绍一下使用了
list中有自己的sort
使用less<int>(升序)
greater<int> (降序)

merge():两链表合并,要求两链表有序

unique():去重,要求数据有序

remove():去除特定值

splice(iterator pos,list& x)将x的所有值剪切到pos之前(pos是this的迭代器)
splice(iterator pos,list& x,iterator i)将x在i处的值剪切到pos之前
splice(iterator pos,list& x,iterator i,iterator j)将x从i到j处的值剪切到pos之前
x也可以是this自己,实现自剪切.



如上面的3个++,可以发现j这个位置是没有被切的.

被切的是真的没了数据了.
3.模拟实现
(1)大框

对list_node模版中的list_node*进行说明:完整的是list_node<T>*,但即使不写<T>,编译器也会自己补全<T>,要<T>的原因
(2)初始化
list_node的构造函数

list的无参构造

(3)push_back()

此处对new node(val)这里进行解释,new会先调用构造函数构造出一个list_node类型的变量(val为参数)此处如果list_node没有以val为参数的构造函数,编译器默认调用的是拷贝构造(没有调用默认拷贝)此时就会试图将val强转为list_node类型,因此没有val的构造函数时报的是无法强转的error.
同时还要补上缺省值T()作为默认构造函数.
(4) empty 和 size

(5)迭代器的设计(基础版)
补充:typedef 也是受访问限定符的限制的
说明:由于常规指针无法实现结点的之间的移动,因此需要对常规指针进行包装,改变其中的一些功能来让其更好的实现修改节点和遍历链表的功能.


->重载返回&是因为在使用->时编译器会自动再加一个->(后面解释)
(4)内部实现begin和end,记得typedef的时候加上<T>

上面使用了含单参构造函数时编译器可以可以进行隐式类型转换,完整调用应为:
二者只是从隐式类型转换变成了先显示转换再return.
(5)insert(const T& pos)在pos前面插入.

实现了insert后就可以使用insert来对push_back和pusk_front进行赋用了.

首先是后置++/--

在此处说明一下iterator的内部不用写深拷贝和析构的原因:
迭代器的目的是查找原链表的数据,使用浅拷贝就可以指向原链表的数据,深拷贝就相当于创建了一个与原链表中的一个结点数据相同但是单独空间的节点,虽然也能实现该有功能,但是浪费空间.
下面对->为什么是地址进行解释:

ti->_a,本来应该是_a的地址,然而却是1,原因在于编译器帮我们省略了一次->,正常情况下是下面那种情况,但为了美观,编译器帮我们写了.常规类型直接解引用即可.


(6)erase和pop_back/pop_front


(6)打印以及const迭代器的实现(基础版本)


可以发现const container 需要const迭代器,因此我们要实现const_iterator.
下面是与非const迭代器唯一不同的的地方
需要注意的是const类型的构造函数不能再用&了,不知道什么原因反正编译器识别不到该构造函数,变成形参就行了.

const_iterator的获取.
(8)template的按需实例化

如上图,对const对象进行解引用但是没有报错,原因是该函数是函数模板,main函数中没有调用这函数,因此编译器并没有生成对应的代码,也就不会发现这里的错误.
(9)迭代器的进阶写法
首先总结基础用法:创建了两个迭代器类list_iterator和list_const_iterator
list_const_iterator与const_iterator的区别就在于list_const_iterator对*和->的返回值进行了const修饰(如果list_iterator的构造也用的新参构造的话,二者类里面的区别就在这了).
然后分别为二者设计了返回iterator和const_iterator的begin和end函数(const_iterator的函数还加了const修饰)
显然可以发现二者的相似度很高,因此二者因该可以用一个类模板来实现.



加多了两个const重载(其实不加也可以)
这些就是迭代器类内部修改的地方,其他地方就将构造函数参数的引用去掉了,此外没有变化.
下面是源代码

list内部就将typedef修改了一下此外没有变化.
下面对上面能成立的原因进行解释:
首先对对同一个变量_head/_head->next,编译器能区分且赋T的原因:
两个begin()/end()能形成重载的原因在于const修饰函数,这个const也只有这个用途了.
(1)首先const函数修饰可以区分容器是否被const修饰,然后无论容器是否被const修饰,_head/_head->next的类型都是list_node*,因此不影响T的赋值,然后根据iterator/const_iterator的原型进行赋值即可.
(2)list_iterator中的self会自动根据传进来的类型进行转换(因为self就是本身)
总结:这就是将list_iterator转换为了类模板,让编译器帮我们实现const_iterator和iterator了.
7.迭代器失效
insert在list中不存在失效,因为其在数据插入后的相对位置没有改变.
erase改变了,因为pos在erase后就删除变成野指针了.
因此erase要返回pos的下一个位置的迭代器,同一写法使insert也要返回pos位置.


8.析构,clear,和拷贝构造,=重载

iterator不实现拷贝构造的原因其只涉及修改和遍历结点的功能,但list本身的拷贝构造要实现深拷贝来然俩个list指向不同的空间.

为什么不用swap的方法:swap中必须是形参,调用拷贝构造,拷贝构造中又有swap,死循环了.

9.初始化新方式
std的list支持的一种初始化方式.
使用的是c++11中才有的类模板initializer_list<T> 是专门用于这种{}初始化的.
这个类的内部有一个数组存储数据,两个指针分别指向{}数据的最左边和最右边.
使用:

说白了就是一个含参构造函数,只是参是一个类,压这个类的数据给list而已,因此 = { }.本质只是一个隐式类型转换.下面的构造方法也是可以的.

常规的隐式构造都支持.