Vector iterators incompatible: runtime error - c++

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.

Related

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

How do i remove a pointer to an object in c++ from a vector dynamically?

Okay, so i'm doing this game like a project where i have 3 different objects.
void Character::shoot(){
Shot* s = new Shot(blablabla);
shots.push_back(s);
}
This happens dynamically, and currently i dont delete the pointers so it has to be loads of pointers to shots in that vector after a little while.
I use Bot, Character and Shot, and as i said i need help with storing and removing a pointer to the shot dynamically, preferably from a vector. I've got it to work like i put all the shot objects in a vector, but they never disappear from there. And i want to delete them permanently from my program when they collide with something, or reaches outside my screen width.
You can use std::remove and std::erase on any std container to remove content:
Shot* to_be_removed = ...;
std::vector<Shot*>::iterator i = std::remove(shots.begin(),shots.end(),to_be_removed);
std::erase(i,shots.end());
delete (to_be_removed);//don't forget to delete the pointer
This works when you know the element you want to remove. If you don't know the element you must find a way to identify the elements you want removed. Also if you have a system to identify the element it could be easier to use the container iterator in order to do the removal:
std::vector<Shot*>::iterator i = ...;//iterator the the element you want to remove
delete (*i);//delete memory
shots.erase(i);//remove it from vector
Lastly if you want to remove all pointers from the container and delete all the items at the same time you can use std::for_each
//c++ 03 standard:
void functor(Shot* s)
{
delete(s);
}
std::for_each(shots.begin(),shots.end(),functor);
shots.clear();//empty the list
//or c++11 standard:
std::for_each(shots.begin(),shots.end(),[] (Shot * s){ delete(s); } );
//the rest is the same
Using simple vector methods
You can iterate trough std::vector and use erase() method to remove current shot:
std::vector<cls*> shots; // Your vector of shots
std::vector<cls*>::iterator current_shot = shots.begin(); // Your shot to be deleted
while( current_shot < shots.end()){
if((*current_shot)->needs_to_be_deleted()){
// Remove item from vector
delete *current_shot;
current_shot = shots.erase(current_shot);
} else {
(*current_shot)->draw();
++current_shot; // Iterate trough vector
}
}
erase() returns iterator to next element after removed element so while loop is used.
Setting values to null and calling std::remove()
Note that std::vector::erase() reorganize everything after delete items:
Because vectors use an array as their underlying storage, erasing
elements in positions other than the vector end causes the container
to relocate all the elements after the segment erased to their new
positions. This is generally an inefficient operation compared to the
one performed for the same operation by other kinds of sequence
containers (such as list or forward_list).
This way you may end up with O(n^2) complexity so you may rather set values to null and use std::remove() as suggested by juanchopanza in comment, Erase-Remove idiom:
int deleted = 0;
for( current_shot = shots.begin(); current_shot < shots.end(); ++current_shot){
if((*current_shot)->needs_to_be_deleted()){
// Remove item from vector
delete *current_shot;
*current_shot = null;
++deleted;
}
}
if( deleted){
shots.erase( std::remove(shots.begin(), shots.end(), null));
}
Using std::list
If you need large amount of shot creations and deletions std::vector may not be the best structure for containing this kind of list (especially when you want to remove items from the middle):
Internally, vectors use a dynamically allocated array to store their
elements. This array may need to be reallocated in order to grow in
size when new elements are inserted, which implies allocating a new
array and moving all elements to it. This is a relatively expensive
task in terms of processing time, and thus, vectors do not reallocate
each time an element is added to the container.
See link from Raxvan's comment for performance comparison.
You may want to use std::list instead:
List containers are implemented as doubly-linked lists; Doubly linked
lists can store each of the elements they contain in different and
unrelated storage locations. The ordering is kept by the association
to each element of a link to the element preceding it and a link to
the element following it.
Compared to other base standard sequence containers (array, vector and
deque), lists perform generally better in inserting, extracting and
moving elements in any position within the container for which an
iterator has already been obtained, and therefore also in algorithms
that make intensive use of these, like sorting algorithms.
The main drawback of lists and forward_lists compared to these other
sequence containers is that they lack direct access to the elements by
their position;
Which is great for removing items from the middle of the "array".
As for removing, use delete and std::list::erase():
std::list<cls*> shots;
std::list<cls*>::iterator current_shot;
// You have to use iterators, not direct access
for( current_shot = shots.begin(); current_shot != shots.end(); current_shot++){
if( (*current_shots)->needs_to_be_deleted()){
delete *current_shot;
shots.erase(current_shot); // Remove element from list
}
}
Using std::shared_ptr
If you have more complex program structure and you use the same object on many places and you can simply determine whether you can delete object already or you need to keep it alive for a little bit more (and if you are using C++11), you can use std::shared_ptr (or use different kind of "smart pointers" which delete data where reference count reaches zero):
// Pushing items will have slightly more complicated syntax
std::list< std::shared_ptr<cls>> shots;
shots.push_back( std::shared_ptr( new cls()));
std::list< std::shared_ptr<cls>>::iterator current_shot;
// But you may skip using delete
shots.erase(current_shot); // shared_ptr will take care of freeing the memory
Simply use "delete" to deallocate the memory:
vector<Shot*>::iterator i;
for(i = shoot.begin(); i != shoot.end(); ++i)
{
delete (*i);//delete data that was pointed
*i = 0;
}
shoot.clear();
This will delete all elements from the heap and clear your vector.

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

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!

