1

I have a global variable as a map and a function that iterates over the elements of this map such as:

void printMap(){
    for ( auto it = MyMap.begin(); it != MyMap.end(); ++it  ){
        std::cout << it->second;
    }
}

which works fine.

I want to add a functionality to the function which is after printing an element, it should be erased from the map like this:

void printMap(){
    for ( auto it = MyMap.begin(); it != MyMap.end(); ++it  ){
        std::cout << it->second;
        MyMap.erase(it);
    }
}

However, by adding the erase line I got an exception error of this type:

Thread 2: EXC_BAD_ACCESS (code=1, address=0x20000002)

I tried another way which is like this:

void myFunction(){
    printMap(); 
    MyMap.clear();
}

but I also got the same exception

Thread 2: EXC_BAD_ACCESS (code=1, address=0x0)

As I understand this kind of exception occurs when we refer to a memory location that does not exist. But I know it is there since the iterator got its value and it was printed. Even so I used the second method just in case that I don't refer to non-existing memory location but still I'm getting the exception.

So how can I iterate over the elements print the result then erase it?

UPDATE1
following the suggestions below and the linked topic I changed my function into this:

void printMap(){
    bool i = true;
    for (auto it = MyMap.cbegin(), next_it = MyMap.cbegin(); it != MyMap.cend(); it = next_it)
    {
        cout << it->second;
        next_it = it; ++next_it;
        if (i) {
            MyMap.erase(it);
        }

    }
}

I have also tried this https://stackoverflow.com/a/42820005/7631183 and this https://stackoverflow.com/a/42819986/7631183

The problem is still not solved and I'm getting the same error




UPDATE2
I run the same exact code on a different machine and it worked fine. I still don't know what is the reason so I would guess as suggested in the comments that std::map on the first had some problems.

P.S. The first machine was a mac and the second was a Linux

Community
  • 1
  • 1
Wanderer
  • 1,065
  • 5
  • 18
  • 40
  • 2
    Possible duplicate of [Erasing elements from a vector](http://stackoverflow.com/questions/347441/erasing-elements-from-a-vector) – Rakete1111 Mar 15 '17 at 19:57
  • I think the problem is a bit different, I'm getting an exception for referring to an existing memory location as if it does not exist – Wanderer Mar 15 '17 at 19:59
  • 3
    Have a look to [http://stackoverflow.com/questions/8234779/how-to-remove-from-a-map-while-iterating-it](http://stackoverflow.com/questions/8234779/how-to-remove-from-a-map-while-iterating-it) – em2er Mar 15 '17 at 20:00
  • It does not exist, because you just freed it, and thus you are not allowed to access it. @em2er's duplicate is a bit better than mine though :) – Rakete1111 Mar 15 '17 at 20:02
  • @Rakete1111 How come.. does the iterator life end after printing? and how about the second case? I'm simply printing whatever is inside it then I clear without any iterators. + I'm using the new version so there should not be any problem. – Wanderer Mar 15 '17 at 20:06
  • @user7631183 Deleting an iterator without using the idiom stated in the answers in a loop through the vector owning that iterator results in undefined behavior, because the iterators are invalidated. Basically, `printMap` has UB. See the answers linked please. And yes the iterator's life ends, but the element still exists (as long as you didn't remove it). – Rakete1111 Mar 15 '17 at 20:12
  • @Rakete1111 I did follow the same approach but still the same problem :( – Wanderer Mar 15 '17 at 20:24
  • 1
    What types are contained in your `map`? Are you sure one of their destructors isn't what's causing the error? – Miles Budnek Mar 15 '17 at 20:37
  • @MilesBudnek Here is the definition of it `std::map MyMap;` I'm using the one from std without changing anything in their destructors so I don't think there is a problem in here – Wanderer Mar 15 '17 at 20:48

2 Answers2

3

When you erase an element out of the map, you have invalidated the iterator you are using in the loop, thus causing error. The erase method returns an iterator for this reason.

for( auto it = MyMap.begin(); it != MyMap.end(); )
{
    std::cout << it->second;
    it = MyMap.erase(it);
}

This is indeed a duplicate question. You just don't understand it well enough to see that it is a duplicate. I hope the explanation of invalidating the iterator helps clear it up.

Christopher Pisz
  • 3,757
  • 4
  • 29
  • 65
  • When I tried this I got this error: thread 1 signal sigabrt. I complied it then run again I got the old error. – Wanderer Mar 15 '17 at 20:29
  • 1
    thread 1 signal abort would most likely mean an unhandled exception has occurred in a thread and may be completely unrelated. This is why it is important to narrow down what you are asking to the simplest test case possible and post that code with the question. If you create a new console application, create an std::map, fill it up with elements, and then use the given loop to erase, and have no other code in the application, you will see how it erases without error. As to the other errors, we have no idea, because we cannot possibly imagine what is in code that is not posted or debug it. – Christopher Pisz Mar 15 '17 at 20:38
  • for debugging unhandled exceptions, you can try enabling the break on exception in the exceptions dialog in Visual Studio, if that happens to be the IDE you are using. See https://msdn.microsoft.com/en-us/library/x85tt0dd.aspx – Christopher Pisz Mar 15 '17 at 20:41
0

In C++11 and later, std:map::erase() returns an iterator to the element following the one being erased. Since you are using auto, you are using C++11 or later, so you can use the new iterator when erasing while looping through the std::map, eg:

void printMap(){
    auto it = MyMap.cbegin();
    while (it != MyMap.cend()) {
        std::cout << it->second;
        it = MyMap.erase(it);
    }
}

Also, since you are using C++11, your first print function can be simplified using a range-for loop:

void printMap(){
    for ( auto &it: MyMap ){
        std::cout << it.second;
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I used this code as exact and I'm still getting the same error – Wanderer Mar 15 '17 at 20:32
  • Then, my guess is you are probably iterating a corrupted `std::map` to begin with. Have you tried any other of the other idioms described on [How to remove from a map while iterating it?](http://stackoverflow.com/questions/8234779/)? Do they exhibit the same errors? – Remy Lebeau Mar 15 '17 at 20:36
  • Then your `std::map` is most likely corrupted, because these are proven idioms that are known to work under normal conditions. – Remy Lebeau Mar 15 '17 at 21:41
  • it seems to be the case. Is there any alternative for the map that are efficient performance and memory wise? – Wanderer Mar 16 '17 at 06:33
  • @user7631183 I think you missed my point. You are clearly operating on an invalid `std::map`, you need to figure out why and fix it, not try to work around it. – Remy Lebeau Mar 16 '17 at 17:55