Map/iterator incremental error - c++

The following code throwing debug assertion map/iterator incremental error ..
void ClassA::Remove()
{
std::map<int, CVClassB*>::iterator it(m_p.begin());
while ( it != m_p.end() )
{
if (it->first >= 0)
{
m_p.erase(it);
it++;
}
}
}
Can you please let me know what is the error

std::map::erase invalidates the iterator on which it operates. So it is not safe to increment it afterwards. But erase() does return the next iterator for you:
it = m_p.erase(it);
Also, you only increment it inside the if, so unless all the keys are >=0, you will get stuck in an infinite loop. You probably wanted something like:
// delete all keys >= 0
if (it->first>=0) {
it = m_p.erase(it); // erase and increment
}
else {
++it; // just increment
}
Also, as Vlad's answer alludes to, who manages the lifetime of the CVClassB*? Do you need to delete it? Why use a pointer at all, you can probably store the value in the map directly. (Or use a smart pointer).

Write the loop like
while ( it != m_p.end() )
{
if (it->first >= 0)
{
it = m_p.erase(it);
}
else
{
++it;
}
}
Also it seems you should delete the object pointed to by the erased iterator.
For example
delete *it;
it = m_p.erase(it);

Your invalidating the iterator by removing inside the loop but in any case all that does is clear the map. Just call m_p.clear() and it will do exactly what you are trying to do. Although not sure what your trying to do is what you intended to do but that's another issue.
If you want to delete the objects pointed to then delete them then clear the map.
for(item : m_p)
delete item->second;
m_p.clear();
//done

Related

SIGBUS when trying to increment a std::map iterator

