I trying to to delete an object both from a vector of objects and from memory using its destructor. I understood the deleting the the object that the iterator points to, is making the the iterator to point to the element that follows the last element removed. Therefore, I tried to implement this:
std::vector<Customer*>::iterator j=customersList.begin();
while (j!=customersList.end()){
customersList.erase(j);
delete *j;
}
is it o.k. or that it jumps 2 places by applying both erase and delete?
The loop is not correct, since
you are invalidating the j iterator, and subsequent to that, issuing a delete call on the dereferenced, invalid iterator.
The j iterator is not incremented at all in that loop.
The easiest way to issue a delete and erase is a simple std::for_each, followed by a vector::clear().
#include <algorithm>
//...
std::for_each(std::begin(customersList), std::end(customersList), [](Customer *c){delete c;});
customersList.clear();
or even simply:
for (Customer* c : customersList )
delete c;
customersList.clear();
Related
I wanted to ask you about the vector::shrink_to_fit() function.
Lets say i've got a vector of pointers to objects (or unique_ptr in my case)
and i want to resize it to the amount of objects that it stores.
At some point i remove some of the objects from the vector by choice using the release() function of unique_ptr
so there is a null pointer in that specific place in the vector as far as i know.
So i want to resize it and remove that null pointer in between the elements of the vector and i'm asking if i could do that with shrink_to_fit() function?
No, shrink_to_fit does not change the contents or size of the vector. All it might do is release some of its internal memory back to a lower level library or the OS, etc. behind the scenes. It may invalidate iterators, pointers, and references, but the only other change you might see would be a reduction of capacity(). It's also valid for shrink_to_fit to do absolutely nothing at all.
It sounds like you want the "Erase-remove" idiom:
vec.erase(std::remove(vec.begin(), vec.end(), nullptr), vec.end());
The std::remove shifts all the elements which don't compare equal to nullptr left, filling the "gaps". But it doesn't change the vector's size; instead it returns an iterator to the position in the vector just after the sequence of shifted elements; the rest of the elements still exist but have been moved from. Then the erase member function gets rid of those unnecessary end elements, reducing the vector's size.
Or as #chris notes, C++20 adds an erase overload to std::vector and a related erase_if, which makes things easier. They may already be supported in MSVC 2019. Using the new erase could just look like:
vec.erase(nullptr);
This quick test show that u can't do like this.
int x = 1;
vector<int*> a;
cout << a.capacity() << endl;
for (int i = 0; i < 10; ++i) {
a.push_back(&x);
}
cout << a.capacity() << endl;
a[9] = nullptr;
a.shrink_to_fit();
cout << a.capacity() << endl;
Result:
0
16
10
m_gates[index].release(); m_gates.shrink_to_fit();
Based on your comment, what you're looking for is simply to erase this single element from your vector right then and there. Replace both of these statements with:
m_gates.erase(m_gates.begin() + index);
Or a more generic version if swapping containers in the future is a possibility:
using std::begin;
m_gates.erase(std::next(begin(m_gates), index));
erase supports iterators rather than indices, so there's a conversion in there. This will remove the pointer from the vector while calling its destructor, which causes unique_ptr to properly clean up its memory.
Now erasing elements one by one could potentially be a performance concern. If it does end up being a concern, you can do what you were getting at in the question and null them out, then remove them all in one go later on:
m_gates[index].reset();
// At some point in the program's future:
std::erase(m_gates, nullptr);
What you have right now is highly likely to be a memory leak. release releases ownership of the managed memory, meaning you're now responsible for cleaning it up, which isn't what you were looking for. Both erase and reset (or equivalently, = {} or = nullptr) will actually call the destructor of unique_ptr while it still has ownership and properly clean up the memory. shrink_to_fit is for vector capacity, not size, and is unrelated.
at the end the solution that i found was simple:
void Controller::delete_allocated_memory(int index)
{
m_vec.erase(m_vec.begin() + index);
m_vec.shrink_to_fit();
}
it works fine even if the vector is made of unique_ptrs, as far as i know it doesn't even create the null pointer that i was talking about and it shifts left all existing objects in the vector.
what do you think?
I am fairly new to c++, but had a question about vectors. My goal is to remove an element from the vector using erase once I hit my out of bounds condition. This all seems to work fine, except that when I call erase, it will be pointing to the first element, but delete the last. Basically, the loop will continue to iterate and delete every element out of my vector. I am using push_back to add Lasers to my vector elsewhere in the code.
std::vector<Laser> m_Lasers;
for (int i = 0; i != m_Lasers.size(); i++)
{
m_Lasers[i].ClearLaser();
if (m_Lasers[i].GetX() > m_ScreenWidth || m_Lasers[i].GetX() < 0 || m_Lasers[i].GetY() < 0)
{
//erase the vector
m_Lasers.erase(m_Lasers.begin() + i);
i--;
}
}
my =operator is defined as:
void operator=(const Laser& L){};
in my laser class. I think my issue may be with this.
Thank you so much for you help!
What vector::erase does is moving all elements after the erased element forward using assignment, and then destroying the last element. It has to do this to maintain the invariant that the elements in the vector are stored contiguously.
For example, give a vector v of size 4, erasing v[1] essentially does v[1] = v[2]; v[2] = v[3]; v.pop_back(); (These are actually move assignments; std::move is omitted for clarity.)
If your assignment is a no-op (which is not allowed by erase's requirements, by the way), then this will just end up destroying the last element.
I have an std::vector instance as defined in the following line:
std::vector< std::pair<EndPointAddr*, EndPointAddr*>* > mServiceSubscriptionsList;
The first item in the underlying std::pair object is the network address of the subscriber entity while the second is the network address of the subscribed entity. So, an std::pair object represents a subscription here as pair of subscriber and subscribed endpoint addresses.
I would like to delete all subscriptions in this vector for a given subscriber endpoint address. For this purpose, I wrote below indicated function where I use std::remove_if with a predicate. Based on the documentation of std::remove_if, my understanding is that std::remove_if puts all occurrences to be deleted to the end of the vector and moves the end of the vector backward to its new position.
My questions is:
How can I reach to these std::pair items that are put into the end of the vector after the call to remove_if in order to deallocate their contents dynamically one by one (i.e. dellocating std::pair* pointers)? Could you indicate the needed code snippet in the below function code? I can delete the first occurence kept in the iterator last. However, I am not sure how can I delete the rest of the occurences. Thanks.
bool
XXX::removeSubscriptionForASpecificSubscriber(EndPointAddr * ptrSubscriberAddr)
{
auto last =
std::remove_if(mServiceSubscriptionsList.begin(),
mServiceSubscriptionsList.end(),
[ptrSubscriberAddr](std::pair<EndPointAddr*, EndPointAddr*>* thePair)
{
return ptrSubscriberAddr->getXXXAddress().compareTo(thePair->first->getXXXAddress());
});
if(last != mServiceSubscriptionsList.end())
{
//HERE I CAN DELET THE FIRST OCCURENCE, but WHAT I WANT IS TO DELETE ALL OCCURANCES
if(*last != nullptr)
{
delete *last;
}
mServiceSubscriptionsList.erase(last, mServiceSubscriptionsList.end());
return true;
}
return false;
}
There is no guarantee that remove_if puts erased elements at the end of the vector: iterators in range [newEnd, oldEnd) are dereferencable, but the elements have unspecified value.
For instance, the following code
std::vector<int> v { 0, 1, 2, 3, 4 };
auto new_end = std::remove_if(v.begin(), v.end(), is_odd);
can modify v such that it contains
0, 2, 4, 3, 4
^
newEnd
You should probably use std::partition instead, or store smart pointers so that you can use the erase-remove idiom (or even do not store pointers at all).
What is that delete supposed to do? at last..end contains 'obsolete' element trash for which the content was copied to the vector before it. Certainly you could call a for_each on the range with delete in ht lambda, but I doubt that would get sensible result.
If you want remove entries and also delete their content you need completely different approach. Like making the raw pointer unique_ptr instead.
If I understood correctly the documentation ("Removing is done by shifting the elements in the range in such a way that elements to be erased are overwritten"), the elements you need to delete are overwritten, so you cannot delete its dynamic content because you lose the pointers to the elements to be erased.
You should find first the indices in your vector of the elements to remove, deallocate them, and do the removal later. I suggest a solution similar to this: 1) use std::find_if to find the first element to remove, 2) deallocate the content and swap pointer with the "last" element of your vector, 3) repeat until std::find_if returns nothing. Here, "last" means the last element that has not been still flagged to be removed.
I'll offer 2 alternatives instead of showing how to delete your elements properly...
Best Solution: Don't dynamically allocate the pair:
std::vector<std::pair<EndPointAddr*, EndPointAddr*>>
Pretty simple. A pair which contains 2 pointers is tiny. It's going to be faster, and easier, to not dynamically allocate that pair. You don't need to worry about deletion either.
Acceptable Solution: Use unique_ptr:
If you know why you are dynamically allocating, and know that you have to in this case, use a smart pointer (unique_ptr). unique_ptr will clean itself up, so you don't need to delete anything.
std::vector<std::unique_ptr<std::pair<EndPointAddr*, EndPointAddr*>>>
First, write erase_remove_if:
template<typename Container, typename Lambda>
Container&& erase_remove_if( Container&& c, Lambda&& closure ) {
using std::begin; using std::end;
auto new_end = std::remove_if( begin(c), end(c), std::forward<Lambda>(closure) );
c.erase(new_end, end(c));
return std::forward<Container>(c);
}
second, erase the data in your remove_if predicate:
bool removeSubscriptionForASpecificSubscriber(EndPointAddr * ptrSubscriberAddr)
{
erase_remove_if( mServiceSubscriptionsList,
[ptrSubscriberAddr](std::pair<EndPointAddr*, EndPointAddr*>* thePair)
{
if (ptrSubscriberAddr->getXXXAddress().compareTo(thePair->first->getXXXAddress()))
{
delete ptrSubscriberAddr;
return true;
} else {
return false;
}
});
return true;
}
if you don't want to use std::unique_ptr to store your pairs-of-pointers. Note that if you have a std::vector which denotes ownership of the pointers within, it really is a nearly painless drop-in fix to make it a vector<unique_ptr<>>. You do have to delete some code that manages the memory, replace some push_back with emplace_back, and add some .get() calls, and that it.
So I have a vector of unsigned ints (vector<unsigned int> is called vector1). I have another vector of a struct I created (vector<struct> is called vector2). vector<int> holds an integer that is the index of the vector<struct>. For example, let's say that vector<int = {5, 17, 18, 19}. That means vector2.at(5) == vector2.at(vector1.at(0)).
In the struct, I have a bool variable called var. In most cases, var is false. I want to delete all of the elements in vector1 that have var = true.
What I did was:
for (unsigned int i = 0; i < vector1.size(); i++)
{
if (vector2.at(vector1.at(i)).var)
vector1.erase(vector.begin() + i);
}
The only problem with this is that it does not delete all of the true elements. I have run the for loop multiple times for all values to be delete. Is this the correct behavior? If it is not, where did I go wrong?
You have to use the erase-remove idiom to delete elements from a vector.
v.erase(std::remove(v.begin(), v.end(), value), v.begin);
std::remove moves the elements to the end of the vector and erase will erase the element from the vector.
You can keep a temporary vector, copy of vector1 and iterate over it in the for loop and delete from vector1.
You are erasing elements in the vector while at the same time iterating over it. So when erasing an element, you always jump over the next element, since you increase i while having just shortened the vector at i (it would be even worse, had you used a proper iterator loop instead of an index loop). The best way to do this would be to seperate both opretions, first "marking" (or rather reordering) the elements for removal and then erasing them from the vector.
This is in practice best done using the erase-remove idiom (vector.erarse(std::remove(...), vector.end())), which first uses std::remove(_if) to reorganize the data with the non-removed elements at the beginning and returns the new end of the range, which can then be used to really delete those removed elements from the range (effectively just shortening the whole vector), using std::vector::erase. Using a C++11 lambda, the removal condition can be expressed quite easily:
vector1.erase(std::remove_if( //erase range starting here
vector1.begin(), vector1.end(), //iterate over whole vector
[&vector2](unsigned int i) //call this for each element
{ return vector2.at(i).var; }), //return true to remove
vector1.end()); //erase up to old end
EDIT: And by the way, as always be sure if you really need std::vector::at instead of just [] and keep in mind the implications of both (in particular the overhead of the former and "maybe insecurity" of the latter).
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.