I've got this piece of code:
for (std::vector<Marker>::iterator it = markers.begin(); it != markers.end(); ++it) {
if (it->getDots().size() < 3) {
markers.erase(it);
}
}
In one of test inputs (the app does image analysis) I get a segfault. I tried to debug the code (to no avail) and noticed one thing. When asking gdb to p markers.size() i receive $9 = 3. So I would expect the loop to iterate three times, but surprisingly it does it (at least) 5 times. In fifth iteration there's a segfault. What I also noticed is that it's not the dereference of *it (here it->) that causes the error. It's specifically it->getDots(), which is a simple getter.
I write in C++ very rarely, so it might be some simple mistake, but neither my debugging, nor googling brought any solution. Could you help?
I'd like to emphasize, that on various different inputs (slightly different images) this function works properly, so it's even harder for me to trace the bug down.
vector::erase invalidates all iterators pointing to the element being erased, and all elements that follow. So it becomes invalid, and ++it expression on the next loop iteration exhibits undefined behavior.
The best way to code this logic is with erase-remove idiom.
The problem is this line:
markers.erase(it);
The iterator is invalidated. But that's okay, erase returns a valid iterator:
auto it = markers.begin();
while(it != markers.end())
{
if(it->getDots().size() < 3) {
it = markers.erase(it);
}
else ++it;
}
You need to update it when you erase:
it = markers.erase(it);
since erase will "change" the vector, and your current it is no longer valid (and only do it++ if when you didn't erase it).
However, the more common way to do this is to use this type of construction:
markers.erase(std::remove(markers.begin(), markers.end(), number_in), markers.end());
Related
I have a simple database consisting of objects with strings containing unix time as keys and strings containing instructions as values
I want to iterate though the database and erase any object who's key is smaller that current time ( so erase objects with dates before current date)
for (auto it = m_jsonData.begin(); it != m_jsonData.end(); it++) {
if (std::stoi(it.key()) <= (std::time(NULL))) {
std::cout << "command is behind schedule, removing\n";
m_jsonData.erase(it);
} else {
/*
*/
}
}
this code works fine as long as m_jsonData.erase(it); isn't invoked. when it does, in the next iteration std::stoi(it.key()) causes a segfault, after a bit of playing with it I came to a conclusion that is somehow loses track of what it's actually iterating. Is my conclusion true? If not then what is? And how do I fix it?
It's extremely normal for mutating container operations to invalidate iterators. It's one of the first things you should check for.
Documentation for nlohnmann::json::erase():
Notes
Invalidates iterators and references at or after the point of the erase, including the end() iterator.
References and iterators to the erased elements are invalidated. Other references and iterators are not affected.
That means after this line:
m_jsonData.erase(it);
the iterator it can't be used for anything including incrementing it to the next element. It is invalid.
Fortunately, the documentation also points out that the successor to the removed element is returned, so you can just write
for (auto it = m_jsonData.begin(); it != m_jsonData.end(); ) {
if (std::stoi(it.key()) <= (std::time(NULL))) {
it = m_jsonData.erase(it);
} else {
++it;
}
}
Note that when I say this is extremely normal, it's because the standard containers often have similar behaviour. See the documentation for examples, but this is something everyone should be aware of:
std::vector::erase Iterator invalidation
std::unordered_map::erase Iterator invalidation
etc.
This is exactly the reason std::erase was added in C++20, and previously std::remove_if was provided to suppport the erase(remove_if(...), end) idiom, instead of writing fragile mutating loops.
I currently have a problem with vector.erase().
vector<gameObject> gameObjects;
for (auto it = gameObjects.end() - 1; it != gameObjects.begin();)
{
if ((it)->getDestroyed()) {
it = gameObjects.erase(it);
}
else {
--it;
}
}
So gameObject is the base class for everything inside the game and it has a bool flag that basically tells us if the object was destroyed. If the flag is set it should be removed from the vector.
class gameObject
{
protected:
bool toBeDestroyed;
public:
bool getDestroyed();
void markToDestroy();
};
Now the first destroyed object gets removed from the vector successfully and then I get get an error that iterator is not dereferencable, pointing to the vector library at line 73(?).
I then check with the msvc debugger. In the data preview it shows that iterator points to the last/newest element of gameObjects. It is then removed (erase(it)) and AFTERWARDS the data preview doesn't change and calling it->getDestroyed() results in the error message.
Debug assertion failed! vector iterator not dereferencible.
PS: I checked cplusplus.com and vector.erase should return a new, valid iterator so I'm not sure where I'm messing it up.
€: After I was told about the erase-remove idiom I went ahead and ended up with the following, which doesn't compile. Due to my function being a member of gameObject I'm not sure how to successfully call remove_if. Thanks
gameObjects.erase(remove_if(gameObjects.begin(), gameObjects.end(), gameObject::getDestroyed), gameObjects.end());
€2: A lot of you pointed out the first object isn't being checked. I propably should've pointed that out but the first element is ALWAYS the player and shouldn't be removed. Thanks for your comments nevertheless. I'll try with a simple forward loop without getting too fancy ^^.
€3: I tried Jonathan Mees suggested code but I get the exact same error message. I'll try and find out where exactly it happens but I can't just put a breakpoint into the erasing part anymore. Will mess around a bit.
€4: Problem was solved by removing the else {} condition and always decrementing the iterator. Thanks again for all your replies.
Let's say you have 2 objects in your vector and the last one is is marked as destroyed. When you call erase, it will return a new, valid iterator pointing at the element after the erased element. There is no element after the erased element, so the returned iterator is gameObjects.end(). You then continue to the top of the loop and dereference this iterator, which is not valid. You need to decrement your iterator after the erase if you want it pointing at a valid element.
One other note: If you ever wanted your first element removed, it will not be. Your loop exits when the iterator == gameObjects.begin(), so the first element is never checked.
Is there some reason you wanted to do this in reverse? If there is no specific reason, I would recommend you use the method recommended by #Borgleader.
Your loop is a little messed up - you're iterating backwards, ignoring the first element, and testing some elements multiple times. Might I suggest rbegin() as an alternative?
vector::erase returns the:
Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.
Meaning that vector::erase will never return vector::begin (unless you removed the only element in the container.) So it will always be dereferenced again after vector::erase is called. It will be dereferenced even if vector::end was returned by the call to vector::erase which is of course illegal.
Instead of this loop, consider using remove_if which is designed for this purpose:
gameObjects.erase(remove_if(begin(gameObjects),
end(gameObjects),
[](const auto& i){ return i.getDestroyed(); }), end(gameObjects));
EDIT:
I noticed you try to use this in your edit. You cannot use a bare function pointer as the predicate. If you want to avoid a lambda, you should consider the use of mem_fn:
gameObjects.erase(remove_if(begin(gameObjects),
end(gameObjects),
mem_fn(&gameObject::getDestroyed)), end(gameObjects));
Live Example
If there's difficulty in reading that line feel free to use as many variable as you like:
auto p = mem_fn(&gameObject::getDestroyed);
auto result = remove_if(begin(gameObjects), end(gameObjects), p);
gameObjects.erase(result, end(gameObjects));
I have been struggling to put a vector object into a project im doing
I have read what little i could find about doing this and decided to give it a go.
std::vector<BrickFalling> fell;
BrickFalling *f1;
I created the vector. This next piece works fine until i get to the erase
section.
if(brickFall == true){
f1 = new BrickFalling;
f1->getBrickXY(brickfallx,brickfally);
fell.push_back(*f1);
brickFall = false;
}
// Now setup an iterator loop through the vector
vector<BrickFalling>::iterator it;
for( it = fell.begin(); it != fell.end(); ++it ) {
// For each BrickFalling, print out their info
it->printBrickFallingInfo(brick,window,deadBrick);
//This is the part im doing wrong /////
if(deadBrick == true)// if dead brick erase
{
BrickFalling[it].erase;//not sure what im supposed to be doing here
deadBrick = false;
}
}
You can totally avoid the issue by using std::remove_if along with vector::erase.
auto it =
std::remove_if(fell.begin(), fell.end(), [&](BrickFalling& b)
{ bool deadBrick = false;
b.printBrickFallingInfo(brick,window,deadBrick);
return deadBrick; });
fell.erase(it, fell.end());
This avoids the hand-writing of the loop.
In general, you should strive to write erasure loops for sequence containers in this fashion. The reason is that it is very easy to get into the "invalid iterator" scenario when writing the loop yourself, i.e. not remembering to reseat your looping iterator each time an erase is done.
The only issue with your code which I do not know about is the printBrickFallingInfo function. If it throws an exception, you may introduce a bug during the erasure process. In that case, you may want to protect the call with a try/catch block to ensure you don't leave the function block too early.
Edit:
As the comment stated, your print... function could be doing too much work just to determine if a brick is falling. If you really are attempting to print stuff and do even more things that may cause some sort of side-effect, another approach similar in nature would be to use std::stable_partition.
With std::stable_partition you can "put on hold" the erasure and just move the elements to be erased at one position in the container (either at the beginning or at the end) all without invalidating those items. That's the main difference -- with std::stable_partition, all you would be doing is move the items to be processed, but the items after movement are still valid. Not so with std::remove and std::remove_if -- moved items are just invalid and any attempt to use those items as if they are still valid is undefined behavior.
auto it =
std::stable_partition(fell.begin(), fell.end(), [&](BrickFalling& b)
{ bool deadBrick = false;
b.printBrickFallingInfo(brick,window,deadBrick);
return deadBrick; });
// if you need to do something with the moved items besides
// erasing them, you can do so. The moved items start from
// fell.begin() up to the iterator it.
//...
//...
// Now we erase the items since we're done with them
fell.erase(fell.begin(), it);
The difference here is that the items we will eventually erase will lie to the left of the partitioning iterator it, so our erase() call will remove the items starting from the beginning. In addition to that, the items are still perfectly valid entries, so you can work with them in any way you wish before you finally erase them.
The other answer detailing the use of remove_if should be used whenever possible. If, however, your situations does not allow you to write your code using remove_if, which can happen in more complicated situations, you can use the following:
You can use vector::erase with an iterator to remove the element at that spot. The iterator used is then invalidated. erase returns a new iterator that points to the next element, so you can use that iterator to continue.
What you end up with is a loop like:
for( it = fell.begin(); it != fell.end(); /* iterator updated in loop */ )
{
if (shouldDelete)
it = fell.erase(it);
else
++it;
}
std::vector<struct::event>::iterator it;
std::vector<struct::event>::iterator last=myvector.end();
for (it=myvector.begin(); it<=last; it++){
if(mysignal.declination<(*last).declination){
if (mysignal.declination>=(*it).declination && mysignal.declination<(*(it+1)).declination){
myvector.insert(it+1, mysignal);
break;
}
}
if (mysignal.declination>=(*last).declination){
myvector.push_back(mysignal);
break;
}
}
I have a vector called myvector with events that are sorted with the declination. now I want to add mysignal to this vector on the right place. but i always get a seg fault after a few events which refers to: if(mysignal.declination<(*last).declination). I just can't see what is wrong.
Your loop is wrong, read the docs:
Returns an iterator to the element following the last element of the container.
This element acts as a placeholder; attempting to access it results in undefined behavior.
You can't dereference end(), it provides a way of knowing that you have overrun the container, so your loop condition should be it != myvector.end(), and last is wrong as well.
As others have said, C++ iterators define a half-open interval
('[begin()...end())'), which is what you should probably be
using in most other cases as well. And although it works with
iterators from a vector, in general, iterators do not support
<= (nor <); the standard idiom for a loop is:
for ( auto current = container.begin();
current != container.end();
++ current ) ...
(In the most likely case that you cannot count on C++11, you'll
have to write out the full iterator type, rather than use
auto. Although auto is one of the few things from C++11
that seems to work with VC++11 and with recent versions of
g++, so if those are the only targets you're concerned with, and
you can be sure of always having very recent versions, you can
use it.)
Also, if you want to access the last element of the vector in
the loop, myvector.back() will return a reference to it.
(myvector.back() is undefined behavior if the vector is empty,
but if the vector is empty, you won't enter the loop.)
end() does not refer to the last element in the container, you need to change your condition as follows.
for (it=myvector.begin(); it != last; it++){
You have other broken logic as well that is dereferencing last that you need to fix.
Following the help in this question, I am using a reference to my Class 'Mover' to manipulate the object (as part of a set) in a vector. I am having issues however, and I cannot seem to identify what's causing it for sure. It appears that once I've reached 30-35 objects in my vector (added at pseudo-random intervals) the program halts. No crash, just halt, and I have to manually end the task (CTRL-C doesn't work).
My problem appears to lie in these bits of code. My original:
int main() {
std::vector< Mover > allMovers;
std::vector< Mover >::iterator iter = allMovers.begin();
//This code runs to the end, but the 'do stuff' lines don't actually do anything.
Mover tempMover;
//Other code
while(iter < allMovers.end()) {
tempMover = *iter;
//Do stuff with tempMover
//Add another tempMover at a random interval
allMovers.push_back(CreateNewMover());
iter++;
}
//Other code
}
My update after the previous question linked to above:
int main() {
std::vector< Mover > allMovers;
std::vector< Mover >::iterator iter = allMovers.begin();
//This code crashes once about 30 or so items exist in the vector, but the 'do stuff' lines do work.
//Other code
while(iter < allMovers.end()) {
Mover& tempMover = *iter;
//Do stuff with tempMover
//Add another tempMover at a random interval
allMovers.push_back(CreateNewMover()); //Crashes here.
iter++;
}
//Other code
}
Any ideas of how to track this down? I have std::couts all over the place to flag where the code is for me. The crash (while happens at a varied number of objects) always crashes on the push_back(), despite having worked successfully multiple times in the same run before the crash.
EDIT
While I accept and (think) I understand the answer re: iterators, what I don't understand is why the code DOES work completely when I am not using a reference to the object? (First code block).
Another EDIT
In case anyone was looking for this specifically, part of my question was not addressed: "How to debug?" As a C++ newbie, I was unaware of the gdb debugger (using MinGW). Now that I've learned about it, it has been very helpful in finding the source of these issues.
When a vector reallocates its memory, all iterators are invalidated (along with any reference or pointer to any element). So sometimes your push_back will invalidate iter, and trying to use it afterwards gives undefined behaviour.
The simplest fix is to use an index rather than an iterator. Alternatively, if you can calculate an upper bound for the maximum size of the vector, you could call reserve before the loop to ensure it never reallocates. Or you could use std::list, whose iterators are preserved when new elements are inserted.
UPDATE: Regarding your edit, both give undefined behaviour. It might be that, in the first case, you don't crash because you don't access a dangling reference (while accessing tempMover in the second might very well crash), and then the memory happens to be reallocated at a lower address than before, so the while condition (which uses < rather than the more conventional !=) exits the loop immediately. Or something completely different could be happening - that's the nature of undefined behaviour.
You are (probably) doing it wrong.
The thing is, mixing iteration over a container and manipulation of the container structure (here adding objects) is extremely error-prone.
Whenever you add an element in allMovers, there is a risk that iter is invalidated. Any usage of iter after it has been invalidated is Undefined Behavior.
It is possible to do it correctly:
iter = allMovers.insert(allMovers.end(), CreateNewMover());
however it's just a bad idea in general.
My advice would be to ban this kind of code from your code base altogether. Every single occurrence is a bug in the making. Find another algorithm.
From documentation for push_back():
If new size() is not larger than capacity(), no iterators or references are invalidated. Otherwise all iterators and references are invalidated.
When you reach 30 or some objects new size() > capacity(), resulting in invalidation of the iterator iter, which is derefenced causing undefined behaviour.
You might probably need to change the line containing the while statement:
while(iter != allMovers.end()) {
the < operator seems to work fine with a vector usually, but I had better results using != which works with other containers and also seems to be used in more example code out there.
Update
You may replace the while loop with an equivalent for loop like this:
for(std::vector<Mover>::iterator iter = allMovers.begin(); iter != allMovers.end(); ++iter)
{
This has the advantage that the increment of the iterator iter "has its place" and is less likely to be forgotten.
Update 2
If I understand your example above, you'd like to fill the container with some content. I suggest (as others did) to get rid of the iterator altogether.
int main()
{
std::vector< Mover > allMovers;
//Other code
while(1) // this loop will add new movers as long as it succeeds to create one
{
Mover new_mover = CreateNewMover();
if ( IS EMPTY (new_mover) ) // pseudocode. Check if the previous
break; // CreateNewMover() succeeded.
allMovers.push_back(new_mover);
}
//Other code
}