I am debugging a big C++98 application and I am obtaining a SIBGUS error when a method tries to increment a std::map::iterator.
By putiing traces, I have discovered that the method in question removes elements from the mentioned map (indireclty, by calling other methods that call other methods and so on...), so I suspect that the problem is to been iterating over the map while deleting its elements.
I have been searching the proper way to iterate over a std::map and delete items safelly, and I have found this:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
Code quoted from How to remove from a map while iterating it?
I have some questions about this:
Is it actually necessary to distinguis between deleting elements or not, taking into account that the iterator is going to be increased in any case?
Is the following code snippet equivalent to the above one, in terms of safety?
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it);
}
it++;
}
The method that is producing the SIGBUS follows the following pattern:
std::map<..., ...>::iterator it = myMap.begin(); // myMap is an instance attribute and can be accessed by any class method.
while(it != myMap.end() {
if(somethingHappens())
doSomethingThatMightDeleteMapElements(); // this can (or not) delete 'myMap' elements.
it++; // The error occurs here
}
Since the deletion is performed by other method/s, I cannot distinguis if an element has been deleted or not (unless I return a boolean value or similar). Is this potentially unsafe?
Is the following code snippet equivalent to the above one, in terms of safety?
No, of course not, you cannot increase it after you passed it to map.erase() as that iterator is invalidated by that call. Difference is:
map.erase(it++);
is logically equivalent to:
iterator tmp = it;
++it;
map.erase( tmp );
So in this case tmp is invalidated, but it is still valid.
Considering this code:
while(it != myMap.end() {
if(somethingHappens())
doSomethingThatMightDeleteMapElements(); // this can (or not) delete 'myMap' elements.
it++; // The error occurs here
}
I think only viable way would be this:
while(it != myMap.end() {
if(somethingHappens()) {
key_type key = it->first();
doSomethingThatMightDeleteMapElements(); // this can (or not) delete 'myMap' elements.
it = myMap.upper_bound( key );
} else
it++;
}
Since the deletion is performed by other method/s, I cannot distinguish if an element has been deleted or not (unless I return a boolean value or similar).
You certainly can. Keep the size of the map before the call to doSomethingThatMightDeleteMapElements. Get the the size of the map after the call to doSomethingThatMightDeleteMapElements. Then take appropriate action depending on whether they are equal or not.
while(it != myMap.end() {
size_t size_before = myMap.size();
size_t size_after = size_before;
if(somethingHappens())
{
doSomethingThatMightDeleteMapElements(); // this can (or not) delete myMap elements.
size_after = myMap.size();
}
if ( size_before != size_after )
{
// Be safe. Iterate from the start again.
it = myMap.begin();
}
else
{
it++; // The error occurs here
}
}

c++ boost map program crash after erase

I have a problem and dont know how to proper solve it or WHY the error appear.
To my problem:
I have 1 loop which execute a function every 2 seconds. That functions does a for() function and erase all entrys which remaining time is at 0. If remaining time is not 0 then it will decrease it by 2000 (2sec).
But after erasing an entry the program crashes...
boost map:
boost::unordered_map<unsigned int, sBUFF_INFO*> p_BuffInfo;
function which get executed from 2 seconds loop
void CSkill::DecreaseAllBuffRemTime()
{
for( itertype(p_BuffInfo) it = p_BuffInfo.begin(); it != p_BuffInfo.end(); it++ )
{
sBUFF_INFO* buff = it->second;
if(buff != NULL)
{
if(buff->dwTimeRemaining <= 0)
{
this->DelPcBuffInfo(buff->tblidx)
}else{
buff->dwTimeRemaining -= 2000;
}
}
}
}
DelPcBuffInfo function:
void CSkill::DelPcBuffInfo(unsigned int tblidx)
{
p_BuffInfo.erase(tblidx);
}
Now after DelPcBuffInfo gets executed the program crash.
At this line it crash:
sBUFF_INFO* buff = it->second;
At debug:
Unhandled exception at 0x00578e0f in GameServer.exe: 0xC0000005:
Access violation reading location 0xddddddd9.
it + node_ = hash_ CXX0030; Error: expression cannot be evaluated
I dont really understand why this error appear..
edit:
If I add a "return" after this->DelPcBuffInfo(buff->tblidx) then the program dont crash..
Adding or removing items from a container will often invalidate your iterators. Check the documentation for unordered_map iterators or here: Iterator invalidation in boost::unordered_map
the correct idiom is
for( itertype(p_BuffInfo) it = p_BuffInfo.begin(); it != p_BuffInfo.end(); )
{
sBUFF_INFO* buff = it->second;
if(buff != NULL)
{
if(buff->dwTimeRemaining <= 0)
{
it = this->DelPcBuffInfo(buff->tblidx)
}else{
buff->dwTimeRemaining -= 2000;
it++;
}
}
}
ie dont increment in the loop. Instead increment if you dont delete otherwise have the delete operation return the new iterator. Thats why remove returns an iterator pointing at the next element
This is courtesy of the awesome Scott Myers
To add to the existing answers pointing out the erase-iterator idiom: The reason for you crash is that the iterator it is invalidated due to the removal of the element. Thus, the increment on the (invalid) operator causes undefined behaviour and it will point to some arbitrary memory block. Dereferencing the "iterator" then crashes your program.
To avoid this problem, apply the idiom as demonstrated in the other answers, that is
* Use the iterator version of erase. It returns an iterator to the next element ( which may be end())
* Use the return value of this erase as new value of it. Since it already points to the next element, do not increment again (otherwise you may skip an element in your map or cause undefined behaviour if it already points to the end of the map.
* Only increment the iterator yourself, when you did not erase an element.
Note: If your intention is to get rid of the sBUFF_INFO element completely upon removal from the map, your programm shows a memory leak. Erasing the pointer from the map does not delete the pointed-to memory. You need to delete the pointee yourself (or use an appropriate smart pointer).
void CSkill::DecreaseAllBuffRemTime()
{
auto it = p_BuffInfo.begin();
while( it != p_BuffInfo.end() )
{
sBUFF_INFO* buff = it->second;
if(buff)
{
if(buff->dwTimeRemaining <= 0)
{
// probably delete buff too
it = p_BuffInfo.erase(it);
} else {
buff->dwTimeRemaining -= 2000;
++it;
}
} else {
++it;
}
}
}

Vector iterator not incremental .erase()

I am trying to delete any element of this vector that collides with player. However when I try to remove the element from the vector the program crashes and I get the error; "vector iterator not incremental".
for (std::vector<Coin>::iterator i=CoinSet.begin(); i!=CoinSet.end(); i++)
{
if (i->PlayerClear(player.collider()) == true)
{
score++;
cout<<score<<endl;
CoinSet.erase(i);
}
}
This code works perfectly well until "CoinSet.erase(i)", I tried using "CoinSet.clear()" at various points, but to no avail. Any help on this would be great, thanks in advance!
This has been discussed to death. You mustn't operate on an invalid iterator. You want something like this:
for (auto it = CoinSet.begin(); it != CoinSet.end(); /* no increment here! */ )
{
if (/* ... */)
{
// ...
CoinSet.erase(it++);
}
else
{
++it;
}
}
I don't like putting ++-statements inside the argument. Therefore erase() returns an iterator that points to the next element, so one could replace the erase line with:
it = CoinSet.erase(it); // iterator is replaced with valid one

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());
}

Remove only one element from multimap with duplicate keys

I have a multimap with Note objects from which I want to remove only one object. There can be several Note objects with the same key. The problem is that right now there's also objects being removed that aren't within the key range I specify:
long key = note.measureNumber * 1000000 + note.startTime; // = 2000001
multimap<long, Note>::iterator it;
for (it = noteList.lower_bound(key); it != noteList.end() && it->first < (key + 1); it++) {
if(it->second.frequency == note.frequency){
noteList.erase(it);
}
}
When I run this code with key 2000001 the object I'm able to erase the right object, but another object with key 1000017 gets erased as well. Both objects have the same frequency though.
Any idea's what is wrong with my for loop?
EDIT: To be clear, I only want to check for objects with one specific key (in this case 2000001) there's no need for the iterator to look at objects with different keys than that one.
Calling erase() with the iterator will invalidate it, so you can't then continue to use it.
See Can I continue to use an iterator after an item has been deleted from std::multimap<>
Once you erase an iterator it becomes invalid. If you wish to erase from a map while you iterate through it, your code needs to change. Try this:
multimap<long, Note>::iterator it;
for (it = noteList.lower_bound(key); it != noteList.end() && it->first < (key + 1);) {
if(it->second.frequency == note.frequency){
noteList.erase(it++);
}
else
{
++it;
}
}
As already indicated here, erasing an iterator invalidates it.
I'd like to point out some inefficiency in code you have:
You don't need to iterate till the end of the loop. Consider this:
for (it = noteList.lower_bound(key); it != noteList.upper_bound(key) && it->first == key; it++)
{
if(it->second.frequency == note.frequency)
{
noteList.erase(it++);
}
else
{
++it;
}
}