一、list的介绍及使用
(一)什么是list
1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2.list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)。
(二)list的使用
1.list的构造
// List的构造
list()
{
CreateHead();
}
list(int n, const T& value = T())
{
CreateHead();
for (int i = 0; i < n; ++i)
{
push_back(value);
}
}
template <class Iterator>
list(Iterator first, Iterator last)
{
CreateHead();
while (first != last)
{
push_back(*first);
++first;
}
}
list(const list<T>& l)
{
CreateHead();
list<T> temp(l.begin(), l.end());
swap(temp);
}
list<T>& operator=(const list<T> l)
{
swap(l);
return *this;
}
void CreateHead()
{
_phead = new Node;
_phead->_prev = _phead;
_phead->_next = _phead;
}
2.list容量及判空
// List Capacity
size_t size()const
{
size_t sz = 0;
Node* cur = _phead->_next;
while (cur != _phead)
{
sz++;
cur = cur->_next;
}
return sz;
}
bool empty()const
{
return size() == 0;
}
3.list返回第一个和最后一个节点值。
T& front()
{
assert(_phead != empty());
return _phead->_next->_val;
}
T& back()
{
assert(_phead != empty());
return _phead->_prev->_val;
}
4.list增删交换和清空
void push_back(const T& val)
{
insert(end(), val);
}
void pop_back()
{
erase(--end());
}
void push_front(const T& val)
{
insert(begin(), val);
}
void pop_front()
{
erase(begin());
}
// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
pNode newnode = new Node(val);
pNode posnode = pos._pNode;
newnode->_prev= posnode->prev;
posnode->_prev->_next = newnode
newnode->_next = posnode;
posnode->prev = newnode;
return iterator(newnode);
}
// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
PNode dnode = pos._pNode;
PNode nnode = dnode->_next;
dnode->_prev->_next = dnode->_next;
dnode->_next->_prev = dnode->_prev;
delete dnode;
return iterator(nnode);
}
void clear()
{
iterator it = begin();
while(it!=end())
{
it=erase(it);
}
}
void swap(list<T>& l)
{
PNode temp = l._phead;
l = _phead;
_phead = temp;
}
5.迭代器的使用
iterator begin()
{
return _phead->_next;
}
iterator end()
{
return _phead;
}
二、list下迭代器类的定义及使用
(一)iterator类的定义
因为list中内存不是连续的,我们使用迭代器时不能像在vector下使用对迭代器++从而来进行访问,这时我们就需要对迭代器进行运算符重载,从而使它达到访问list下的不连续内存,这里我们要将迭代器定义为struct而不是class,因为class具有封装性,实际应用中,我们需要随时访问该类,这个类不需要析构函数,因为本身就是一个结构体指针,没有必要。
struct ListIterator
{
typedef ListNode<T>* PNode;
typedef ListIterator<T, Ref, Ptr> Self;
PNode _pNode;
ListIterator(PNode pNode = nullptr)
:_pNode(pNode)
{}
T& operator*()
{
return PNode->_val;
}
T* operator->()
{
return &PNode->_val;
}
Self& operator++()
{
_pNode = _pNode->_next;
return *this;
}
Self operator++(int)
{
self temp(*this);
_pNode = _pNode->_next;
return temp;
}
Self& operator--()
{
_pNode = _pNode->_prev;
return *this;
}
Self& operator--(int)
{
self temp(*this);
_pNode = _pNode->_prev;
return temp;
}
bool operator!=(const Self& l)
{
return _pNode != l._pNode;
}
bool operator==(const Self& l)
{
return !(*this != l);
}
};
(二)list的迭代器失效
在list中,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。