You may be aware of the concept of "iterator invalidation", and you are using size_t i
to avoid this. Unfortunately, what you're using is still essentially an iterator and erasing an element from a vector invalidates all iterators not just the ::iterator
typed ones :).
invalidate
is a carefully chosen term that has subtle nuances. It implies that the iterators didn't stop working, and that you may sometimes find they still work. But you shouldn't trust them to.
So invalidate
doesn't mean they are broken or wrong, it means that you should reconsider them. For example, if you have captured vec.begin()
and you have caused the vector to relocate it's data, the value of vec.begin()
may no-longer be valid. It typically isn't, but you've been warned :)
std::vector
is implemented over a simple contiguous array:
[ 0 ][ 1 ][ 2 ][ 3 ]
When you erase an element, the object it contains is destructed and then all of the elements to the right are moved left, physically resituated in memory.
[ 0 ][~~~~~][ 2 ][ 3 ] sz=4
[ 0 ][ 2 << 2 ][ 3 ] sz=4
[ 0 ][ 2 ][ 3 << 3 ] sz=4
[ 0 ][ 2 ][ 3 ][?????] sz=4
Then the vector reduces size
by the count of elements removed, in this case 1:
[ 0 ][ 2 ][ 3 ] sz=3
You can see that the overall process of erase()
, then, is expensive when the objects are not simple or when the vector is large, but I'll come back to that.
The problem with your implementation is that you increase i
, and the size shrinks, so you wind up deleting every second element.
i=0
[ 0 ][ 1 ][ 2 ][ 3 ] sz=4
erase(i);
i=0
[~~~~~][ 1 ][ 2 ][ 3 ] sz=3
[ 1 << 1 ][ 2 ][ 3 ] sz=3
[ 1 ][ 2 << 2 ][ 3 ] sz=3
[ 1 ][ 2 ][ 3 << 3 ] sz=3
[ 1 ][ 2 ][ 3 ][?????] sz=3
[ 1 ][ 2 ][ 3 ] sz=3
i=0
i++;
i=1
[ 1 ][ 2 ][ 3 ] sz=3
erase(i);
i=1
[ 1 ][~~~~~][ 3 ] sz=3
[ 1 ][ 3 << 3 ] sz=3
[ 1 ][ 3 ][?????] sz=3
[ 1 ][ 3 ] sz=2
i=1
i++;
i=2
[ 1 ][ 3 ] sz=2
break;
std::vector
provides clear
to empty an array, but if you are interested in understanding how to perform such an operation yourself:
Option 1: delete from back
while (!vec.empty())
vec.pop_back();
What this does is destruct the last element and then reduce size. It's very cheap.
[ 0 ][ 1 ][ 2 ][ 3 ] sz=4
pop_back();
[ 0 ][ 1 ][ 2 ][~~~~~] sz=4
[ 0 ][ 1 ][ 2 ] sz=3
Option 2: Erase a range of elements
std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
vec.erase(vec.begin() + 2, vec.begin() + 5);
vec.erase(vec.begin(), vec.end());
Option 3: Shrink the array:
vec.resize(0);
Option 4: Clear
This is the preferred and normal way to empty a vector
vec.clear();