When using iterators, we need to pay attention to the validation of them. In some cases, the iterator may turns invalid, which is not easy to notice.
For example, in the following case, we use an iterator to delete all the even numbers in the vector.
intmain() { vector<int> v;for (int i =0; i <20; ++i) {vec.push_back(rand() %100+1); }for (auto it =v.begin(); it !=v.end(); ++it) {if (*it %2==0) {v.erase(it); // ERROR } }return0;}
But the program exits abnormally. In fact, after the first element is erased from the vector, the elements after it need to be moved forward. Therefore, all iterators behind the current one is no longer valid any more.
The second example is to add n - 1 before every even number n:
intmain() { vector<int> v;for (int i =0; i <20; ++i) {vec.push_back(rand() %100+1); }for (auto it =v.begin(); it !=v.end(); ++it) {if (*it %2==0) {v.insert(it,*it-1); // ERROR } }return0;}
Similarly, an error occurs after the first element is added. When an element is added, all elements after it need to be moved backward, so their iterators are invalid. Moreover, the vector is likely to be double-expanded, in which all iterators of the container are invalid now.
If an iterator is invalid, we can no longer do iterator operations, for example increment and dereference. To fix this, we need to update the iterator at the erase point or insert point. The designers of STL containers have taken this into account, so function erase() and insert() return a new iterator pointing to the current element after called.
After an element is erased from the container, the elements after it will move forward, and the returned iterator points to the one after the erase point, so increment is not needed when an element is erased.
intmain() { vector<int> v;for (int i =0; i <20; ++i) {vec.push_back(rand() %100+1); }for (auto it =v.begin(); it !=v.end();) {if (*it %2==0) { it =v.erase(it); } else {++it; } }return0;}
After an element is erased from the container, the elements after it will move backward, and the returned iterator points to the one being added, so another increment is needed in this case.
intmain() { vector<int> v;for (int i =0; i <20; ++i) {vec.push_back(rand() %100+1); }for (auto it =v.begin(); it !=v.end();) {if (*it %2==0) {v.insert(it,*it-1);++it; } }return0;}
How does the iterator invalidation works at the bottom? In STL containers, a linked list is used to maintain the status of current iterators. Every time a container operation is performed, the validation of current iterator is verified using the linked list. We will not elaborate here, but we modify our MyVector class to implement this feature.
#include<stdlib.h>​template <typenameT>classAllocator {public:T*allocate(size_t size) { return (T *)malloc(sizeof(T) * size); }​voiddeallocate(void*p) { free(p); }​voidconstruct(T*p,constT&val) { new (p) T(val); }​voiddestroy(T*p) { p->~T(); }};​template <typenameT,typenameAlloc= Allocator<T>>classMyVector {public:MyVector(int size =10) { _first =_allocator.allocate(size); _last = _first; _end = _first + size; }​~MyVector() {for (T *p = _first; p != _last; p++) {_allocator.destroy(p); }_allocator.deallocate(_first); _first = _last = _end =nullptr; }​MyVector(constMyVector<T> &other) {int size =other._end -other._first; _first =_allocator.allocate(size);int len =other._last -other._first;for (int i =0; i < len; i++) {_allocator.construct(_first + i,other._first[i]); } _last = _first + len; _end = _first + size; }​MyVector<T> &operator=(constMyVector<T> &other) {if (*this== other) return*this;for (T *p = _first; p != _last; p++) {_allocator.destroy(p); } _first =_allocator.allocate(size);int len =other._last -other._first;for (int i =0; i < len; i++) {_allocator.construct(_first + i,other._first[i]); } _last = _first + len; _end = _first + size;return*this; }​voidpush_back(T&val) {if (full()) expand();_allocator.construct(_last, val); _last++; }​voidpop_back() {if (!empty()) {verify(_last -1, _last); _last--;_allocator.destroy(_last); } }​T&back() const { return*(_last -1); }​boolfull() const { return _last == _end; }​boolempty() const { return _last == _first; }​intsize() const { return _last - _first; }​T&operator[](int index) { return_first[index]; }​ // Iterators for MyVectorclassiterator {public:iterator(MyVector<T,Alloc> *pvec =nullptr,T*p =nullptr):_p(p),_pVec(pvec) { Iterator_Base *itb =newIterator_Base(this,_pVec->_head._next);_pVec->_head._next = itb; }​booloperator!=(constiterator&other) {if (_pVec ==nullptr|| _pVec !=other._pVec) {throw"iterator incompatable!"; }return _p !=other._p; }​voidoperator++() {if (_pVec ==nullptr) {throw"iterator invalid!"; } _p++; }​T&operator*() {if (_pVec ==nullptr) {throw"iterator invalid!"; }return*_p; }​constT&operator*() const {if (_pVec ==nullptr) {throw"iterator invalid!"; }return*_p; }​private: T *_p; MyVector<T, Alloc>*_pVec; };​iteratorbegin() { returniterator(_first); }​iteratorend() { returniterator(_last); }​ // Insert an element into the vectoriteratorinsert(iterator it,constT&val) {verify(it._ptr -1, _last); T *p = _last;while (p >it._ptr) {_allocator.construct(p,*(p -1));_allocator.destroy(p -1); p--; }_allocator.construct(p, val); _last++;returniterator(this, p); }​ // Erase an element from the vectoriteratorerase(iterator it) {verify(it._ptr -1, _last); T *p =it._ptr;while (p < _last -1) {_allocator.destroy(p);_allocator.construct(p,*(p +1)); p++; }_allocator.destroy(p); _last--;returniterator(this,it._ptr); }​private: T *_first; // Start point T *_last; // One step after the last valid element T *_end; // One step after the last element in the space Alloc _allocator;​ // A linked list storing pointes to iteratorsstructIterator_Base {Iterator_Base(iterator*c =nullptr,Iterator_Base*n =nullptr):_cur(c),_next(n) {} iterator *_cur; Iterator_Base *_next; }; Iterator_Base _head;​voidexpand() {int size = _last - _first; T *tmp =_allocator.allocate(2* size);for (int i =0; i < size; i++) {_allocator.construct(tmp + i,_first[i]); }for (T *p = _first; p != _last; p++) {_allocator.destroy(p); }_allocator.deallocate(_first); _first = tmp; _last = _first + size; _end = _first + size *2; }​ // Verify the validation of iteratorsvoidverify(T*first,T*last) { Iterator_Base *pre =&this->_head; Iterator_Base *it =this->_head._next;while (it !=nullptr) {if (it->_cur->_ptr > first &&it->_cur->_ptr <= last) {it->_cur->_pVec =nullptr;pre->_next =it->_next;delete it; it =pre->_next; } else { pre = it; it =it->_next; } } }};