Why is this vector iterator not incrementable?

I'm trying to delete the vector's content and I'm getting an error - vector iterator is not incrementable, why is that?
This is my destructor:
City::~City()
{
vector <Base*>::iterator deleteIterator;
for (deleteIterator = m_basesVector.begin() ; deleteIterator != m_basesVector.end() ; deleteIterator++)
m_basesVector.erase(deleteIterator);
}
thanks.
erase invalidates the iterator. You can't use it any more. Luckily for you, it returns an iterator that you can use:
vector <Base*>::iterator deleteIterator = m_basesVector.begin();
while (deleteIterator != m_basesVector.end()) {
deleteIterator = m_basesVector.erase(deleteIterator);
}
Or:
m_basesVector.clear();
Are you responsible for freeing the memory referred to by the pointers in the vector? If that's the reason that you're iterating (and your real program has more code that you haven't shown, that frees those objects in the loop), then bear in mind that erasing from the beginning of a vector is a slow operation, because at each step, all the elements of the vector have to be shifted down one place. Better would be to loop over the vector freeing everything (then clear() the vector, although as Mike says that's not necessary if the vector is a member of an object that's being destroyed).
The problem is that you are trying to use an iterator while using the erase() function. erase(), push_back(), insert(), and other modifying functions invalidate iterators in STL.
Just use the clear() function:
City::~City()
{
m_basesVector.clear();
}
If you are trying to free the data in the vector, do this:
for (std::vector<Base*>::iterator it = v.begin(), e = b.end(); it != e; ++it)
delete *it;
Posting this just incase anyone else has this this problem and attempts this solution wondering why it's not working here's an actual solution/explanation.
#Steve Jessop - Your code is flawed and you've also got it written here... ( I've also edited his post to fix the issue as soon as it's approved it'll be fixed in the original post )
http://techsoftcomputing.com/faq/3779252.html
I don't see how this is a "Solution" to the issue when it create an new issue by making an endless loop there should be a deleteIterator++ within the while loop so that it actually reaches the end of the vector.
Also I've ran into this problem and my solution was inside the while loop checking whether the iterator was equal to the end or if the vector size was 0 and breaking before attempting to incrementing the iterator.
Ex.
std::vector<RankPlayer*>::iterator Rank_IT = CurrentPlayers.begin();
while ( Rank_IT != CurrentPlayers.end() )
{
RankPlayer* SelPlayer = (*Rank_IT);
if( strstr( SelPlayer->GamerTag, this->GamerTag ) != NULL )
{
delete[] SelPlayer->PlayerData;
delete[] SelPlayer;
Rank_IT = CurrentPlayers.erase( Rank_IT );
}
if( Rank_IT == CurrentPlayers.end() || CurrentPlayers.size() == 0 )
{
break;
}
++Rank_IT;
}
This is not relevant to the original problem posted above, but Google search on the error takes me to this page so I am posting it here for anyone to see.
I ran into this error message recently and all lines of codes checked out (there was no 'erase' or anything alike; the vector was merely read).
Eventually, I realized that there is a problem with nested loops.
For example, consider something like this:
`for (it=begin(); it!=end();i++)
{
for (; it!=end();i++)
{
}
}`
When you are done with the nested loop, it will increment the iterator - and then, the parent loop will increment it again(!), ultimately making the iterator step over the end(). I.e. it would be "end()+1" if there were such a thing.
Consequently, the parent loop throws this error at the next check.
To get around this, I ended up insert this line after the child loop:
`if (it == vStringList.end()) --it;`
Dirty, but works :D
I know it may be obvious to some, but I've been scratching my head over this for a while, lol
Any iterator pointing to the deleted element or to the elements after the one that is deleted gets invalidated when the vector's erase method is called. Erase method returns a valid iterator pointing to the next element in the vector. You should use that iterator to continue your looping & not increment the invalidated iterator. You may also use the clear method to remove all the elements in the vector. However, you will need to remember to explicitly de-allocate any allocated memory for the elements.
This code leaks all the contents of the vector - you have to delete *deleteIterator in the loop too. You can avoid all of this by using Base instead of Base* as the vector contents, then clear() will destruct them for you. Or use boost::ptr_vector which automates destruction if you do need raw pointers.
Calling erase() in a forward iteration like this can be very costly if the vector is large, as every element above the current position has to be moved down to ensure elements remain contiguous. Avoid manual erase of the type you propose, for this and other reasons.
Vector iterators are incrementable, but if you delete elements, the vector contents are modified and thus the iterator is invalid.
So, if you delete objects, you should use the return value of erase() that gives you the next valid iterator.