Reset iterator inside loop with erase method - c++

Here's a little piece of code, which is making us a little mad...
for (vector<QSharedPointer<Mine>>::iterator itMine = _mines.begin();
itMine != _mines.end();) {
auto point2 = shipFire[l].getBulletLine().p2();
if (_mines[pos]->checkCollision(point2)) { // _mines is a Vector<QSharedPointer<Mine>>
Explosion explosionMine(_mines[pos]->point());
_explosions.push_back(explosionMine);
itMine = _mines.erase(itMine);
bulletErased = true;
scoreFrame += 100;
}
else {
++itMine;
pos++;
}
}
Here is the problem, the itMine is erasing two of our vector<..<Mine>> and it's making the program to shutdown unexpectedly
We thought about it, and came up with this : All of our iterators are being invalidated after erasing the one of the Mine, right ?
But we are a little confused about how to change our actual code to fit to the new one ?
The main question is : how do we reset this itr ?
ps : if you have any questions, or if you need a little more of code to understand the logic behind, feel free to ask more question !
Best Regards, and thank you in advance.

A not uncommon solution to this problem is to swap the current position with the back position. The last item can then be removed without invalidating any iterators. See live example.
#include <iostream>
#include <vector>
#include <memory>
int main()
{
struct Mine
{
Mine( int id = 0 ): id( id ) { }
bool check_collision_point( int point ) const { return id % point == 0; }
int id;
};
auto mines = std::vector<std::shared_ptr<Mine>>{ 7 };
for( auto i = 0u; i < mines.size(); ++i )
{
mines[i] = std::make_shared<Mine>( i );
}
// save point outside loop
auto point = 2;
for( auto it = mines.begin(); it != mines.end(); )
{
// don't use mines[pos] in loop
if( (*it)->check_collision_point( point ) ){
std::iter_swap(it, mines.rbegin() );
mines.resize( mines.size() -1 );
}
else
++it;
}
for( auto it = mines.begin(); it != mines.end(); ++it)
std::cout << (*it)->id << " ";
}

Update
for (auto itMine = _mines.begin(); itMine != _mines.end();){
auto point2 = shipFire[l].getBulletLine().p2(); // thomas : -> here we need to do it in the loop, to get every bullet position while looping.
if (_mines[pos]->checkCollision(point2)){
Explosion explosionMine(_mines[pos]->point());
_explosions.push_back(explosionMine);
if (_mines[pos]->checkCollision(point2)){
mines_to_be_deleted.push_back(*itMine);
shipFire.erase(it);
bulletErased = true;
scoreFrame += 100;
qDebug() << "Collision bullet mine";
}
}
//else
++itMine;
pos++;
}
for (auto itrDelete = mines_to_be_deleted.begin(); itrDelete != mines_to_be_deleted.end();)
{
_mines.pop_back(); // Here instead of that, we need to erase the current itr (here is our mistake)
//mines_to_be_deleted.erase(itrDelete);
//_mines.resize(_mines.size());
qDebug() << "mine deleted";
++itrDelete;
}
mines_to_be_deleted.clear();
At this point we came up with this.
We tried everything you told us, but none of them worked, we thinked about swapping the current itr to the end of the vector. Thanks for your help.
For now, the program is running perfectly, but when we hit a Mine with a bullet, a ramdom one disapear.
For those who want to know, we have found the solution.
while(!mines_to_be_deleted.empty()) { // _mine_to_be_deleted is a QList<Sharedpointer<Mine>>
std::remove(begin(_mines),end(_mines),mines_to_be_deleted.front());
_mines.pop_back();
mines_to_be_deleted.pop_front();
}
using std::remove allow us to erase the Mine without creating a malloc()

Related

Error when trying to remove an object from a list while going through it in C++

