1)list介绍
结构,list的迭代器用类封装为了实现重载,<T,T&,T>*
template < class T, class Alloc = allocator<T> > class list;
①:list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代 ②: list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素 ③: list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效 ④: 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好 ⑤:与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
2)list使用
详细请参考:cplusplus -list
①list构造函数
②list的访问及遍历
③list容量操作
③list修改
注意容器尽量只用自己的swap,库中的实现如下
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
对于list容器的swap,只用交换头指针就行了, 库中的T c(a); 语句是进行的深拷贝,自然不推荐使用
④list操作
①:list的成员函数sort不建议使用,因为是归并排序,效率低无意义(链表的快排有缺陷 ,有序时候是O(N^2)) ②: 注意list不能使用库函数的sort,库函数是使用的快排
- list迭代器不是原生指针,++不是取到下一个数据,地址是随机的,要实现找到下一个位置的这个操作本身实现就需要O(N)的时间复杂度,所以无意义
- 库函数里的sort用了迭代器相减,变相只支持原生指针,list迭代器不能相减
3)list模拟实现(结构:三个类)
list底层是一个带头双向循环链表
Ⅰ.节点类
前面说过struct中的成员默认都是公有的(public)
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_prev(nullptr)
,_next(nullptr)
,_val(val)
{}
ListNode<T>* _prev;
ListNode<T>* _next;
T _val;
};
Ⅱ. List的迭代器类
- 当我们要使用const_iterator时,只能重新定义一个const版的List的迭代器类
- 而const_iterator和iterator只有解引用的重载不一样,我们使用这种方法
- 类模板参数改为三个分别对应
T,T&,T*
template<class T, class Ref, class Ptr>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T, Ref, Ptr> Self;
Node* _pNode
};
1.构造
默认构造和拷贝构造
注意 : 在List的迭代器类里不需要自定义析构函数,默认析构函数已经够用了
ListIterator(Node* pNode = nullptr)
:_pNode(pNode)
{}
ListIterator(const Self& l)
: _pNode(l._pNode)
{}
2.运算符重载
①解引用*和->重载
这里是引用
Ref operator*()
{
return _pNode->_val;
}
②前置/后置++/ - -
注意 : 后置,临时变量出作用域销毁,不能引用返回
Self& operator++()
{
_pNode = _pNode->_next;
return *this;
}
Self operator++(int)
{
Self tmp = _pNode;
_pNode = _pNode->_next;
return tmp;
}
Self& operator--()
{
_pNode = _pNode->_prev;
return *this;
}
Self operator--(int)
{
Self tmp = _pNode;
_pNode = _pNode->_prev;
return tmp;
}
③关系运算符!= ==重载
bool operator!=(const Self& it) const
{
return _pNode != it._pNode;
}
bool operator==(const Self& it) const
{
return (_pNode == it._pNode);
}
Ⅲ. list类
1.构造
2.赋值运算符重载
3. 容量
4.修改操作
insert
iterator insert(iterator pos, const T& val)
{
Node* cur = pos._pNode;
Node* prev = cur->_prev;
Node* newnode = new Node(val);
newnode->_next = cur;
cur->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
return iterator(newnode);
返回值有三种写法 :
- 原始
iterator ret(newnode); return ret; - 隐式类型转换
return newnode; - 匿名对象
return iterator(newnode);
erase
iterator erase(iterator pos)
{
assert(pos!=end());
Node* cur = pos._pNode;
Node* prev = cur->_prev;
prev->_next = cur->_next;
cur->_next->_prev = prev;
delete cur;
return iterator(prev->_next);
}
5.非成员函数重载
swap
4)迭代器总结
①:迭代器从使用功能分类 :
- 正向/正向const
- 反向/反向const
②:迭代器从底层结构分类 :
单向 单链表/哈希表(只支持++)双向 双向链表/二叉树/map(支持++/ - -)随机 dequeue/vector/string/map(支持++ /- -/ +/ -)
随机>双向>单向(包含关系) 迭代器在不暴露容器底层实现细节的情况下,提供统一的方式去修改容器中储存的数据,是算法和容器的胶合剂
|