Program crashes when attempting to erase the last std::vector element - c++

I'm iterating through the vector std::vector<Bullet*> bullets, and I'm looking for collisions with an enemy. It works great in every case, except for the case when: the last fired bullet (there has to be more than one) collides with the enemy.
Code-
for(std::vector<Bullet*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
{
if ((*it)->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete *it;
bullets.erase(it);
enemy->destroy();
if (bullets.size() == 0)
break;
}
}
I commented particular elements in the for loop, and found out that the bullet.erase(it) call crashes the program.
When that crash happens, I receive a return code: 134 (0x86). What's the issue for that code?
(*it)->getSprite() returns a pointer to a sprite from Bullet class.

What about using remove_if and erase combo:
auto is_hit = [&enemy](Bullet *bullet)
{
if (bullet->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete bullet;
enemy->destroy();
return true;
}
return false;
};
bullets.erase(std::remove_if(bullets.begin(), bullets.end(), is_hit), bullets.end());

For your consideration:
The following code snippet shows how I clean a vector from its tail (the complementary action to adding element to the tail with push_back())
while(!gBoard.empty())
{
Cell_t* cell = gBoard.back(); // fetch last element (a ptr)
gBoard.pop_back(); // remove last element
delete cell; // remove cell from heap - raw pointer
}
Perhaps you could do this style of clean and use multiple vectors ... it still might be faster than alternatives.
In your problem, each bullet appears to have at least two destinations ... hit or miss.
while ( ! Bullets.empty() ) // spin through bullet list
{
Bullet* aBullet = Bullets.back(); // fetch copy of last element
Bullets.pop_back(); // remove last element
if (*aBullet)-> getSprite()->getGlobalBounds().
intersects(enemy->getSprite()->getGlobalBounds()))
{
// HIT!
Hit.push_back(aBullet); // capture the element to Hit bucket
enemy->destroy(); // tbd - a decision? or always final?
// no delete
if (bullets.size() == 0) // no more to compute, redundant to while
break;
}
else
{
// MISS
Missed.push_back(aBullet); // capture element to Missed bucket
}
} // while
assert(bullets.empty()); // bullets have been consumed
// clean up spent bullets that intersected
while (! Hit.empty() )
{
Bullet* aBullet = Hit.back(); // copy last element from Hit
Hit.pop_back(); // remove last element from Hit
delete aBullet; // tbr - delete the dynamic memory
}
// clean up spent bullets that missed
// move the bullet from Missed vec back into Bullets vec
// for tbd - furthur evaluation ... did the bullet hit any other obj
// the following also happens to 'undo' the seq reversal
while (! Missed.empty() )
{
Bullets.push_back (Missed.back()); // copy last element from Missed
Missed.pop_back(); // remove last element from Missed
// tbd - also delete the missed bullet?
// or do you check for these bullets to collide with other objects
}
// possibly a copy can do this last loop, but this is simple and
// undoes the reversal.

and found out that the bullet.erase(it) call [for that last element]
crashes the program
In some sense, you are probably performing the erase prematurely.
Consider the following:
It is possible that the test parameters of range, target, and weapon-type might combine to achieve, for example, a 10% hit ratio. Thus, in a collection of 1000 shots, (1000 == bullets.size()), there would be (~) 100 bullets that have hit the target.
Your code finds each of the elements, and creates 100 'holes' in the vector by using bullets.erase(). Because the vector data is maintained contiguous, the erase method also moves other elements to fill the holes created by erase. (The details of how might differ between implementations.)
Generally, 100 erases causes 100 shuffles of less than (at-most) 1000 elements each time ... this one-at-a-time-approach would probably be a comparatively 'slow' process.
As an alternative to the current design, instead of find-and-erase, defer the erase until your code has identified and marked all 'intersects'.
you can find the intersects (hits) in the same way, but 'mark' them, don't erase them yet. Options include adding a bool to the Bullet class, or maintaining a matching bool vector to hold this flag for each bullet.
Using two indexes,
-- i1 initialized to 0 (first (left most) vector element) and
-- i2 initialized to (bullets.size() - 1) [last (right most) vector element]
-- Spin increment i1 to find the first hit,
-- Spin decrement i2 to find the last miss,
-- then std::swap (bullets[i1], bullets[i2])
Repeat until i1 >= i2
Now that all the hits are contiguous AND at the tail of the vector, perform a single erase of the 100 hits
This should eliminate any shuffling.
Also, there should be no use of an erased element ... because the erase occurs at the end of the process.