Hi I am a beginner in c++ and I would like to know why this code returns a Debug Assertion Failed error everytime an object is erased from the list.
for (auto it = ProjectileList.end(); it != ProjectileList.begin();) {
--it;
if (it->position_y < 0) {
ProjectileList.erase(it);
}
else {
it->Draw(window.renderer);
it->position_y--;
}
}
You have to assign the new iterator that the function erase() returns to it.
for (auto it = ProjectileList.end(); it != ProjectileList.begin();) {
--it;
if (it->position_y < 0) {
it = ProjectileList.erase(it); // assign the new iterator
}
else {
it->Draw(window.renderer);
it->position_y--;
}
}
Another solution would have been to write two loops, one that erases, and the other that draws. Using the std::remove_if could be used:
#include <algorithm>
//...
// Erase
auto iter = std::remove_if(ProjectileList.begin(), ProjectileList.end(),
[&] (auto& p) { return p.position_y < 0; });
ProjectileList.erase(iter, ProjectileList.end());
// Now draw the remaining ones.
for (auto& p : ProjectileList)
{
p.Draw(window.renderer);
p.position_y--;
}

a runtime error on my leetcode submission :heap use after free on address

#include <bits/stdc++.h>
using namespace std;
#include <unordered_set>
#include <queue>
struct word {
string s;
int level;
word(string a, int b)
: s(a)
, level(b)
{
}
};
bool isadj(string s1, string s2)
{
int len = s1.length(), count = 0;
for (int i = 0; i < len; i++) {
if (s1[i] != s2[i])
count++;
if (count > 1)
return false;
}
return count == 1 ? true : false;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList)
{
unordered_set<string> st;
for (string s : wordList)
st.insert(s); // adding elements into a set
if (st.find(endWord) == st.end())
return 0;
queue<word> q;
q.push(word(beginWord, 0)); // initialising the queue
while (!q.empty()) {
word temp = q.front(); // pop the current string
q.pop();
if (temp.s == endWord)
return temp.level;
for (auto it = st.begin(); it != st.end(); it++) { // loop over the set to find strings at a distance of 1 and add them to the queue
if (isadj(temp.s, *it)) // i have inserted code here to print the string *it
{
q.push(word(*it, temp.level + 1));
st.erase(*it); // delete the element to avoid looping
}
}
}
return 0;
}
int main()
{
// make dictionary
vector<string> D;
D.push_back("poon");
D.push_back("plee");
D.push_back("same");
D.push_back("poie");
D.push_back("plie");
D.push_back("poin");
D.push_back("plea");
string start = "toon";
string target = "plea";
cout << "Length of shortest chain is: "
<< ladderLength(start, target, D);
return 0;
}
The problem i am trying to solve is https://leetcode.com/problems/word-ladder/
I am unable to trace where I am using a memory that was deallocated again in my program?
The following are my attempts to debug :
I tried to run it on another online ide where the code compiles and runs successfully but gives a wrong answer . in order to debug it I have inserted some lines into my code in order to print all the strings which are at a distance of 1 for my current string. surprisingly an empty string is appearing to be in the set. Please help me in understanding where am I doing a mistake.
unordered_set::erase returns a value, and this returned value is important. You should not ignore it.
In your case, once you erase something from the set, it is invalid. Trying to increment it results in Undefined Behavior.
The correct approach is to replace the current iterator with the returned one, then not increment during the loop.
for (auto it = st.begin(); it != st.end(); )
if (...) {
// ...
it = st.erase(*it);
} else
++it;
After the line:
st.erase(*it); // delete the element to avoid looping
the it iterator is not valid and should not be used.
Your problem seems to be already addressed, but if you'd be interested, this'd also pass without using std::queue, only using std::unordered_set:
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
std::cout.tie(NULL);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>
using ValueType = std::int_fast16_t;
static const struct Solution {
static const int ladderLength(
const std::string start,
const std::string end,
const std::vector<std::string>& words
) {
std::unordered_set<std::string> words_map(std::begin(words), std::end(words));
std::unordered_set<std::string> head;
std::unordered_set<std::string> tail;
std::unordered_set<std::string>* curr_head;
std::unordered_set<std::string>* curr_tail;
if (words_map.find(end) == std::end(words_map)) {
return 0;
}
head.insert(start);
tail.insert(end);
ValueType ladder = 2;
while (!head.empty() && !tail.empty()) {
if (head.size() < tail.size()) {
curr_head = &head;
curr_tail = &tail;
} else {
curr_head = &tail;
curr_tail = &head;
}
std::unordered_set<std::string> temp_word;
for (auto iter = curr_head->begin(); iter != curr_head->end(); iter++) {
std::string word = *iter;
for (ValueType index_i = 0; index_i < word.size(); index_i++) {
const char character = word[index_i];
for (ValueType index_j = 0; index_j < 26; index_j++) {
word[index_i] = 97 + index_j;
if (curr_tail->find(word) != curr_tail->end()) {
return ladder;
}
if (words_map.find(word) != std::end(words_map)) {
temp_word.insert(word);
words_map.erase(word);
}
}
word[index_i] = character;
}
}
ladder++;
curr_head->swap(temp_word);
}
return 0;
}
};
You might want to break it into more methods, a bit too long for a function.
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.

