大家好,我是苏貝,本篇博客带大家了解如何自主实现list,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
下图是实现list的第一步
1. 构造函数
写出list的构造函数:创建哨兵位
因为要new Node,Node是自定义类型,因此要调用它的默认构造函数。我们来自己写一下Node的默认构造函数
2. push_back
3. 迭代器
写完了push_back后,我们想将list的内容打印出来,需要用到迭代器,所以我们要在list里面定义迭代器iterator
在前面实现string和vector中,迭代器都是指针,那list的迭代器也是指针吗?不是。String和vector的迭代器是指针的原因是:string/vector的是一块连续的空间,指针it++就是从当前元素跳到下一个元素
可是list不同,它是由许多不连续的空间通过指针链接在一起,指针it++不一定是从当前元素跳到下一个元素,所以list的迭代器是需要我们自己写的自定义类型
在命名空间zy中声明迭代器的类
我们还需要实现迭代器的!=、前置/后置++/–、*it的操作
问:需不需要重载>/<呢?不需要,因为比较的是地址,我们不能保证list中后面元素的地址一定大于前面元素的地址,因此重载>/<是无意义的
因为形参类型listIterator比较长,所以我们typedef一下
完成上面的3种操作后,再于类list 里写begin/end函数
Begin/end的返回值类型是迭代器,因此用_head->_next来新建一个迭代器x,return x。也可以返回匿名对象
还可以直接返回_head->_next,这是因为单参数的构造函数支持隐式类型转换,iterator的构造函数正是单参的,因此可以直接用_head->_next来创建一个类iterator的临时对象
这样就可以遍历list
4. Insert/erase/push_back/push_front/pop_back/pop_front
erase会导致迭代器失效,所以要返回pos对应的下一个元素的迭代器
接下来的4个函数会对insert/erase进行复用
注意:尾删的时候,pos不是end(),而是end()的前一个
5. size/empty
6. operator->
我们上面的测试都是用list来的,如果list<>中的类型是自定义类型,如下图的A,那我们的遍历也要修改,因为*it是A,A有2个成员变量
方法1:
既然*it是A,那么(*it)._a1就是_a1的值
方法2:
我们的迭代器是想让它类似于指针那样使用的,下图的指针ptr要访问_a1,就只需要ptr->_a1,我们的迭代器也想这样使用,那么就需要->运算符重载
返回的为什么是_data的地址?下面会说
It->_a1和it.operator->()->_a1是等价的。回答上面的问题,为什么->运算符重载的返回值是_data的地址?因为只有返回地址,才能通过地址->_a1来访问_a1。所以准确来讲,要想访问_a1,不应该是it->_a1,而应该是it->->_a1,但是编译器为了可读性,省略了一个->
7. const成员
我们在命名空间zy里再写一个函数来打印list的内容,因为我们对x只想读,因此用const修饰x
但是我们之前写的begin/end函数都是非const修饰的,因此const对象x不能调用非const的begin/end。为了解决这一问题,我们可以直接对begin/end用const修饰吗?
不能。直接用const修饰begin/end会导致PrintListInt函数的被const修饰的形参x的内容能被修改,为什么?
直接用const修饰begin/end,那就是const/非const对象都能调用begin/end,begin/end的返回值类型是iterator,类iterator对*运算符重载的返回值类型是引用,可读可写,因此这样的话,即使是const对象也能修改内容。
可是const对象本不应该能被修改,因此不能直接用const修饰begin/end。
解决方法:写begin/end的重载函数(用const修饰)
重载的Begin/end的参数用const修饰,那函数的返回值类型是什么?是const iterator吗?
问:const修饰的对象x,我们是不想让迭代器的指向修改,还是不想让迭代器指向的内容修改?迭代器指向的内容即x的内容。迭代器的指向是需要被修改的,如it++,让迭代器从当前元素指向下一个元素
如果是const iterator,那就是不想让迭代器的指向修改,而迭代器指向的内容可以修改。就像const int a=10;我们不能修改a,但是可以通过指针来修改a的值。
因此我们用const_iterator作为返回值类型,该类型之前没有被声明,因此我们需要声明该类型
声明const_iterator类型最简单的方法就是将iterator类(即listIterator类)拷贝一份,再对*/->的运算符重载修改
再于list类中typedef一下constlistIterator
这样就能保证const修饰的对象x,其内容不能被修改
但是listIterator和constlistIterator类很相似,只有类名和*/->的运算符重载的返回值类型不同,那我们可不可以写成一个模板类呢?将*/->的运算符重载的返回值类型作为模板参数
8. clear/析构函数
9. 拷贝构造/operator=
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️