Related

Nested loops, deleting from vectors

I am trying to make my simple game and I came across small issue. So I have 2 vectors of pointers.
One is for bullets and one is for enemy units.
First I tried to iterate through all of my bullets and then in second loop iterate through my enemies and when I find collision I erase enemy unit and bullet, but erasing bullets is crashing my game, so I figured out that I shouldnt erase from vector while its still iterating in first loop.
My second ide was something like this:
std::vector<Bullet*>::iterator remove = std::remove_if(bullets.begin(), bullets.end(),
[&](Bullet* x)
{
for (std::vector<EnemyUnit*>::iterator it = enemyUnits.begin(); it != enemyUnits.end(); ++it)
{
if (x->collision->CheckCollision((*it)->collision))
{
enemyUnits.erase(it);
return true;
}
else
{
return false;
}
}
});
bullets.erase(remove, bullets.end());
So it seems to work in a way, but my bullets can only collide with one enemy at a time and it seems like its not checking for all enemies. For example I shoot 5 bullets and firstly they can only collide with one enemy and after one bullet kills enemy other bullets will be able to collide with second enemy and so on . It seems like
for (std::vector<EnemyUnit*>::iterator it = enemyUnits.begin(); it != enemyUnits.end(); ++it)
Doesnt take place and it only gets one enemy? Is it wrong way to do it?
I need to use pointers and vectors, because its for my project.
The predicate for std::remove_if returns after the first check, no matter what.
To fix this, the predicate can return on a collision, or at the end (after the loop) on no collision, e.g.
for (auto it = enemyUnits.begin(); it != enemyUnits.end(); ++it)
{
if (x->collision->CheckCollision((*it)->collision))
{
enemyUnits.erase(it);
return true;
}
}
return false;
This way, if a collision is detected, it will exit early and return true.
If there's no collision with any enemy, it will stop after the loop and return false.
Answer advising to remove the return statements is correct. The return statements are exiting the inside loop early, but that isn't the only problem. Removing those returns will reveal the next problem. You need:
it = enemyUnits.erase(it);
to correctly set the iterator after an erase to prepare for the next pass through the loop.

How to get exactly one element from an unordered_set?

I have:
std::unordered_set<ObjectRepresentation*> incompletePieces;
I would like to get exactly one object from the unordered_set. To do that I am using a for loop, and "break", at the end of the loop so that the loop runs at most once.
while (incompletePieces.size()){
for (auto containedPiece : incompletePieces){ //Warning at this line that loop will run at most once
// .... doing some stuff with the contained piece
incompletePieces.erase(containedPiece);
break;
}
}
This is the desired behaviour that I want. The problem is that the compiler shows a warning:
Loop will run at most once (loop increment never executed)
How do I rewrite my code so that the warning goes away ? Is there a better way to get an item from the unordered_set ?
You could use begin() to get the first element.
if (incompletePieces.size() > 0)
auto containedPiece = *(incompletePieces.begin());
The code you presented does in fact process all elements and clears the set of them as it gets done, but it does so in a highly unidiomatic way.
There are two idiomatic ways of doing this, depending on whether processing an element could modify the set itself.
1) If the "doing some stuff" code is guaranteed to not touch incompletePieces (i.e. completing one piece does not create additional incomplete pieces), then the idiomatic and efficient solution is to just loop over the set and clear it afterwards:
for (auto piece : incompletePieces) {
// process piece
}
incompletePieces.clear();
2) If this is not the case, or you really need to clear elements as you go, then the idiomatic solution is still iterator based looping:
auto it = incompletePieces.begin();
while (it != incompletePieces.end()) {
// process *it
#if C++11
it = incompletePieces.erase(it);
#else
auto prev = it++;
incompletePieces.erase(prev);
#endif
}
Whereas *unordered_set::begin() will give you first element (no unordered_set::front()),
I would rewrite:
while (incompletePieces.size()){
for (auto containedPiece : incompletePieces){
// .... doing some stuff with the contained piece
incompletePieces.erase(containedPiece);
break;
}
}
into:
for (auto* containedPiece : incompletePieces){
// .... doing some stuff with the contained piece
}
incompletePieces.clear();
You can rewrite the code as below:
for(auto* containedPiece : incompletePieces){
//Process the set contents
}
//Clear entire set in one go
incompletePieces.clear();
If you want to clear it one by one, you would have to use iterators as shown below:
auto it = incompletePieces.begin(); //Take the pointer to first element of set
for( ; it !=incompletePieces.end() ; it++){
incompletePieces.erase(*it); //Erase one element at a time
}