C++ how can i remove vector of pointers in my game

Im trying to make first game sth simillar to Tanks. But my game crashes when i try to delete the enemy tank (sometimes works, sometimes not) and i have no any idea what is wrong with my code. Will you help me?
void Game::update(sf::Time elapsedTime){
if (enemies.size() < 6){
eTank1 = new EnemyTank(150, 130);
enemies.push_back(eTank1);
}
tank->move(elapsedTime);
for (vector<EnemyTank*>::const_iterator it = enemies.begin(); it != enemies.end();)
{
if (collision->checkCollisionWithEnemyTankAndTank(tank, *it)){
(*it)->move(elapsedTime);
}
collision->checkShoot(tank->getBulletsVector(), tank, *it);
//probably there is a problem
if ((*it)->getlife() <= 0){
delete *it;
enemies.erase(it);
cout << "TANGO DOWN" << endl;
}
++it;
}
checkShoot looks like this:
void Collision::checkShoot(vector <Bullet*> &bullet, Tank *tank, EnemyTank* enemyTank){
distance = 0;
for (int i = 0; i < bullet.size(); i++){
distance = abs(bullet[i]->getPosition().x - enemyTank->getPosition().x) + abs(bullet[i]->getPosition().y - enemyTank->getPosition().y);
if (distance < 45){
cout << "act life: " << enemyTank->getlife() << endl;
enemyTank->setLife(); // -0.5 from actual life
tank->setVector(i); // delete bullet from vector
}
}
}
And the code of destructor
cout << "DESTRU";
delete tank_bullet;
for (auto &it : bullets){ delete it; } bullets.clear();
cout << "DESTRU T - END\n";
Finally the setVector(i);
void setVector(int index){
if (!bullets.empty()){
delete (bullets.at(index));
bullets.erase(bullets.begin() + index);
}
}
The answer really is in the comments, but in case they feel too terse, I'm writing a slightly longer explanation.
Here's a minimal example similar in style to your code:
int main() {
auto v = std::vector<int> {1, 2, 3, 4, 5};
for(auto it = v.cbegin(); it != v.cend(); ) {
if(*it == 5)
v.erase(it);
++it;
}
}
This segfaults (doesn't if you compare to something that is not the last element of the vector, e.g. *it == 3, but this behaviour is probably compiler-dependent). Why? v.erase(it) invalidates the iterator it, so you can't be using it after that line. Instead, you should be using what erase returns, it = v.erase(it), which is the iterator following the deleted one. Now if that happens to be v.cend() and on the next line you increment it, ++it, you're out of bounds and out of luck.
I am not encouraging you to use the following, but if you want to keep with the style of your code, what should work is
if(*it == 5)
it = v.erase(it);
else
++it;
What the else does is it avoids the double incrementation on erase.

vector iterator incrementable when erasing element of vector in 2 for loops

I am currently programming a little game for the console with an 2D map. 2 Elements of my game are: destroying fields and an enemy, which spreads in a random direction (its getting bigger). These two "entities" are saved in a structure which contains two vectors (X and Y). I am now trying to erase an element of "_Enemy"(<-private instance of the structure in a class, same as "_DestroyedFields") if you destroy the field where the enemy is.
I tried a lot of different variations to do so and whats giving me the error least is this method (I already searched the internet for a while now an couldn't find a answer to my question):
for (std::vector<int>::iterator itEX = _Enemys.X.begin(), itEY = _Enemys.Y.begin();
itEX != _Enemys.X.end() && itEY != _Enemys.Y.end();
++itEX, ++itEY) {
for (std::vector<int>::iterator itX = _DestroyedFields.X.begin(),
itY = _DestroyedFields.Y.begin();
itX != _DestroyedFields.X.end() && itY != _DestroyedFields.Y.end();
++itX, ++itY) {
if (*itY == *itEY && *itX == *itEX){
itEY = _Enemys.Y.erase(itEY);
itEX = _Enemys.X.erase(itEX);
}
}
}
PS: sorry if my english isn't the best, im german ^^
PSS: if you wanna watch over my whole code, you can find it on Github: https://github.com/Aemmel/ConsoleGame1
After erasing using iterator it, you cannot use it further as it is invalidated. You should use a result of a call to erase which is new, valid iterator.
for( it = v.begin(); it != v.end();)
{
//...
if(...)
{
it = v.erase( it);
}
else
{
++it;
}
...
}
I fixed the bug with first: making a "simple structure"(struct Entity{int X; intY} and then std::vector [insert name here]) and then with adding an break; if the condition is true.
for (Uint itE = 0; itE < _Enemys.size(); ++itE){
for (Uint it = 0; it<_DestroyedFields.size(); ++it){
if (_Enemys.at(itE).Y == _DestroyedFields.at(it).Y
&& _Enemys.at(itE).X == _DestroyedFields.at(it).X){
_Enemys.erase(_Enemys.begin()+itE);
break;
}
}
}
With struct Position {int x; int y;}; and some utility operators,
you may do one of the following: (https://ideone.com/0aiih0)
void filter(std::vector<Position>& positions, const std::vector<Position>& destroyedFields)
{
for (std::vector<Position>::iterator it = positions.begin(); it != positions.end(); ) {
if (std::find(destroyedFields.begin(), destroyedFields.end(), *it) != destroyedFields.end()) {
it = positions.erase(it);
} else {
++it;
}
}
}
Or, if input are sorted, you may use a 'difference':
std::vector<Position> filter2(const std::vector<Position>& positions, const std::vector<Position>& destroyedFields)
{
std::vector<Position> res;
std::set_difference(positions.begin(), positions.end(),
destroyedFields.begin(), destroyedFields.end(),
std::back_inserter(res));
return res;
}

Better way to iterate std::map

Given a map, I need to retrieve and operate two immediately stored items.
To me, working on a vector is litter easier since I can do "iter + 1" or "iter - 1".
While for map, I am out of luck.
For example, I give a simple example as follows:
Note: in my real application, I don't simply subtract those numbers.
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
map<char,int>::iterator firstItem = mymap.begin();
map<char,int>::iterator secondItem = ++mymap.begin();
for ( ; secondItem != mymap.end(); ++firstItem, ++secondItem )
cout << secondItem->second - firstItem->second << endl;
return 0;
}
Question> Is there a better solution for this?
Thank you
Instead of incrementing both iterators in the loop control (incrementing is a bit slow), just assign firstItem = secondItem then increment secondItem.
You can do it with a single iterator. Move the increment from the header to the middle of your loop, and exit the loop when you hit the end of your map, like this:
map<char,int>::iterator item = mymap.begin();
for (;;) {
int first = item->second;
++item;
if ( item == mymap.end()) break;
cout << item->second - first << endl;
}
This is a matter of style. You can do eg.
auto first = m.begin();
if (first != m.end())
{
auto second = first;
second++;
for (; second != m.end(); first = second++)
{
...
}
}
You can also bailout more elegantly in the case where the map is empty. For instance you can do:
if (m.empty()) return;
auto first = m.begin(), second = first;
for (second++; second != m.end(); first = second++)
{
...
}
I'd favor the latter if I can, and use the former only if I must.
Your current loop will show undefined behaviour if the map is empty.
Your loop could be rewritten (more simply, and checking for an empty map) like so:
int main(int argc, char * argv[])
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
for ( it = ( mymap.begin() == mymap.end() ? mymap.end() : std::next(mymap.begin()) ) ; it != mymap.end(); ++it )
cout << it->second - std::prev(it)->second << endl;
return 0;
}
Your code will have undefined behavior if the map is empty but other than that it seems to be a reasonable approach, depending on your overall goal. Since map iterators are not random access you can't just add or subtract one, only increment/decrement.
An alternate approach is to make a copy of the iterator and then incrementing inside the loop.
Neither better, nor worse, just an alternative:
if (map.size() >=2)
std::accumulate(
++mymap.begin(),
mymap.end(),
mymap.begin(),
[](mymap_type::const_iterator iprev, mymap_type::value_type const& entry)->mymap_type::const_iterator
{
/* do something */;
return ++iprev;
});