katang
katang

Reputation: 2774

What is the safe way to iterate through a vector, and delete its certain elements

Is it possible to iterate in one single step through elements of a vector, while deleting some of the elements, to prevent repeated processing? (I know the fragment is bogus, but illustrates what I want)

std::vector<MyRecord*>* BLV = GetVector();
for (std::vector<MyRecord*>::iterator it = BLV->begin(); it != BLV->end(); ++it)
{
  MyRecord* BL = *it;
  if(Selected)
  { 
    delete BL;
    BLV->erase(it);
  }
}

Upvotes: 2

Views: 1165

Answers (2)

MikeMB
MikeMB

Reputation: 21156

songyuanyao already gave you a nice answer: In short, you have to reset your iterator to the value returned by erase.

However, if applicable, I'd suggest using a vector of unique_ptr's and the erase-remove idiom (which is also more efficient, as it only moves the elements once).

std::vector<std::unique_ptr<MyRecord>>* BLV = GetVector();
BLV->erase(
    std::remove_if(BLV->begin(), BLV->end(), 
        [&](const std::unique_ptr<MyRecord>& rec) { return Selected; }
    ),
    BLV->end()
);

Upvotes: 1

songyuanyao
songyuanyao

Reputation: 172924

Note std::vector::erase will invalidate the iterator to the element to be erased, and then ++it; will lead to UB.

Invalidates iterators and references at or after the point of the erase, including the end() iterator.

You can use the return value of erase().

Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.

then change the loop to

for (std::vector<MyRecord*>::iterator it = BLV->begin(); it != BLV->end(); )
{
  MyRecord* BL = *it;
  if(Selected)
  { 
    delete BL;
    it = BLV->erase(it);
  } else
  {
    ++it;
  }
}

Upvotes: 8

Related Questions