【C++STL】双向循环链表与其迭代器的深度剖析及实现(百字短文速通)

1,双向循环链表基本结构的实现(不包含需要迭代器的部分)

先用struct封装链表的节点,这里我们仅需要提供一个构造函数即可,并且构造函数必须提供缺省值,因为会有如下使用场景:new Node();此时需要默认构造函数,所以必须有缺省值。

在list类模版中,先typedef 一下,这样方便使用Node<T>,

成员变量是双向循环链表的哨兵位头节点。

接下来是一些基本的接口函数:

空构造:仅仅需要new一个哨兵位头节点出来,但是要记得保证双向循环结构

push_back:传入一个T,不过T也有可能是自定义类型,所以要用&,const可以接收const对象和非const对象。

迭代器区间构造:

构造函数:构造时填入n个x。

值得一提的是,这个构造函数有可能跟上面的迭代器区间构造发生冲突,比如说:

list<int> lt2(5,1); 中5和1一般会被编译器默认为是int类型,于是:

对于迭代器区间构造函数:InputIterator会被推演为int,这样的话两个形参的类型都是int。

对于本构造函数:模版参数推演之后,两个参数分别是size_t和int。

系统认为两个int形参类型更加匹配,所以会去调用迭代器区间构造。

而迭代器区间的这个操作:

*first相当于对int进行解引用,所以会报错:非法的间接寻址

箭头所指处也更能证明我们的想法。

而解决方法只有重载一个如下图的函数:

这样就会调用这个函数,而不是迭代器区间的构造函数。

2,迭代器的实现以及相关函数的补充

我们知道list的底层空间不是连续的,所以原生指针的++和--不能达到我们预想的结果,我们所需要的是:++的时候走到它的next,--的时候走到它的prev。

将迭代器实现成一个类模版,对++ --以及一些其他的运算符进行重载可以解决这个问题。

2,1迭代器的实现

基本结构:

typedef是为了让代码更简洁易懂

成员变量是一个节点的指针

至于除了T之外的两个模版参数Ref和Ptr ,接下来会进行解释捏

构造函数:

++重载:

--重载:

判断是否为同一个节点:

重载*(解引用):

这里的返回值为什么不直接写成T&呢?

因为除了普通的迭代器,我们还要使用const迭代器,那样的话,返回值就是const T& 了,而const迭代器跟普通迭代器的结构极其相似,只有返回值不同,所以为了防止代码冗余,尽量复用代码,这才引入了模版参数Ref来手动控制。

重载->:Ptr模版参数的引入同上,是为了手动控制T和T*

你没看错,->的重载就是这么奇怪,这个需要我们以特殊的眼光来看待:

迭代器定义operator->的原因是:

迭代器是像指针一样的东西,如果是原生指针,解引用就可以获得对象,如果是一个结构,想要得到结构里的成员变量,就需要通过->来获得,比如日期类:

所有类型重载->都要这样,算是个奇怪的模版吧,至少我觉得很奇怪。

2,2相关函数的补充

看,这里我们手动传模版参数就可以实现iterator和const_iterator

begin,end,cbegin,cend:

erase:

clear:复用erase

析构函数:复用clear();

拷贝构造和赋值运算符重载:

这里很简单,唯独有一点要说的是,

拷贝构造函数是借助构造函数和std::swap来实现的,swap后,tmp对象会调用析构函数自动析构,

考虑到这一点,我们必须对this指向的对象进行处理,要不然就会把一个野指针交换给tmp,析构的时候会出现内存问题的。

在此之前,我们的处理都是将类中的指针类型的成员变量置为nullptr,

比如string:

比如vector:

但是这次不一样了,因为析构函数的内部必然需要对链表进行遍历,一边遍历一边释放节点,而遍历就需要用到iterator,iterator一旦++就会对当前节点进行解引用找到下一个节点,如果将_head置空,就会对空指针进行解引用。

所以我们要搞一个最简单的链表结构——空链表,以便于析构。

insert:

返回新插入节点的迭代器是为了符合官方库,不必在意这里。

3,反向迭代器的实现以及相关函数的补充

3,1反向迭代器的实现

Iterator这个模版参数,传的是谁的迭代器,实现的就是谁的反向迭代器,至于Ref和Ptr,

跟上面解释的一样。

成员变量就是某个容器的正向迭代器

构造函数:

重载*:

没错,解引用反向迭代器返回的是前一个位置的数据,这是因为下图:

由于rbegin和rend的位置跟begin和end的位置对称,所以,如果解引用返回当前节点的数据的话,就会访问到_head中的随机值,必须返回前一个位置的数据才行。

重载->:

重载++--

判断是否为同一个节点:

3,2相关函数的补充

这里是要先typedef出const_reverse_iterator的,因为如果先typedef出reverse_iterator的话,下面就会出现歧义。

ps:拷贝构造函数往往要对成员变量进行初始化,以便于交换之后析构。

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值