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
}
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 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;
}
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());
EDIT: TLDR? Here's a summary:
The requirement is for an essentially infinitely (or arbitrarily) long container. So list sounds like a good idea, because it will fit the objects in whatever memory space is available.
However vectors are much faster/efficient at access, but might not be able to fit in memory if we don't have a long sequential strip.
Vector of pointers was suggested to reduce memory usage, but the problem remains if there are a gigabyte of pointers and I have 4GB of ram, it might just not fit!
Solution: A list of vectors might be the way to go. Each item in the list could be a vector with 1000 pointers to items which we want to be able to access. A class could handle this functionality.
** Original Question:**
As a wise man once said: "With pointers, if it works once, that doesn't guarantee you are doing it correctly."
I have a class:
class A;
And class A is inside a std::list:
std::list<A> list_of_A;
To access items inside it I am using:
std::list<A>::iterator iter = list_of_A.begin();
std::advance(iter, <an_unsigned_int>);
return *iter;
This seems to be working, but is return *iter the correct thing to be doing? I should mention the last 3 lines are inside a function which returns a const A&.
I looked for an answer on stackoverflow, but couldn't find a duplicate of this question, which surprises me.
List > Vector because I will be swapping things in and out of the list.
Yes; you will return a reference inside the list if your function returns A& or A const& and a copy if your function returns A.
However, if you are doing this regularly, why not use a std::vector? They have random access iterators and are almost always more efficient than a std::list, unless the objects are large and you have a large number of them. std::list are very cache-inefficient.
This is good as long as you have not advanced to (or past) end().
const A& stuff(std::list<A>& list_of_A, int index)
{
assert(index <= list_of_A.size()); // Protect against UB
// of advancing past end.
std::list<A>::iterator iter = list_of_A.begin();
std::advance(iter, index);
if (iter == list_of_A.end())
{ throw std::runtime_error("Failed"); // Not allowed to de-reference end()
}
return *iter;
}
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.