I'm currently trying to run through a vector of ints with an iterator. Every time I run through it, I'm only getting a value of 0. Both vectors do have valid data in them.
OffenseName and Down are private member data that the user inputs.
vector <string> :: iterator itr;
vector <int> :: iterator itrdown;
int count = 0;
for (itr = dataOffenseName.begin(); itr!= dataOffenseName.end(); ++itr)
{
if ( OffenseName == *itr )
{
for (itrdown = dataDown.begin(); itrdown != dataDown.end(); ++itrdown)
{
//Here itrdown is always coming at 0. The dataDown vector
//does have valid data in it
if (Down == *itrdown)
{
count++;
}
else
{
break;
}
}
}
}
return count;
if (Down = *itrdown)
Come on, the oldest trick in the book :)
if (Down == *itrdown)
will be correct.
if (Down = *itrdown)
should be
if (Down == *itrdown)
The former is not a comparison, just an assignment, meanwhile you meant second: comparison.
However, there is a more important lesson in here: turn the warnings on when building. If you had done, you could not have missed this one unless you disregard warnings without evaluating them which is not a good idea.
That is probably a more important lesson to learn out of this situation because you would not need to debug issues like this at all then. ;-)
Related
I've been having a headache with this problem. I've challenged myself to make a CLI space shooter, where you have you spaceship, lasers and meteors to shoot. The problem is this: whenever a laser or a meteor reaches the boundaries of our arena, I want to erase from the vector so that it wouldn't cluster it up. Here's how my working code looks now:
std::vector<Meteoras>::iterator itMet = meteorai.begin();
std::vector<Lazeris>::iterator itLaz = lazeriai.begin();
while (itMet != meteorai.end() || itLaz != lazeriai.end())
{
if (itLaz != lazeriai.end())
{
if (itLaz->getCoord()->x == j && itLaz->getCoord()->y == i)
{
itLaz->move();
++j;
if (itLaz->getCoord()->x >= ILGIS - 1) continue;
else std::cout << itLaz->getIcon();
}
++itLaz;
}
if (itMet != meteorai.end())
{
if (itMet->getCoord()->x - 1 == j && itMet->getCoord()->y == i)
{
itMet->move();
++j;
if (itMet->getCoord()->x <= 0) continue;
else std::cout << itMet->getIcon();
}
++itMet;
}
}
So there are two "continues" in there. Instead of them I tried placing iterator removals (as in itLaz = lazeriai.erase(itLaz) but that the program seemed to crash during runtime giving the error that I stated before. I tried doing other logical checks but that didn't seem to work either. I would appreciate if someone could explain a proper way of removing a useless object (in this case a meteor/laser) from a vector.
The comment about erase() invalidating an iterator is true. The part about needing to restart at the beginning is wrong. (You could do, but you certainly don't need to.)
erase() returns a still-valid iterator which points to the element after the erased item.
if( test for erasing )
{
itLaz = lazeriai.erase(itLaz);
}
else
{
++itlaz;
}
You might be able to refactor your code to use std::remove_if() instead.
I'm new to C++ so I'm having trouble figuring out how to best remove an object from a vector while still iterating through it.
Basically, I need to iterate through two vectors. For every item, if the ID's match, I can remove them.
//For every person, check to see if the available bags match:
for(std::vector<Person>::iterator pit = waitingPeopleVector.begin(); pit != waitingPeopleVector.end(); ++pit) {
for(std::vector<Bag>::iterator bit = waitingBagsVector.begin(); bit != waitingBagsVector.end(); ++bit) {
int pId = pit->getId();
int bId = bit->getId();
if(pId == bId){
//a match occurs, remove the bag and person
}
}
}
Working with iterators is a bit confusing, I know I can use the .erase() function on my vectors, but I can't really pass pit or bit. Any help appreciated. Thanks
From the standard:
The iterator returned from a.erase(q) points to the element
immediately following q prior to the element being erased. If no such
element exists, a.end() is returned.
I would use it in something like using the erase method:
std::vector<Person>::iterator pit = waitingPeopleVector.begin();
std::vector<Bag>::iterator bit = waitingBagsVector.begin();
while (pit != waitingPeopleVector.end())
{
bool didit;
while (bit != waitingBagsVector.end())
{
didit = false;
if (pit->getId() == bit->getId() && !didit)
{
bit = waitingBagsVector.erase(bit);
pit = waitingPeopleVector.erase(pit);
didit = true;
}
else
{
++bit;
}
}
if (didit)
continue;
else
++pit;
}
Using the erase-remove idiom will achieve this objective, the below offers an (untested) way using lambdas and <algorithm> functions to remove elements from wPL which have the same ID as wBL. It shouldn't be too much effort to extend this to both lists. Note, we have used std::list instead of std::vector for faster removal.
std::list<Person> wPL;
std::list<Bag> wBL;
//...
wPL.erase(std::remove_if(wPL.begin(), wPL.end(),
[&wBL](auto x) { return std::find_if(wBL.begin(), wBL.end(), [](auto y)
{ return x.getId() == y.getId();); }), wPL.end() };
I am quite green regarding vectors, and this is my first time actually using them for collision checking. This is for my project, and I am stumped on how to implement the collision. The current Collision check and response codes I have seem to be ... bad design.
This is my code:
for(auto it = ArrayofEntities.begin(); it != ArrayofEntities.end(); it++)
{
CEntity * go = (*it);
for(auto i = ArrayofEntities.begin(); i != ArrayofEntities.end();)
{
//Collision for entities. Collision Event returns the iterator after an element is erased.
CEntity * other = (*i);
if (go != other)
{
if (!theCollision.CheckCollision(go, other, false, false, false, false)) //Checks if it has collided go with other
{
i = go->CollisionEvent(*other, ArrayofEntities); //Run collision code, setting i to the iterator which is returned.
//break;
}
else
{
i++;
}
}
else
{
i++;
}
}
}
CEntity is the base class for all the entities.
My CheckCollision just returns a true or false on the collision, and my collision event runs the collision and returns an iterator (because I might have to destroy things in the vector).
My collision event is below
vector<CEntity*>::iterator bullet::CollisionEvent(CEntity &other, vector<CEntity*> & theArray)
{
case ZOMBIE:
{
other.hp -= power * 0.01;//Effect
int Counter, index, bulletindex;
auto it = theArray.begin();
//Find the bullet and the other in the array.
for (it = theArray.begin(), Counter = 0; it != theArray.end();it++, Counter++)
{
CEntity *go = NULL;
go = (*it);
if (go == &other)
{
index = Counter;
}
if(go->ID == BULLET && go->GetX() == GetX() && go->GetY() == GetY())
{
bulletindex = Counter;
}
}
this->~bullet();//Delete the bullet
theArray.erase(theArray.begin() + bulletindex);
if(other.hp <= 0)
{
other.~CEntity();
it = theArray.erase(theArray.begin() + index); //delete from array.
return it;
}
it = theArray.begin() + index;
return it;
}
}
I have basically done this like how I would do an array. Just check it against itself. The error it gives is "Vector Iterator not Incrementable", on the first for loop after the collision event has been run.
So my question: 1) What am I doing wrong?
2) Is my thinking to do this like checking arrays wrong?
This is my school project, so I have full control of the codes.
I would prefer to have a quick fix over a complete rewriting of all the collision codes, but if it really comes down to it, I will rewrite my codes.
If you look at the implementation of std::remove_if, you'll see that they've solved the issue of iterator invalidation in another way. instead of erasing elements, they move them to the end of the array.
This may be the easiest solution for you as well. Keep an iterator which points after the last "live" entirty. It starts out at .end but as bullets hit things, you swap the entities to the back of your range and decrement that last-live iterator.
Then, when you're done looping over your array, you clean up with a single call to .erase.
And yes, you should use either std::unique_ptr<CEntity> or std::shared_ptr<CEntity> in the collection. In that way, .erase won't just erase the pointer but also the object pointed to.
I made a game, rambo shoots bullets and bullets hit zombies and I want to erase the zombie who got hit, from the zombie vector.
This nested loop checks collision between every zombie and bullet one by one. It works good for a while but when I start killing more, at some point, it crashes because it wants to use a function of erased zombie.
for ( it = zombies.begin(); it != zombies.end(); ++it ) {
it->attack();
for (rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it) {
if(checkBasicCollision(it,rambo.it) && it != zombies.end()){
zombies.erase(it);
}
}
}
I've added it--; after zombies.erase(it); works better now but it still crashes sometimes.
I think its happening like, for example there are 5 zombies and 20 bullets, zombie iterator is at second zombie, and second zombie starts the bullet loop to check if it got hit. Loop starts, lets say third bullet hit the zombie, but loop is still going, even if zombie is erased, it still continues the loop.
I've added break; after zombies.erase(it); now it hasn't got any problem. But the code looks so dirty. Is there another way to erase the current element easily
While the solution for manually erasing was presented, note that it is not the most idiomatic one. In idiomatic C++ you would make use of the std::remove_if algorithm in the erase-remove idiom like so:
// 1. A predicate that check whether a zombie was it by any bullet:
auto is_zombie_hit = [&rambo](Zombie const& zombie) {
auto is_bullet_hitting_zombie = [&zombie](Bullet const& bullet) {
return checkBasicCollision(zombie, bullet);
};
return std::any_of(
rambo.bullets.begin(),
rambo.bullets.end(),
is_bullet_hitting_zombie
);
};
// 2. Use the erase-remove idiom:
zombies.erase(
std::remove_if(zombies.begin(), zombies.end(), is_zombie_hit),
zombies.end()
);
Note: yes, you can use lambda in-place, however I prefer naming them to indicate their role.
Note: this uses C++11, however replacing lambda with predicates is trivial and an implementation of any_of is easy enough to produce, much like all_of and none_of.
To use erase you need to use the returned value and assign it back to the iterator so it is valid for the next iteration.
for ( it = zombies.begin(); it != zombies.end(); ) {
it->attack();
for (rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it) {
if(checkBasicCollision(it,rambo.it) && it != zombies.end()){
it = zombies.erase(it); // erase will increment the iterator
}
else{
++it; // no erase, increment the iterator manually
}
}
}
From the documetion for vector::erase the return value is:
An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
When erasing vector elements, iterators and indices become invalidated. Also your code is incorrect for the case 2 or more bullets hit the same zombie (if it is possible). Because with the second bullet the inner loop will try to erase the zombie that was already hit. Instead, you should do it like this:
for ( uint i = 0; i < zombies.size(); ++i)
{
for( auto it = rambo.bullets.begin(); it != rambo.bullets.end(); ++it)
{
if(checkBasicCollision(zombies[i], it)
{
zombies.erase( zombies.begin() + i );
--i;
break; // zombie is dead (well, technically it was already dead)
// so no further checks are needed (i.e. exit inner loop)
}
}
}
Perhaps something like this:
auto zombie_tail = std::remove_if(zombies.begin(), zombies.end(), [&](Zombie const & zombie) {
zombie.attack();
return std::any_of(rambo.bullets.begin(), rambo.bullets.end(), [&](Bullet const & bullet) {
return checkBasicCollision(zombie, bullet);
});
});
zombies.erase(zombie_tail, zombies.end());
Alternatively, if you wanted to stay away from c++ algorithms:
for (it = zombies.begin(); it != zombies.end(); ) {
it->attack();
// Attempt to find a bullet that hit.
for(rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it)
if (checkBasicCollision(it, rambo.it))
break;
// Possibly remove the zombie, and advance the iterator
if (rambo.it != rambo.bullets.end())
it = zombies.erase(it);
else
++it;
}
direct, easy to read and grasp, but maybe not very fancy;
for ( auto& z : zombies )
z.attack();
for( auto& b : rambo.bullets )
{
auto itr = zombies.begin();
while( itr != zombies.end() )
{
if( checkBasicCollision(b,*itr) )
itr = zombies.erase(itr);
else
++itr;
}
}
checkBasicCollision now takes references, not iterators
No doubt some of you have seen my recent posting, all regarding the same program. I keep running into problems with it. To reiterate: still learning, not very advanced, don't understand pointers very well, not taking a class, don't understand OOP concepts at all, etc. This code just merges two sorted vectors, farray and sarray, into a single sorted vector. At least, I hope that's what it does. Tell me:
//int num is to find the size of the original vector and
//build up farray and sarray; not used in the merge process
int num = original.size()
std::vector<int> final;
std::vector<int>::iterator it = farray.begin();
std::vector<int>::iterator iter = sarray.begin();
//farray.size() == (0 thru (num / 2))
//sarray.size() == ((num / 2) thru num)
for (;it != farray.end() && iter != sarray.end();) {
if (*it > *iter) {
final.push_back(*it);
it++;
}
else
{
final.push_back(*iter);
iter++;
}
if (it == farray.end()) {
for (int i = 0; iter < sarray.end(); i++) {
final.push_back(*iter);
}
}
if (iter == sarray.end()) {
for (int i = 0; it < farray.end(); i++) {
final.push_back(*iter);
}
}
}
I rewrote the merge part of my merge sort function so as to...well, make it work. I actually have several questions about this code:
Is it good form to compare against std::vector::iterators it && iter for my last two if statements if the for loop might change them on its next pass?
Will the values of iter and it change on this loop's last pass and screw up my code? Will putting my last if statements before the *it and *iter comparison?
Does the end() member function refer to the last value of whatever is calling it? It seems like it might extend past it somehow.
EDIT: I will respond to all replies tomorrow, so check back then if you want to hear more. It's past midnight. G'night.
1 . It's fine to compare iterators which are from the same container as a for loop condition, but this only makes sense if you are moving one or other iterators in either the increment part if the for loop statement or in the body of the for loop itself. In this for loop you compare iter against sarray.end() but the for loop never changes iter. This means that either there will be no iterations or the for loop will never terminate. Also, you probably want to use != and not < for the comparison. == and != work for all iterators, < doesn't.
for (int i = 0; iter != sarray.end(); i++) {
final.push_back(*iter);
}
As iter starts where you want the loop to begin, you may want something like this:
for (; iter != sarray.end(); ++iter) {
final.push_back(*iter);
}
As you're still learning (although aren't we all!), it's probably instructive to work through an algorithm like this, but you should be aware of std::merge which probably does what you want.
std::merge( farray.begin(), farray.end(), sarray.begin(), sarray.end(), std::back_inserter( final ) );
(You need to #include <iterator> and <algorithm>.)
2 . I don't see incrementing iter or it in the outer for loop invalidating the logic in the later for loops, the point in 1. aside.
3 . end() points to one past the end of a container, so you can use it for loop termination checks, but you shouldn't try to dereference an iterator which is "==" to ".end()".
I didn't check your algorithm's implementation, I will just refer to your three questions:
Iterators are much like pointers to values of a container. It's exactly like using size_t i and then ++i in the for loop. would you feel it's problematic to compare farray[i] with sarray[i]? probably not, therefore it's OK.
What I see you doing in your code here, is that you just read the values of *it and *iter, you don't actually change them, therefore they won't change.
The end() points to an invalid place. It doesn't point to the last value, but to "after it". It's like "NULL" if you will, therefore if(iter == sarray.end()) is true, you will crash if you will write *iter, because you can't dereference an iterator which is equal to end().
Some general advice: You need to think about variable names. Calling your iterators 'it' and 'iter' is going to confuse you at some point. Actually, if you look closely, it already has. If 'farray' and 'sarray' are meaningful names, how about 'fiter' and 'siter'.
Also, think through what the merge sort is doing. Those last two blocks are there just to "drain" whichever iterator has some stuff left. So they don't need to be in the first loop.
I'd probably write it as (pseudocode):
while not (list1.empty and list2.empty):
if list1.empty:
result.push(list2.pop)
else if list2.empty:
result.push(list1.pop)
else if list1.top > list2.top:
result.push(list2.pop)
else:
result.push(list1.pop)
Or in somewhat rusty cargo-culted C++:
std::vector<int>::iterator fiter = farray.begin();
std::vector<int>::iterator siter = sarray.begin();
while (fiter != farray.end() || siter != sarray.end()) {
if (fiter == farray.end()) final.push_back(*siter++);
else if (siter == sarray.end()) final.push_back(*fiter++);
else if (*fiter > *siter) final.push_back(*siter++);
else final.push_back(*siter++);
}
You have a few things to think about here.
First, if you are merging two ranges you would be much better off using the std::merge function rather that rolling your own.
Your code is a little difficult to read because you use varying styles for indentation and where you out your curly braces. Pick a style & stick to it.
The first part of your for loop seems to be a correct implementation of a merge:
for (;it != farray.end() && iter != sarray.end();) {
if (*it > *iter) {
final.push_back(*it);
it++;
}
else
{
final.push_back(*iter);
iter++;
}
...and this should be all you need to get the job done.
The second part of your loop has a couple problems:
for (;it != farray.end() && iter != sarray.end();) {
: :
if (it == farray.end()) {
for (int i = 0; iter < sarray.end(); i++) {
final.push_back(*iter);
}
}
if (iter == sarray.end()) {
for (int i = 0; it < farray.end(); i++) {
final.push_back(*iter);
}
}
}
For one thing, the for() conditionals are written so that both it and iter must not point to the end() of their respective collection, or else the loop ends. So it can never point to sarray.end(), iter can never point to farray.end(), and neither if statement can ever fire. They are both dead (unreachable) code.
But even if they weren't dead code, they have bugs. The conditional in the for(...) breaks the loop when the iterator points to the end of the collection, but this iterator is never moved, so you have an infinite loop.
Again both of these for(...)s are uneeded dead code because the iterators can never point to the end of the vector.
One simple comment: why not use while (condition) instead of for(; !condition; ).
The latter construction is nonstandard and hard to understand!