4、list迭代器失效问题
list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响
- 示例:
void TestListIterator1() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list<int> l(array, array + sizeof(array) / sizeof(array[0])); auto it = l.begin(); while (it != l.end()) { // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值 l.erase(it); ++it; } PrintList(l); } // 改正 void TestListIterator2() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list<int> l(array, array + sizeof(array) / sizeof(array[0])); auto it = l.begin(); while (it != l.end()) { l.erase(it++); //it=l.erase(it); } PrintList(l); } void TestListIterator3() { int array[] = { 1, 2, 3 }; list<int> l(array, array + sizeof(array) / sizeof(array[0])); auto it = l.begin(); while (it != l.end()) { //insert后it迭代器的意义不会改变 l.insert(it,4); ++it; } PrintList(l); }
三、list剖析和模拟实现
1、list迭代器封装和节点类
- 迭代器有两种实现方式,具体应根据容器底层数据结构实现:
原生态指针,比如:vector,string
将原生态指针进行封装,因迭代器使用形式与指针完全相同(使用重载进行封装指针,达到迭代器的效果)
封装方法:
指针可以解引用,迭代器的类中必须重载operator*()
指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)至于operator–()/operator–(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,所以需要重载,如果是forward_list就不需要重载–
迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
实现代码:
// List的节点类 template<class T> struct ListNode { ListNode(const T& val = T()) :_val(val) ,_pPre(nullptr) ,_pNext(nullptr) {} //成员变量 ListNode<T>* _pPre; ListNode<T>* _pNext; T _val; }; //List的迭代器类(Ref(T&),Ptr(T*)) //(封装迭代器,使原生指针能执行迭代器基本操作) template<class T, class Ref, class Ptr> struct ListIterator { typedef ListNode<T>* PNode; typedef ListIterator<T, Ref, Ptr> Self; ListIterator(PNode pNode = nullptr) :_pNode(pNode) {} Ref operator*() { return _pNode->_val; } Ptr operator->() { return &_pNode->_val; } Self& operator++() { _pNode = _pNode->_pNext; return *this; } Self operator++(int) { Self tmp(_pNode); _pNode = _pNode->_pNext; return tmp; } Self& operator--() { _pNode = _pNode->_pPre; return *this; } Self operator--(int) { Self tmp(_pNode); _pNode = _pNode->_pPre; return tmp; } bool operator!=(const Self& l)const { return _pNode != l._pNode; } bool operator==(const Self& l)const { return _pNode == l._pNode; } PNode _pNode; };
注:这里的节点类和迭代器类,我们希望能直接被list类访问使用,使用struct默认访问限定类型为public
2、list常用接口实现
- 实现代码:
//list类 template<class T> class list { typedef ListNode<T> Node; typedef Node* PNode; public: typedef ListIterator<T, T&, T*> iterator; typedef ListIterator<T, const T&, const T&> const_iterator; // List的构造 list() :_pHead(new Node)//构建哨兵节点 { _pHead->_pNext = _pHead; _pHead->_pPre = _pHead; } list(int n, const T& value = T()) { _pHead = new Node; _pHead->_pNext = _pHead; _pHead->_pPre = _pHead; while (n--) { push_back(value); } } template <class Iterator> list(Iterator first, Iterator last) { _pHead = new Node; while(first!=last) { push_back(*first); ++first; } } list(const list<T>& l) { _pHead = new Node; _pHead->_pNext = _pHead; _pHead->_pPre = _pHead; const_iterator it = l.begin(); while (it != l.end()) { push_back(*it); ++it; } } list<T>& operator=(list<T> l)//现代式 { swap(_pHead, l._pHead); return *this; } ~list() { clear(); delete _pHead; _pHead = nullptr; } // List Iterator iterator begin() { return iterator(_pHead->_pNext);//迭代器封装指针 } iterator end() { return iterator(_pHead); } const_iterator begin()const { return const_iterator(_pHead->_pNext); } const_iterator end()const { return const_iterator(_pHead); } // List Capacity size_t size()const { size_t sz = 0; iterator it = begin(); while (it != end()) { ++it; ++sz; } return sz; } bool empty()const { return begin() == end(); } // List Access T& front() { assert(!empty()); return _pHead->_pNext->_val; } const T& front()const { assert(!empty()); return _pHead->_pNext->_val; } T& back() { assert(!empty()); return _pHead->_pPre->_val; } const T& back()const { assert(!empty()); return _pHead->_pPre->_val; } // List Modify 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) { assert(pos._pNode); PNode cur = pos._pNode; PNode pre = cur->_pPre; PNode newnode = new Node(val); newnode->_pNext = cur; cur->_pPre = newnode; pre->_pNext = newnode; newnode->_pPre = pre; return iterator(newnode); } // 删除pos位置的节点,返回该节点的下一个位置 iterator erase(iterator pos) { assert(pos._pNode); assert(pos!=end()); PNode pre = pos._pNode->_pPre; PNode next = pos._pNode->_pNext; pre->_pNext = next; next->_pPre = pre; delete pos._pNode; return iterator(next); } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } } private: PNode _pHead; };
3、list和vector对比
vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同
- 对比展示: