Analysis of STL Iterator Failure Problem in C++
Containers in STL are divided into sequential containers and associative containers. Sequential containers include: vector, deque, and list; associative containers include set, map, multiset, and multimap.
1. Containers
of vector, deque, and list When traversing and deleting certain elements in vector, deque, and list, the following methods can be used
(1) Correct usage 1
Obtain the position of the next element through the return value of the erase method.
JAVA;">
std::vector
Vec;
std::vector
::iterator itVec; for(itVec = Vec.begin(); itVec != Vec.end(); ) { if (WillDeleteCondition(*itVec)) itVec = Vec.erase(itVec); else itVeC++; }
JAVA;">
std::list
List;
std::list
::iterator itList; for(itList = List.begin(); itList != List.end (); ) { if (WillDeleteCondition(*itList))
itList = List.erase(itList); else itList++; }
(2) The following methods can also be used for lists
Use "++" to get the position of the next element before calling the erase method
JAVA;">
std::list
List;
std::list
::iterator itList; for(itList = List.begin() ; itList != List.end(); ) { if (WillDeleteCondition(*itList))
List.erase(itList++); else itList++; }
(3) Wrong
usage Use "++" to get after calling the erase method The position of the next element, since the position of the element has been deleted after the erase method is called, if the next position is obtained based on the old position, an exception will occur.
JAVA;">
std::list
List;
std::list
::iterator itList; for(itList = List.begin(); itList != List.end();
itList++) { if (WillDeleteCondition(*itList) )
List.erase(itList);
JAVA;">
std::map
Map
std::map
::iterator itMap for(itMap = Map.begin(); itMap != Map.end(); ) { if (WillDeleteCondition(itMap->second) )
Map.erase(itMap++); else itMap++; }
3. Analysis of the Reason
In the C++ standard, the erase function of sequential containers will return iterator, but the erase function of associative containers will not return iterator.
(1) For sequential containers vector and deque, deleting the current iterator will invalidate the iterators of all subsequent elements. This is because vector and deque use continuously allocated memory, and deleting an element causes all subsequent elements to move forward by one position. The erase method can return the next valid iterator. [That is, correct usage 1]
(2) For associative containers map, set, multimap, and multiset, deleting the current iterator will only invalidate the current iterator, as long as the current iterator is incremented when erase. This is because containers such as map are implemented using red-black trees, and inserting or deleting a node will not affect other nodes. [namely correct usage A]
(3) For the sequential container list, the erase method can return the next valid iterator [namely correct usage 1]. Since list is a linked list, deleting the current iterator will only invalidate the current iterator, so you can also increment the current iterator when erasing [that is, correct usage A].
(4) The erase function returns the iterator of the next element of the deleted element. In STL, iterators cannot be viewed in terms of pointers, pointers are bound to memory, and iterators are bound to the elements in the container.
Fourth, the failure of the iterator
(1) Vector
internal data structure: array Access each element
randomly , and the time required is constant.
The time required to add/delete elements at the end is independent of the number of elements, and the time required to add/delete elements at the beginning or middle varies linearly with the number of elements.
Elements can be dynamically increased or decreased, and memory management is done automatically, but the programmer can use the reserve() member function to manage memory.
add element: the iterator of all elements is invalid (when memory is reallocated) (when more than capacity()-size() elements are inserted into the vector, the memory will be reallocated)
the iterator of any element after the current element is invalid (memory When there is no reallocation)
delete element: the iterator of any element after the deleted element will be invalid
(2) deque
internal data structure: array
random access to each element, the time required is constant
Add/delete elements at the beginning or end The time required is independent of the number of elements, and the time required to add/delete elements in the middle varies linearly with the number of elements.
Elements can be dynamically increased or decreased, memory management is done automatically, and member functions for memory management are not provided.
add element: iterator invalidation
delete element: iterator invalidation (deleting intermediate elements)
the iterator pointing to this element invalidation (deleting head/tail elements)
(3) list
Internal data structure: doubly circular linked list
can not access an element randomly, but can traverse in both directions.
The time required to add/delete elements at the beginning/middle/end is constant. Elements
can be dynamically increased or decreased, and memory management is automatically completed.
add element: will not invalidate the iterator
delete element: the iterator pointing to the currently deleted element is invalid, and other iterators will not be invalid
(4) set
internal data structure: red-black tree The
key and value are equal, the key is unique, the element defaults Sort in ascending order.
add element: will not invalidate the iterator
delete element: the iterator pointing to the currently deleted element is invalid, and other iterators will not be invalid
(5) map
internal data structure: red-black tree The
keys are unique, and the elements are arranged in ascending order by default.
add element: will not invalidate the iterator
delete element: the iterator pointing to the currently deleted element will be invalid, and other iterators will not be invalid
5. Example
JAVA;">
// iteratorInvalidateVector.cpp
#include
#include
typedef std: :vector
Vector; typedef std::vector
::iterator VectorIt; void printLog(Vector vectOne) { VectorIt it; for (it = vectOne.begin(); it != vectOne.end(); it++) { std::cout<< *it<<" "; } std::cout<
JAVA;">//iteratorInvalidateList.cpp
#include
#include
typedef std::list
List; typedef std::list
::iterator ListIt; void printLog(List listOne) { ListIt it; std::cout<
JAVA;">// iteratorInvalidateMap.cpp
#include
#include
#include
typedef std::map
Map; typedef std::map
::iterator MapIt; void printLog(Map m) { MapIt it; for (it = m.begin(); it != m.end(); it++) { std::cout<< it->second < ;<" "; } std::cout<
second % 2)) { m.erase(it++); } else { it++; } } } void deleteValueOne(Map &m, int n) { MapIt it; for (it = m.begin(); it != m.end(); it++) { if (0 == (it->second % 2)) m.erase(it); } } int main() { Map m; int i = 0; for(i = 0; i < 21; i++) { m[i] = i; } m[3] = 4; printLog(m); deleteValue(m, 5); printLog(m); return 0; }
Note: If the function deleteValueOne() is used in iteratorInvalidateMap.cpp, it will not cause a crash, but the correct result is not obtained. The result is as follows
0 1 2 4 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 4 5 7 9 11 13 15 17 19
Because (when it points to 2) after executing m.erase(it), it is automatically bound to the next element (the first 4), and at this time the it++ in the for loop is executed (it points to the second 4), Just skip the first 4 in the map.
Guess you like
Origin http://43.154.161.224:23101/article/api/json?id=326413672&siteId=291194637
Ranking