- 了解list容器
- 熟悉常见接口
- 迭代器
- list模拟实现
list容器
1.list容器
list容器底层是带头双向循环链表。任意位置插入删除效率都很高。
2.list与vector的优缺点
vector | list | |
---|---|---|
优点 | 1.cpu缓存命中率高 2.支持随机访问 | 1.任意位置插入删除效率高 2.扩容消耗小 |
缺点 | 1.扩容消耗大 2.头插,头删,中间插入删除效率低 | 1.不支持随机访问 |
接口
1.构造函数
//默认构造函数
list();
//用n个val构造
list(size_t n, T& val = T());
//迭代器区间构造
template<class InputIterator>
list(InputIterator first, InputIterator last);
2.重载函数
list<T> operator=(const list<T>& lt);
3.数据修改
//返回首元素
T& front();
//返回尾元素
T& back();
//在position位置前插入val
iterator insert(iterator position, const T& val);
//删除position位置
iterator erase(iterator position);
//尾插
void push_back(const T& val);
//尾删
void pop_back();
//头插
void push_front(const T& val);
//头删
void pop_front();
4.容量
//返回元素个数
size_t size();
//判空
bool empty();
迭代器
1.迭代器的本质
从外部接口来看,用户都是使用iterator来创建迭代器,但是它们的底层却并不简单,像string,vector之类底层是连续的空间,那么迭代器可以用原生指针,因为指针加1指向下一个有效元素,解引用指针也可得到正确的值,但是像list这样由一个一个节点构成,并不能将节点的地址作为迭代器,因为它们节点之间并不是连续的空间,将当前节点的地址+1也并不能指向下一个节点,将节点的地址解引用得到一个节点,但是我们需要的是节点内部的数据,所以list的原生指针并不能作为迭代器使用。如果我们想要达到我们的目的,即:迭代器+1指向下一个元素,解引用迭代器得到数据,那么就要结合前面的知识(函数重载),我们只要将原生指针封装为一个类,然后对该类的进行函数重载,此时,在外部创建迭代器,就是创建了一个该类的对象,对迭代器++或者解引用,就可以调用我们自己编写的重载函数,执行我们想要的操作。
总之:迭代器要么是原生指针,要么是类对原生指针的封装,如果原生指针不能达到目的,那么我们就需要封装原生指针。
2.list中迭代器失效问题
- list中insert不会造成迭代器失效,因为迭代器指向的元素不会改变。
- list中erase会造成迭代器失效,因为已经释放了迭代器指向的元素。常见的解决办法是erase函数返回下一个元素。
3.封装后迭代器如何控制const迭代器问题
我们直接可以想到的办法就是重新封装一个const_iterator类,但是因为模板可以传参,我们可以将引用作为参数传入,这样就可以实现。详见下列代码中的实现。
list模拟实现
namespace zs
{
//当成员访问权限不应受限制时,我们可以把节点封装为struct类型
template<class T>
struct list_node
{
list_node<T>* _prev;
list_node<T>* _next;
T _val;
list_node(const T& val = T())
:_prev(nullptr)
,_next(nullptr)
,_val(val)
{}
};
//为什么要重载->运算符
//当T为类对象时,我们可以使用->来轻易访问类内的数据成员,此时就要用函数重载重载->
//为什么要在模板中加入第三个参数,和Ref一样,是为了控制const属性
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(node* n)
:_node(n)
{}
Ptr operator->()
{
return &_node->_val;
}
//->调用方法:
//假设T为class AA{int a; int b;}
// auto it = lt.begin();
// cout << it->a << it->b << endl; 这里应该是it->->a, 但是为了可读性,省略了一个箭头
Ref operator*()
{
return _node->_val;
}
bool operator==(const self& it)
{
return it._node == _node;
}
bool operator!=(const self& it)
{
return it._node != _node;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
//默认拷贝构造可以满足
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
template<class T>
class list
{
typedef list_node<T> node;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
empty_init()
{
_head = new node;
_head->_prev = _head;
_head->_next = _head;
}
list()
{
empty_init();
}
//迭代器区间构造
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
empty_init();
while (first != last)
{
push_back(*first++);
}
}
void swap(const list<T>& lt)
{
std::swap(_head, lt._head);
}
//拷贝构造
//现代写法
list(const list<T>& lt)
{
empty_init();
list<T> tmp(lt.begin(), lt.end());
swap(tmp);
}
//现代写法
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
void push_back(const T& val)
{
insert(end(), val);
}
void push_front(const T& val)
{
insert(begin(), val);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
void insert(iterator pos, const T& val)
{
node* cur = pos;
node* prev = pos._node->_prev;
node* newNode = new node(val);
newNode->_prev = prev;
prev->_next = newNode;
newNode->_next = cur;
cur->_prev = newNode;
}
iterator erase(iterator pos)
{
assert(pos != end());
node* prev = pos._node->_prev;
node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
private:
node* _head;
};
};
}