文章目录
- 1.基本结构
- 2.迭代器的实现
-
- [2.1 尾插的实现](#2.1 尾插的实现)
- [2.2 迭代器的实现](#2.2 迭代器的实现)
- 3.打印函数(模版复用实例化)
- 4.任意位置的插入删除
-
- [1. 插入](#1. 插入)
- [2. 删除](#2. 删除)
- 5.析构与拷贝构造
-
- [5.1 析构函数](#5.1 析构函数)
- [5.2 拷贝构造](#5.2 拷贝构造)
- [5.3 赋值重载](#5.3 赋值重载)
1.基本结构
与vector
和string
不同list
需要:
一个类来放入数据和指针也就是节点
一个类来连接这些节点
如下:
cpp
template <class T>
class list_node
{
T _data;
list_node<T>* _next;//指向下一个节点
list_node<T>* _prev;//指向前一个节点
};
template<class T>
class list
{
typedef list_node<T> Node;
public:
list()
{
_head = new Node;//没有内存池直接new一下即可
_head->_next = _head;
_head->_prev = _head;
}
private:
Node* _head;
};
2.迭代器的实现
2.1 尾插的实现
在写迭代器之前需要现将数据插入到链表中
那么尾插函数如何实现呢?
-
先来创建一个节点
-
将节点连接到链表中
-
通过头来找到尾节点
-
将尾节点和插入的节点连接起来
-
再将最后一个尾节点和头相连,成为新的尾节点
-
代码实现如下:
当然我们还可以加一个size
来记录我们链表的长度:
2.2 迭代器的实现
这里有一个和之前最大的区别就是:
之前无论是vector还是string都可以直接通过解引用或++
来直接拿到我们想要的值和到达下一个位置
但是我们对list的节点解引用或++
就拿不到我们想要的值和到达下一个节点
那我们该怎么办呢?
我们可以将*
和++
等操作符进行重载来拿到我们想要的内容
在将要重载的内容封装成一个类:
写完后我们就可以来完成begin和end的返回了
这里实现有三种方式:
- 创建一个类进行返回
- 用匿名对象进行返回
- 用隐式类型转换返回
再将后置++
也完成出来:
但是这是如果出现链表是存放的是结构体就会出问题:
其原因仍然是直接解引用无法直接取到值:
我们可以重载一个->
来完成进行AA
结构体进行重载
这里的使用了特殊处理,应该使用两个->
才合理,但是为了美观省略了一个
3.打印函数(模版复用实例化)
但是这里会有些问题,在函数外面写的范围for支持,而函数内的范围for不支持了
这是为什么呢?
原因就是这里是const
容器,所以要const
迭代器
要注意这两个迭代器的区别:
const_iterator |
迭代器指向的内容不能修改(本身无法进行++) |
---|---|
const iterator |
迭代器本身不能修改 |
这里要实现const_iterator
就要将原有的iterator
复制一份,后将其中部分进行修改:
但是这样实现代码就显得重复度很高了,能不能直降返回的那部分替换掉呢?
这里可以通过定义多个模版来实现这一功能:
这样我们只需要在调用的时候将要返回的参数写下来就能返回我们想要的迭代器
当然也可以用typedef
进行重命名来更加方便实现
4.任意位置的插入删除
1. 插入
实现比list简单一点
先通过Node*
找到插入的位置后
将将节点的链接方式改变一下就行了
当尾插写完后我们的其他插入方式就可以直接进行调用了
2. 删除
也是通过Node*找到删除节点的下一个节点的位置和前一个节点位置
将节点删除后将这两个节点相连即可:
当然为了放在删除后迭代器失效,我们可以再删除后返回下一个节点的位置
具体使用方法如下:
同理头删和尾删也可以直接调用erase
5.析构与拷贝构造
5.1 析构函数
我们现将析构函数写出来
这里需要现将所有节点全部删除后再将头节点删掉
我们可以现将清空函数分离开来,方便后续使用
5.2 拷贝构造
而拷贝构造就更加简单了,直接调用循环遍历进行尾插即可:
但是push_back
就要有list
的头节点,所以要进行初始化
我们可以将初始化封装成函数进行调用:
5.3 赋值重载
这里我们呢直接用新写法进行交换就行
注意:赋值重载函数不要传引用,否则就成为了真交换了
测试一下: