1

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..

dobby
  • 13
  • 4

4 Answers4

2

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

Community
  • 1
  • 1
iwolf
  • 1,080
  • 1
  • 7
  • 10
1

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

pm100
  • 48,078
  • 23
  • 82
  • 145
1

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).

Johannes S.
  • 4,566
  • 26
  • 41
0
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;
        }
    }
}
sp2danny
  • 7,488
  • 3
  • 31
  • 53