How do you change a std::map element while iterating? - c++

I have a large array of object pointers (lets call it A), and a smaller map of object pointers (M) keyed with the index of A.
While iterating M, I want to swap the mapped pointer (second) with the pointer that is currently at that index (first) in A.
I have something like this:
map<LONG, Object*>::iterator mit;
for (mit = M.begin(); mit != M.end(); mit++)
{
if ((*mit).first != NO_ID)
{
Object* pTmp = pA->ReplaceObject((*mit).first, (*mit).second);
if (pTmp != NULL)
{
M.at((*mit).first) = pTmp;
}
}
}
Here ReplaceObject first gets A[(*mit).first] for return, then changes A[(*mit).first] to (*mit).second.
My mapped pointer is staying stubbornly unchanged - though the debugger, appears to show the change happens correctly.
What am I doing wrong?

A great 'wood-for-the-trees' moment - I was simply negelcting to copy my altered undo operation back to the undo stack. Once I stopped looking for the "error" and just read my code, it was obvious! D'oh!

Related

C++: struct pointer in map structure

I declared a struct like this, and the following data structures
struct piece{
int start, height, end;
piece(int a, int b, int c){
start = a; height = b; end = c;
}
};
vector<piece> piecesTog;
map <int,piece*> data;
Then, when I read the elements I do this:
while(scanf("%d %d %d", &aux1, &aux2, &aux3) != EOF){
piecesTog.push_back( piece(aux1, aux2, aux3) );
data[a] = data[c] = &piecesTog[tam];
}
Well, until now, I have had no problem.
However, later in the program, I have to use the piece* part, to do so, I use an iterator like this
for(map< int, piecesTog* >::iterator it = data.begin(); it != data.end(); it++){
piece* aux = it->second;
...
}
I want to have access to the structure that the it->second points, but I tried everything and nothing worked.
I printed the memory adress of it->second and &piecesTog[tam] and they are the same, but when I do (*aux).height or it->second->height they give number completely crazy, probably some trash.
I have no clue why that is happening.
If anyone has any idea how to fix it, I would appreciate it.
while(scanf("%d %d %d", &aux1, &aux2, &aux3) != EOF){
piecesTog.push_back( piece(aux1, aux2, aux3) );
data[a] = data[c] = &piecesTog[tam];
}
is almost certainly not following the Iterator invalidation rules.
piecesTog.push_back( piece(aux1, aux2, aux3) );
can trigger a resize which typically creates a new datastore, copies the elements from the old data store to the new one and then deletes the old datastore, leaving the pointers cached by
data[a] = data[c] = &piecesTog[tam];
dangling. When you use those pointers some time in the future, Ka-Blammo! Undefined Behaviour and an easily identified crash if you're lucky.
Insufficient information has been provided to supply a definitive solution, but here are a few general alternatives (in order of attractiveness):
If you know ahead of time the number of pieces that will go into piecesTog, you can reserve storage to eliminate the need to resize the vector.
If elements are only added to the end of the vector and no elements are ever removed, you can store the indexes of the elements rather than pointers to them. If the ordering never changes, the indexes will always refer to the correct elements no matter how many more items are added.
If it is possible to do so, rewrite the reader to load all of the pieces into piecesTog and then build the maps.
The above options all assume that piecesTog is assembled all at once and then left alone. If your insertion is more free-form, you sort the structure or you remove elements, you'll need to use a data structure with more favourable invalidation rules such as std::list.

Accessing pointers after erasing from Map

The sample scenario in my code implementation is as follows
I have a map defined as map<int,map<int,object*>*> . The inner map which is in heap has a object pointer.
The scenario is,
After using(processing) all the elements in the inner map. I will erase the inner map contents using iterator. But the object* will not be deleted. I will use the object pointer further after erasing the key from the map.
My question is will that object* exist even after erasing it's presence in the map. As far as my understanding , yes the object is in heap and it can be used even after the erase in map. But i am facing random crash in the process after few minutes of execution. This makes me to post the question here.
multimap<ULONG, Class*>::iterator it_top3 = InnerMap->begin();
if (InnerMap->size() >= classLimit)
{
if (it_top3->first <= ClassObj->m_classSize)
{
if (it_top3->second != NULL)
{
delete it_top3->second;
it_top3->second = NULL;
}
InnerMap->erase(it_top3);
InnerMap->insert(pair<ULONG, Class*>(ClassObj->m_classSize, ClassObj));
}
Secondly , On analyzing debug diag the line it_top3->second = NULL; points as the crash point with access violation exception. What would be possible reason for the crash here.?
You don't just erase from map, the line
delete it_top3->second;
it_top3->second = NULL;
deallocates the pointer, which may cause your crashes.
Just the InnerMap->erase() call would do what you'd expect.

using the function erase in std::vector

I Have this function, its purpose is to delete a pointer of class BaseFile
from a vector called children
//children is vector of type vector<BaseFile*>
void Directory::removeFile(BaseFile* file)
{
for(int i = 0 ; (unsigned)i < children.size() ; i++)
{
if ((children[i]->getName()).compare(file->getName()) == 0)
{
BaseFile* a = children[i];
children.erase(children.begin()+i);
if(a != nullptr)
{
delete a;//err in this line : double free or corruption
}
}
}
}
first question is why I AM getting an error in the line (delete a;) ?
dose the method erase removes the pointer an delete it?
if yes how can i remove the pointer from the vector without deleting it's content in Heap/Stack?
What you need to do is to use std::remove_if to obtain the vector without the matching element.
However, once you have performed the call to std::remove_if you have no way to delete the matching items as the documentation states (emphasis mine):
Removing is done by shifting (by means of move assignment) the elements in the range in such a way that the elements that are not to be removed appear in the beginning of the range. Relative order of the elements that remain is preserved and the physical size of the container is unchanged. Iterators pointing to an element between the new logical end and the physical end of the range are still dereferenceable, but the elements themselves have unspecified values (as per MoveAssignable post-condition).
Therefore we'll handle the deletion directly in the predicate. Note that we must also take care not to double free anything so we'll keep track of the deleted item through the use of an std::unordered_set
void Directory::removeFile(BaseFile *file) {
std::unordered_set<BaseFile*> deleted_set { file }; // Just to avoid deleting the input argument if it's stored in children as well...
auto match_it = std::remove_if(begin(children), end(children),
[&](BaseFile *current_file) -> bool {
bool result = current_file->getName().compare(file->getName()) == 0;
if (result && end(deleted_set) == deleted_set.find(current_file)) {
delete current_file;
deleted_set.insert(current_file);
}
return result;
});
children.erase(match_it, end(children));
}
Finally, I hope that the pointer you give as the file argument isn't a member of children as well and if it is, that you don't end up delete-ing it!
Note: Wouldn't it be possible to use smart pointers in your case? It seems that the Directory object has ownership on the BaseFile objects stored in children... So maybe std::unique_ptr would help...

Popping the second last

I'm a C# programmer having problems with messy pointers and I just can't find out what the mistake is.. I could use some help with the list
So basically I have something like a stack of cards and these cards are saved in a list. I just want to take the upper most and return it to the function. I could use pop_back() but the last card has to stay as it is because it is the cardback (I'm making it later with textures and stuff)
Card * CardStack::HandOut()
{
if (m_Stack.size() > 1)
{
list<Card *>::iterator it = m_Stack.end();
advance(it, -2);
Card *ret = *it;
Card tmp = *ret;
Card *tmpp = &tmp;
m_Stack.remove(ret);
return tmpp;
}
return NULL;
}
So I want to always pop the second last Card back.
I'm sure its a total beginner mistake :(
You are returning a pointer to a local variable,
Card tmp = *ret;
Card *tmpp = &tmp;
m_Stack.remove(ret);
return tmpp;
that doesn't exist anymore after the function exited. So when you use the pointer later, you invoke undefined behaviour.
You should not bother with tmp and tmpp, returning ret ought to do it, the remove doesn't destroy the card, it just removes (the pointer to) it from the stack.
You could just erase the item pointer by the iterator directly. This also ensures the removal is O(1) instead of O(n) using .remove(), and avoids removing extra items if the contents are not unique.
std::list<Card*>::iterator it = m_Stack.end();
std::advance(it, -2);
Card* res = *it;
m_Stack.erase(it);
return res;
Note that it is not idiomatic in C++ to store raw pointers. It is better to store the object by value (i.e. use list<Card>) if copying is cheap, or use a smart-pointer (e.g. list<shared_ptr<Card> >) so the memory can be collected automatically when it is no longer used.

Vector iterators incompatible: runtime error

I'm currently having trouble with a destructor of a class which contains a vector of objects. The application runs fine, however upon freeing the heap it throws an error.
Here is the code of my destructor:
~StaticNetwork(void) { // clear memory
for(vector<Node*>::iterator iter = nodes.begin(); iter != nodes.end(); )
nodes.erase(iter++);
}
And nodes are being added to the network as follows:
if((temp = is_already_added(regex_d[1])) >= 0) // check if the src node has already been added
{
if((temp1 = is_already_added(regex_d[2])) >= 0) // check if the next_hop has already been added
{
nodes[temp]->add_n_vchannels(regex_d[5]);
nodes[temp]->add_next_hop(nodes[temp1]);
}
else // the next_hop has not been added
{
Node *anext_hop = new Node(regex_d[2]);
nodes[temp]->add_next_hop(anext_hop);
nodes[temp]->add_n_vchannels(regex_d[5]);
nodes.push_back(anext_hop); // add next hop
param.n_of_nodes++;
}
}
The network is comprised of pointers to the actual nodes.
Any help/suggestion/reference/(constructive)criticism will be greatly appreciated.
Your iteration over the container is wrong. If the node is a member of the class, then ignore it as the destructor of the vector will take care of it. If it is not a member and you really want to remove all elements, the simplest thing is calling node.clear() (Note both are equivalent to your code, but they will leak the pointed memory if it should be managed by your class)
If the pointers are managed by your class, consider using smart pointers or specific pointer containers. Else the simplest loop to free all memory would be:
for ( std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it )
delete *it;
Note that I did not modify the container itself, just the contained elements.
You do not need to delete elements of vector manually, it will be done by vector itself. This is how destructors work: they call destructors of member objects of deleted object, so you don't have to worry about it.
erase doesn't work as you expect: it removes the elements from the container, i.e. the pointers and not the pointed object. So you are leaking memory here.
Moreover, erase invalidates the iterators following the erased element(s), thus the test iter != nodes.end(); causes the error, as you increment the pointer past it.
Anyway, you can write the code as shown by David Rodríguez - dribeas.