Iterator invalidation (1)-be careful when using the erase () of the STL container ()

Source: Internet
Author: User

For the following code:

      my_container.erase(iter);

My_container is a container of STL, and ITER is an iterator pointing to an element in the container. If it is not in the for, while loop,

There is no problem in deleting elements in this way. If you are performing m_container iteration in for and while, deleting all elements that meet the conditions may cause problems.

If m_iner iner is iterated in for and while, deleting all elements that meet the conditions may cause problems.

The problem is:

Deleting elements during iteration containers may result in invalidation of iterators and lead to undefined behaviors.

(Undefined behavior );

For example, the value obtained by unreferencing an iterator is not the value that the iterator points to before erase () is executed, or it may not point to any

The program crash is triggered by the value assignment of the element iterator.

The similar problem code is as follows:

  std::vector<int>  my_container;  for (int i = 0; i < 100; ++i) {       my_container.push_back(i);    }  std::vector<int>::iterator it = my_container.begin();  for (it != my_container.end(); it++) {    if (*it % 2 == 1) {           my_container.erase(it);       }  }

After my_container.erase (it), it and its later iterators have expired and should not be used. When it ++ is executed, its behavior is undefined.

Other containers may also encounter the problem that the iterator fails:

All iterators that delete elements in a vector and those that point to the following elements are invalid.

Deleting a deque element at the beginning or end will only invalidate the iterator pointing to the deleted element. Any other insert or delete operation will direct the element to

All iterators are invalid.

Only the iterator pointing to the deleted element in the list is invalid.

For (mutex) map, (Multi) set only points to the iterator of the deleted element.

So golden rule is:Try not to use the iterator before the insert/delete operation of the container.

Why are the failure situations of different container iterators different? This is related to the implementation of the data structure of each container.

How can I delete elements in an iteration container? The common practices of each container are as follows:

            std::vector<int>::iterator it = my_container.begin();            for (it != my_container.end();/**blank*/ ) {              if (*it % 2 == 1) {                      my_container.erase(it++);                 }                 else{                      it++;                 }            }

My_container.erase (it ++) cleverly points to the element after the deleted element before executing erase (), and then to erase () it iterators that are not auto-incrementing are passed,

To locate the element to be deleted. If the element value is an odd number, delete this element. It points to the next element. If the element value is an even number, check the value of the next element. During the entire iteration process

The iterator will not expire.

I ++ operations are performed on two different branches in the code above. The following code example shows how to prevent the I ++ operations on any of the branches from being forgotten.

            MyContainer::iterator it = myContainer.begin();            While(it != myContainer.end()){               MyContainer::iterator curIt = it;               if (*curIt == matchingValue)    {                       myContainer.erase(curIt);               }            }

Another feasible method for vector, deque, list is:

           std::vector<int>::iterator it = my_container.begin();           for (it != my_container.end();/**blank*/ ) {             if (*it % 2 == 1) {                    it = my_container.erase(it);                }                else{                     it++;                }           }

The above code is feasible because vector: erase () returns a new iterator pointing to the elements after the deleted element. You can continue to use the new iterator.

For some unknown reason (Multi) map: erase (), (Multi) set: erase () does not return such an iterator. (The Return iterator is also supported since C ++ 11 ).

However, for vector, such as deleting all odd numbers from 0 to 99, you can use remove () and remove_if () of STL to optimize the performance. The Code is as follows:

          bool isOdd(int value)          {               return (value % 2) == 1;          }          my_container.erase( std::remove_if(m_container.begin(), m_container.end(), isOdd), m_container.end());

Let's take a look at the versions without remove_if:

          for (it != my_container.end();/**blank*/ ) {            if (*it % 2 == 1) {                   it = my_container.erase(it);               }               else{                    it++;               }           }

If you have read the erase () source code or know how it works, the performance problem is obvious: erase () deletes an element in order to delete all the elements following the deleted element.

Move the position of an element forward and delete the last element. The time complexity is O (n ^ 2 ).

The time complexity of remove () and remove_if () is O (n). The operations for deleting elements are as follows:

            template<class ForwardIt, class UnaryPredicate>            ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)            {                  ForwardIt result = first;                  for (; first != last; ++first) {                       if (!p(*first)) {                            *result++ = *first;                       }                  }                  return result;             }

Traverse all elements in the container from the beginning to the back, move the elements to be retained forward to occupy the position of the elements to be deleted, remove_if () returns the end in the new element range (begin, end,

Record as new_end_of_range, and then call erase () to delete all elements from new_end_of_range to my_container.end.

In fact, remove_if () does not delete any elements in the container. It does not change my_container.end (). After remove_if () is called, the number of elements in the container does not change !! Deleting an element

Handed over to erase ().

Scott Meyers also used remove_if () in his "negative STL" discussion on this issue. From this point of view, he indeed proposed some suggestions to make STL valid.

Learn more about the failure of the STL iterator:

Search STL iterator invalidation rules in Google to obtain a lot of information about STL iterator invalidation.

References:

1. STL remove_if ()
Http://en.cppreference.com/w/cpp/algorithm/remove

2. More C ++ idioms/Erase-Remove http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove

3. Valid STL, item 32-Scott Meyers

4. cpp invalid iterators [describes the failure of various iterators]


Http://www.angelikalanger.com/Conferences/Slides/CppInvalidIterators-DevConnections-2002.pdf

5. The following is a discussion about how to delete elements in containers during iteration on stackoverflow:

Http://stackoverflow.com/questions/1604588/iterate-vector-remove-certain-items-as-i-go

Http://stackoverflow.com/questions/3747691/stdvector-iterator-invalidation? RQ = 1

Http://stackoverflow.com/questions/2874441/deleting-elements-from-stl-set-while-iterating? RQ = 1

Http://stackoverflow.com/questions/1038708/erase-remove-contents-from-the-map-or-any-other-stl

-Container-while-iterating/1038761 #1038761

Http://stackoverflow.com/questions/799314/difference-between-erase-and-remove? RQ = 1

Repost this article please indicate the author and the source [Gary's influence] http://garyelephant.me, do not for any commercial purposes!

Author: Gary Gao focuses on the internet, distributed, high concurrency, automation, and software teams

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.