前言
C++引入了面向对象的思想,相比于C语言,一个类能更好地对一些数据结构进行管理和操作。
在C语言中,我们动态开辟一个个的节点,并且用指针将他们连接起来,形成链式结构,链式结构在物理上不连续,在逻辑上连续
在C++中,基于面向对象的思想,用来管理这链式结构的类便应运而生,从本质上讲,list是带头双向循环链表
目录
1.list的简介
我们学习STL时,文档是我们的利器,学会查文档会让学习事半功倍,以下是两个C++文档网站:- 官网:www.cppreference.com
- 常用网站(更新至C++11):www.cplusplus.com
list的文档介绍:
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
其前一个元素和后一个元素。 - list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
(优点)
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息
(缺点)
STL作为泛型编程的典范,我们的list类自然就是一个类模板
template <class T>
class list {
//...
}
模板参数T很显然,就是我们想要在节点里插入的元素的类型,可以是int, char等内置类型数据,也可以是string, vector等自定义类型数据
list <int> lt1;
list <char> lt2;
list <vector<int>> lt3;
list <string> lt4;
再来看一下list类的成员变量
Node* _head;------>节点指针
节点是一个结构体,list用一个结构体指针来维护整张链表,相信大家对于节点并不陌生
template <class T>
struct __list_node {
__list_node(const T& x = T())
: _next(nullptr)
, _prev(nullptr)
, _data(x) {
}
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
};
在这里,节点也是用类模板定义出来的,因为要和list的模板参数T
我们typedef了一下
typedef __list_node<T> Node;
2.vector的常见接口及模拟实现
2.1 list类对象获取元素和迭代器的接口
和string、vector的迭代器是原生指针不同,由于list的每个节点的物理地址不连续,所以list的迭代器不能用原生指针来替代
于是我们创建了一个类iterator,将解引用、++等操作进行了类的运算符重载,从表观的调用上达到了和原生指针相同的效果
由于迭代器分为普通迭代器和const迭代器,参照STL源码,我们将迭代器设计成带有3个模板参数的一个类
template <class T, class Ref, class Ptr>
struct __list_iterator {
typedef __list_node<T> Node;
typedef __list_iterator<T, Ref, Ptr> self;
Node* _node;
__list_iterator<T, Ref, Ptr>(Node* node)
: _node(node) {
}
Ref operator*() {
return _node->_data;
}
self operator++() {
_node = _node->_next;
return __list_iterator(_node);
}
self operator++(int) {
__list_iterator<T, Ref, Ptr> tmp(_node);
_node = _node->_next;
return tmp;
}
self operator--(