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