Removing element from vector / deleting object - c++

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.

Related

Segmentation fault during erasing from vector

I'm developing an Asteroid game clone but I'm facing a problem during erasing elements from vector of asteroids. So generally when I hit the asteroid it should split into 3 parts. So I create 3 new asteroids and erase the old one an then it crashes.
void Level::missleAsteroidCollision(){
std::cout<<this->asteroidVector.size()<<std::endl;
for(auto ptr = this->missleVector.begin();ptr!=this->missleVector.end();++ptr){
sf::FloatRect missleBounds = (*ptr)->shape.getGlobalBounds();
for(auto ptrTwo = this->asteroidVector.begin(); ptrTwo!= this->asteroidVector.end();++ptrTwo){
if(missleBounds.intersects((*ptrTwo)->shape.getBounds()) && (*ptrTwo)->isFinalForm == false){
for(int i = 0; i < 3; ++i){
this->createAsteroid((*ptrTwo)->origin,true);
}
delete *ptrTwo;
this->asteroidVector.erase(ptrTwo);
}
else if(missleBounds.intersects((*ptrTwo)->shape.getBounds()) && (*ptrTwo)->isFinalForm == true){
delete *ptrTwo;
this->asteroidVector.erase(ptrTwo);
}
}
}
}
First of all, when you use the .erease function, the iterator is changed so you need to update it, in your case, ptr = this->asteroidVector.erase(ptrTwo); the iterator will now point to the next element after deletion so keep that in mind (either you decrease the pointer by one or you only increase the ptr (ptr++) if you did not use the .erase function.
Secondly, I believe this->createAsteroid((*ptrTwo)->origin,true); creates new items, this will also invalidate the iterator, so one fix could be, creating the new asteroids after checking and deleting the old one. Maybe store the new asteroids in a vector created before the for loop, adding the new meteorites there and after the for loop add the vector to your current vector of asteroids.
Inserting an item into a vector (whether or not at the end of the vector) can invalidate all iterators to that vector. I suspect your createAsteroid does so.

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

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.

Deleting an element from an array of objects

I tried to write a function that gets an object ("Stone") and deletes the stone from a given array. code:
void Pile::del_stone(Stone &s)
{
Stone *temp = new Stone[size - 1];//allocate new array
for (int i = 0;i <size;++i)
{
if (s != Pile_arr[i])//if the given object to delete is different from the current
{
temp[i] = Pile_arr[i];//copy to the new array
}
else
{
i--;
}
}
Pile_arr = temp;
set_size(this->size - 1);
temp = NULL;
delete[] temp;
}
Pile_arr is a member of Pile class.
The problem is that i get an infinite loop, because i decrease i. I cant figure out how to solve this issue. Any ideas?
Use two indexes: i and j. Use i to know which element of the original array you are looking and j to know where to put the element in temp.
You need to use a separate counter to track where new elements should be placed.
I have used n below:
Stone *temp = new Stone[size - 1];
int n = 0; // Stores the current size of temp array
for (int i = 0;i <size;++i) {
if (s != Pile_arr[i]) {
temp[n++] = Pile_arr[i];
}
}
It's also worth considering the case where s is not found in the array, as this would cause a runtime error (Attempting to add size elements to an array of size size - 1).
Using a STL container would be a far better option here.
This function will:
Allocate a new array of length size-1
Search for the intended object
If you find it, copy it to the same exact position in the array
If you don't --i
Finally, ++i
First of all, this function is bad for 3 reasons:
It only copies one item over--the given item. You have an array with only 1 object.
It copies the item from index to index. Since the final array is one smaller, if the object is at the max original index, it will be out of bounds for the new array.
If the object is not immediately found, the array will get stuck, as you decrease the index, and then increase it using the loop--you'll never move again.
Stone *temp = new Stone[size - 1];//allocate new array
for (int i = 0;i
Instead:
Cache the found object, then delete it from the original array or mark it. temp = found object
Copy the array, one by one, without copying empty spaces and closing the gap. Copy temp_array[i] and increment i if and only if temp_array[j] is not marked/deleted. Increment j
Decide where to put the found object.
Once again, you can decide to use separate indexes--one for parsing the original array, and one for filling the new array.

C++ Access Violation Reading location 0x003AE000 when trying to erase from vector

I am getting an error when I try to erase an item from an vector if the item is set to not alive by a boolean. I have tried searching the net but havn't found anything about it. I have tried to find different ways to delete elements on index x in a vector and found the function: vector.erase(vector.begin() + index)
So when I try using it in my for loop I get access violation reading location pointing at the erase function line.
Code for the loop where the error is:
if (!player.getBullets().empty())
{
for (int x = 0; x < player.getBullets().size(); x++)
{
//Check for projectiles whos status is dead.
if (!player.getBullets()[x]->getAlive())
{
//Erase the element at position x.
player.getBullets().erase(player.getBullets().begin() + x);
}
}
}
Don't reinvent the wheel, especially not the really difficult one that can go under water and up walls. Use a ready-made one:
#include <algorithm>
player.getBullets().erase(
std::remove_if(player.getBullets().begin(),
player.getBullets().end(),
[](Projectile * p) -> bool { return !p->getAlive(); }),
player.getBullets().end());
(I'm assuming that Bullet is the same as decltype(player.getBullets())::value_type, i.e. the element type of the bullets container. Adjust to suit.)
It's much easier to erase correctly if you use an iterator instead of an index. An (almost) direct conversion of your code:
vector<Projectile*> bullets = player.getBullets();
for (vector<Projectile*>::iterator x = bullets.begin(); x != bullets.end(); )
{
//Check for projectiles whos status is dead.
if (!(*x)->getAlive())
{
//Erase the element at position x.
x = bullets.erase(x);
}
else
{
++ x;
}
}
Note that this only works with a local copy of the vector. If you want to update the vector in the player class itself, you will need to change getBullets to return a reference:
vector<Projectile*> &Sprite::getBullets()
{
return bullets;
}
And then for the loop:
vector<Projectile*> &bullets = player.getBullets();
Though the loop is invalid because counter x is increased while the size of the vector is decreased and as the result not all the vector is traversed I do not see the reason of the access violation. I think that the problem is related to objects stored in the vector.

deallocating memory in a map with pointers

I was trying to erase pointer elements (the value in the map is a pointer) from the map and I saw the code here What happens to an STL iterator after erasing it in VS, UNIX/Linux?
for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
// wilhelmtell in the comments is right: no need to check for NULL.
// delete of a NULL pointer is a no-op.
if(it->second != NULL) {
delete it->second;
it->second = NULL;
}
}
I am not sure if the 'delete it->second' with de-allocate the correct memory because the erase(it++) step already moves the iterator to the next object. By the time, it reaches the delete statement, it is pointing to the next element which we don't want to delete. Am I missing something?
I believe this will work as expected.
The third section of the for loop (where the iterator is erased and then incremented) executes after the first iteration, and so on for each relevant iteration. Thus, you're always erasing the element you've already "dealt with" in the loop contents.
A parallel example:
for (int i = 0; i < 1; ++i) { ...
You will still enter the loop and execute with i = 0 before incrementing i and checking the looping condition.
You may want to try another way:
while (T2pS.size() > 0) {
if (T2pS.begin()->second != NULL) {
delete T2pS.begin()->second;
}
T2pS.erase(T2pS.begin());
}