Removing element from vector / deleting object

I am going crazy about two issues I am having with my code.
I am trying to delete an element from my vector containing a list of objects.
//Remove Object
if (button2 == true)
{
//go through objects and check collision
for (std::vector<cOBJECT*>::size_type i = 0; i != GameObjects.size(); i++)
{
//Check for collision and delete object
if (MouseRect(GameObjects[i]->getrect(), mx + offX, my + offY) == true)
{
//GameObjects[i]->~cOBJECT();
delete GameObjects[i];
GameObjects.erase(GameObjects.begin() + i);
}
}
} // if (button2 == true)
For some reasons I run into two issues.
1) Access violation reading location 0xFEEEFEEE.
It seems to somehow have an issues with me destroying the texture. If I take out the "delete ...." and replace it with the destructor of the object instead, it works fine.
2) Vector subscript out of range
So if I use the destuctor to pass the first problem. I run into the next one. Now even if I use "GameObjects.erase(GameObjects.begin());" I get the same error.
If you think carefully about what the operation you implemented does you will notice that when the i-th element matches you remove that element from the vector and the (i+1)-th element is moved to the i-th position, but at this point the end of the loop is reached and i is incremented, which means that you will not test the element that was in the (i+1) position originally (and is now in i-th position) and also that if the value of i is GameObjects.size() - 1 before the removal the variable i now has a value that is GameObjects.size()+1 and the loop won't terminate.
Regarding the issue with the delete, you should check you created the object. Unless it was allocated with new chances are that you should not call delete on the pointer.

Iterator and reverse Iterator

I am quiet fresh to C++ and programming in general, I am writing an OpenCv application in C++ environment.
WHAT I AM TRYING TO ACHIEVE:
OK, so I got some Rectangles center points stored in a vector, Now I am using a reverse Iterator to iterate over the vector with rectangle center points and store every 10th center point into new vector.
I then again iterate over that new vector that stores every 10th rectangle center point with normal iterator, And I want to subtract 1st element from 2nd element 3rd element from 4th element and so on, the subtraction results, I want to store into another new vector :D
It might be slightly confusing to some people; I am confused, myself, that is why below I will add the code I have written.
vector<Point> Rightarm;
vector<Point> Leftarm;
vector<Point>::reverse_iterator RightMovmentIter;
vector<Point>::reverse_iterator LeftarmMovmentIter;
vector<Point> RightTracking;
vector<Point> LeftTracking;
for(RightMovmentIter = Rightarm.rbegin(); RightMovmentIter != Rightarm.rend(); RightMovmentIter+=10)
{
RightTracking.push_back(*RightMovmentIter);
}
for(LeftarmMovmentIter = Leftarm.rbegin(); LeftarmMovmentIter != Leftarm.rend(); LeftarmMovmentIter+=10)
{
LeftTracking.push_back(*LeftarmMovmentIter);
}
vector<Point>::iterator RresultIter;
vector<Point>::iterator Leftresult_Iter;
vector<Point> summery;
for(RresultIter = RightTracking.begin(); RresultIter != RightTracking.end(); RresultIter++)
{
summery = *RresultIter - *RresultIter++;
}
PROBLEMS:
1st Problem is that when I run the program I get run time error I belief it's because at the begining of the vector Rightarm & Leftarm do not have 10 elements and when the Iterator runs through it and is trying to look for the 10th element i cant....HOW do I work this out then?
2nd Problem is to do with this line summery = *RresultIter - *RresultIter++; I know it's wrong and this is the best attempt I could of think of, but what I want to do is to subtract 1st element from 2nd element and store it in summery element...
Hopefully This describes my problem well enough for the readers
Regards
As you've correctly noticed, this won't work unless Rightarm.size() is an exact multiple of 10. One way to work around this is to skip elements at the beginning, to make the end line up.
for(RightMovmentIter = Rightarm.rbegin() + Rightarm.size() % 10;
RightMovmentIter != Rightarm.rend();
RightMovmentIter+=10)
As for taking the running difference, there's a standard algorithm for that, std::adjacent_difference.
std::adjacent_difference( RightTracking.begin(), RightTracking.end(),
std::back_inserter( summery ) );
summery.erase( summery.begin() );
This copies the first value without taking a difference (similar to assuming the "before-the-first" value is zero) so the erase() line gets rid of that.

CLI/C++ Tetris block crashes when check if cell is occupied is empty MSVisualStudio 2008

Here is my code for checking if future move is legal, I have assumed its legal and copied move into mySquares array. I then call this method in the game cycle set in the form and in the timer handler which is:
canvas->drawGrid();
testBlock->drawBlock();
testBlock->moveDown();//this method has checkBounds for when hit sides, top & bottom
if(newBlock->canMoveDown()==false)
{
newBlock->addMySelfToGameBoard();
mainGameBoard->updateGrid();
}
//timer1 handler finish
bool TTetrisBlock::canMoveDown()
{
array<Point>^ temporaryCopy = gcnew array<Point>(4);
bool canGoDown = true;
for(int i=0;i<mySquares->Length;i++)
{
//Set future move
temporaryCopy[i].X = mySquares[i].X;
temporaryCopy[i].Y = mySquares[i].Y+1;
}
//Check if future move cells are full, if not assign values to mySquares
//Check if future move is legal
for(int j=0;j<temporaryCopy->Length;j++)
{
if(gameBoard->isCellOccupied(temporaryCopy[j].X,temporaryCopy[j].Y) == true)
{
mySquares[j].X = temporaryCopy[j].X;
mySquares[j].Y = temporaryCopy[j].Y;
}
}
return canGoDown;
}
//end of moveDown
in my gameboard class i have the method which checks if TCell is occupied or not. TGameBoar holds an array of TCells which has a color and bool isOccupied = false;
bool TGameBoard::isCellOccupied(int c,int r)
{
//Checks if TCell is occupied
return myGrid[c,r]->getIsOccupied();
}
It Crashes and indicates here was the problem, Im currently learning C++ at school. I would appreciate some help. I am also struggling with the Keydown for moving left and right using e->KeyData == Keys::Left) etc. and creating a newblock when gone through loop.
I have my project rar if you want to check it out. I have all the classes done, its just putting it together is the hard bit.
Project Tetris
I see three problems.
First you should only move mySquares when isCellOccupied returns false (not true as you currently have it). I suspect this is the cause of your crash as it looks like you will be moving a block into a cell that is already occupied.
Second, when isCellOccupied returns true you should set canGoDown to false and break out of your for loop (or better yet, make canGoDown (==true) an additional condition of your for loop i.e. j < temporaryCopy->Length && canGoDown). As it is, your function always return true because it is never set to false and that can't be right.
Just making an assumption here, but don't all mySquares consist of 4 elements? You are initializing temporaryCopy with 4 elements but it isn't clear whether mySquares has 4 elements. If not, this could be dangerous as in your first loop you are looping on mySquares->Length and addressing temporaryCopy with that index value, which could be out of range. And then later doing the opposite. It might be better to use a constant (4) in all all loops or better yet, always use mySquares->Length (especially when creating the temporaryCopy array) to ensure that both arrays contain the same number of elements.