Safety deleting elements of the container - c++

Could you suggest safety deleting of element of std::vector in to cases:
1. Clear all elements of the vector;
2. Erase one element depending on condition.
What are the dangers of this code:
typename std::vector<T*>::iterator it;
for (it=std::vector<T*>::begin();it!=std::vector<T*>::end();it++)
{
if (*it) delete *it;
}
Thank's.

You don't remove the element from the vector. So the vector element is pointing to the location as before, i.e. the same T. However, since you have deleted that T you can not dereference the pointer anymore - that would be UB and may crash your program.
delete is calling the destructor of T (great, it's something you shall do) but deleteis not changing the vector. Consequently the iterator is valid all the time.
Either you should remove the element for which you have called delete or at least set the vector element to nullptr.
typename std::vector<T*>::iterator it;
for (it=std::vector<T*>::begin();it!=std::vector<T*>::end();it++)
{
delete *it;
*it = nullptr; // Only needed when you don't erase the vector element
}
That solution requires that you always check for nullptr before using any element of the vector.
In most cases the best solution is however to remove the element from the vector.
In the case where you destroy all elements by calling delete on every element, simply call clear on the vector after the loop.

For example, for you first two questions which appeared to regard clearing a vector
std::vector<int> v;//maybe have elements or not...
Clear the vector by calling clear
v.clear();
If you wish to remove elements while you walk over the vector which satisfy a condition (i.e. a predicate) using v.erase(it) you need to be careful. Fortunately, erase returns the iterator after the removed position, so something like
for (auto it=v.begin();it!=v.end();)
{
if (predicate(it))
it = v.erase(it);
else
++it;
}
Obviously you can use std::remove_if instead (with std::erase) if you want to shrink your vector slightly, avoiding a "awfully performant hand-written remove_if" as a commenter described this.
If you want to delete things as you loop round, you can either hand-do a loop yourself or use an algorithm - but smart pointers make far more sense. I suspect you are wanting to delete the items possibly in addition to shrinking the vector since you said " I found that element of this vector can be used after its deleting". If you do want to store raw pointers in a container, just delete the items, you don't the the if in your suggested code. You need to consider when you plan on doing this.
Note: changing the contents of an iterator doesn't invalidate an iterator.
You should consider using smart pointers, for exception safety etc.

Related

How to delete element in vector<type> through a reference for loop

Hello I'm pretty new to C++ and very much so in references and pointers and am trying to remove elements in the following for loop:
for(type &i : vectorName)
{
if(condition)
{
//delete i
}
}
How do I do this while making sure no null values are created?
Don't do that. Use nice built-in std::remove_if.
vectorName.erase(std::remove_if(vectorName.begin(), vectorName.end(), [](const auto& i){return condition;}),
vectorName.end());
erase is crucial, because remove_if doesn't change the size of the container. It simply returns it's new end (so that it can work with any range, not only full containers). It's called erase-remove idiom.
Your approach is much more difficult to use, because erasing element from the vector invalidates all iterators. This means that after removing one element, you cannot continue in the same range-based loop.

How does std::vector::end() iterator work in memory?

Today, I was attempting to extract a subset of N elements from a vector of size M, where N < M. I realised that I did not need to create a new copy, only needed to modify the original, and moreover, could take simply the first N elements.
After doing a few brief searches, there were many answers, the most attractive one being resize() which appears to truncate the vector down to length, and deal neatly with the memory issues of erasing the other elements.
However, before I came across vector.resize(), I was trying to point the vector.end() to the N+1'th position. I knew this wouldn't work, but I wanted to try it regardless. This would leave the other elements past the N'th position "stranded", and I believe (correct me if i'm wrong) this would be an example of a memory leak.
On looking at the iterator validity on http://www.cplusplus.com/reference/vector/vector/resize/,
we see that if it shrinks, vector.end() stays the same. If it expands, vector.end() will move (albeit irrelevant to our case).
This leads me to question, what is the underlying mechanic of vector.end()? Where does it lie in memory? It can be found incrementing an iterator pointing to the last element in the vector, eg auto iter = &vector.back(), iter++, but in memory, is this what happens?
I can believe that at all times, what follows vector.begin() should be the first element, but on resize, it appears that vector.end() can lie elsewhere other than past the last element in the vector.
For some reason, I can't seem to find the answer, but it sounds like a very basic computer science course would contain this information. I suppose it is stl specific, as there are probably many implementations of a vector / list that all differ...
Sorry for the long post about a simple question!
you asked about "the underlying mechanic of vector.end()". Well here is (a snippet of) an oversimplified vector that is easy to digest:
template <class T>
class Simplified_vector
{
public:
using interator = T*;
using const_interator = const T*;
private:
T* buffer_;
std::size_t size_;
std::size_t capacity_;
public:
auto push_back(const T& val) -> void
{
if (size_ + 1 > capacity_)
{
// buffer increase logic
//
// this usually means allocation a new larger buffer
// followed by coping/moving elements from the old to the new buffer
// deleting the old buffer
// and make `buffer_` point to the new buffer
// (along with modifying `capacity_` to reflect the new buffer size)
//
// strong exception guarantee makes things a bit more complicated,
// but this is the gist of it
}
buffer_[size_] = val;
++size_;
}
auto begin() const -> const_iterator
{
return buffer_;
}
auto begin() -> iterator
{
return buffer_;
}
auto end() const -> const_iterator
{
return buffer_ + size_;
}
auto end() -> iterator
{
return buffer_ + size_;
}
};
Also see this question Can std::vector<T>::iterator simply be T*? for why T* is a perfectly valid iterator for std::vector<T>
Now with this implementation in mind let's answer a few of your misconceptions questions:
I was trying to point the vector.end() to the N+1'th position.
This is not possible. The end iterator is not something that is stored directly in the class. As you can see it's a computation of the begging of the buffer plus the size (number of elements) of the container. Moreover you cannot directly manipulate it. The internal workings of the class make sure end() will return an iterator pointing to 1 past the last element in the buffer. You cannot change this. What you can do is insert/remove elements from the container and the end() will reflect these new changes, but you cannot manipulate it directly.
and I believe (correct me if i'm wrong) this would be an example of a
memory leak.
you are wrong. Even if you somehow make end point to something else that what is supposed to point, that wouldn't be a memory leak. A memory leak would be if you would lost any reference to the dynamically allocated internal buffer.
The "end" of any contiguous container (like a vector or an array) is always one element beyond the last element of the container.
So for an array (or vector) of X elements the "end" is index X (remember that since indexes are zero-based the last index is X - 1).
This is very well illustrated in e.g. this vector::end reference.
If you shrink your vector, the last index will of course also change, meaning that the "end" will change as well. If the end-iterator does not change, then it means you have saved it from before you shrank the vector, which will change the size and invalidate all iterators beyond the last element in the vector, including the end iterator.
If you change the size of a vector, by adding new elements or by removing elements, then you must re-fetch the end iterator. The existing iterator objects you have will not automatically be updated.
Usually the end isn't stored in an implementation of vector. A vector stores:
A pointer to the first element. If you call begin(), this is what you get back.
The size of the memory block that's been managed. If you call capacity() you get back the number of elements that can fit in this allocated memory.
The number of elements that are in use. These are elements that have been constructed and are in the first part of the memory block. The rest of the memory is unused, but is available for new elements. If the entire capacity gets filled, to add more elements the vector will allocate a larger block of memory and copy all the elements into that, and deallocate the original block.
When you call end() this returns begin() + size(). So yes, end() is a pointer that points to one beyond the last element.
So the end() isn't a thing that you can move. You can only change it by adding or removing elements.
If you want to extract a number of elements 'N' you can do so by reading those from begin() to begin() + 'N'.
for( var it = vec.begin(); it != begin() + n; ++it )
{
// do something with the element (*it) here.
}
Many stl algorithms take a pair of iterators for the begin and end of a range of elements you want to work with. In your case, you can use vec.begin() and vec.begin() + n as the begin and end of the range you're interested in.
If you want to throw away the elements after n, you can do vec.resize(n). Then the vector will destruct elements you don't need. It might not change the size of the memory block the vector manages, the vector might keep the memory around in case you add more elements again. That's an implementation detail of the vector class you're using.

Vector iterators

I have a the following code.
vector<IRD>* irds = myotherobj->getIRDs();//gets a pointer to the vector<IRD>
for(vector<IRD>::iterator it = irds->begin(); it < irds->end(); it++)
{
IRD* ird = dynamic_cast<IRD*>(it);
ird->doSomething();
//this works (*it).doSomething();
}
This seems to fail...I just want to get the pointer to each element in the vector without using (*it). all over.
How do I get the pointer to the object?
When I iterate over the vector pointer irds, what exactly am I iterating over? Is it a copy of each element, or am I working with the actual object in the vector when I say (*it).doSomething(),
Why do you want to get a pointer?
Use a reference:
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
IRD & ird = *it;
ird.doSomething();
}
Alternatively:
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
it->doSomething();
}
Also, as everyone said, use != when comparing iterators, not <. While it'll work in this case, it'll stop working if you use a different container (and that's what iterators are for: abstracting the underlying container).
You need to use != with iterators to test for the end, not < like you would with pointers. operator< happens to work with vector iterators, but if you switch containers (to one like list) your code will no longer compile, so it's generally good to use !=.
Also, an iterator is not the type that it points to, so don't try to cast it. You can use the overloaded operator-> on iterators.
vector<IRD>* irds = myotherobj->getIRDs();//gets a pointer to the vector<IRD>
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
it->dosomething();
}
Dereference the iterator to get a reference to the underlying object.
vector<IRD>* irds = myotherobj->getIRDs();
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
IRD& ird = *it;
ird.doSomething();
// alternatively, it->doSomething();
}
First consider whether you actually need a pointer to the element or if you're just trying to kind of use iterators but kind of avoid them. It looks like you're trying to code C in C++, rather than coding C++. In the example you gave, it seems like rather than converting to a pointer and then working with that pointer, why not just use the iterator directly? it->doSomething() instead of ird->doSomething().
If you're thinking that you need to save that pointer for later to use on the vector after doing some work, that's potentially dangerous. Vector iterators and pointers to elements in a vector can both be invalidated, meaning they no longer point to the vector, so you are basically attempting to use memory after you've freed it, a dangerous thing to do. A common example of things that can invalidate an iterator is adding a new element. I got into the mess of trying to store an iterator and I did a lot of work to try to make it work, including writing a "re_validate_iterator()" function. Ultimately, my solution proved to be very confusing and didn't even work in all cases, in addition to not being scalable.
The solution to trying to store the position of the vector is to store it as an offset. Some integer indicating the position within the vector that your element is at. You can then access it with either myvector.begin() + index if you need to work with iterators, or myvector.at (index) if you want a reference to the element itself with bounds checking, or just myvector [index] if you don't need bounds checking.
You can get a pointer from an iterator by doing &*it. You get a pointer to the actual IRD object stored inside the vector. You can modify the object through the pointer and the modification will "stick": it will persist inside the vector.
However, since your vector contains the actual objects (not pointers to objects) I don't see any point in dynamic_cast. The type of the pointer is IRD * and it points to IRD object.
The only case when the dereferenced iterator might refer to a copy (or, more precisely, to a proxy object) is vector<bool>, which might be implemented as a bit-vector.
When I iterate over the vector pointer irds, what exactly am I iterating over? Is it a copy of each element, or am I working with the actual object in the vector when I say (*it).doSomething(),
When you iterate over a vector you work with the object itself, not a copy of it.
The usual idiom is &*it to get a pointer. Dynamic casts have nothing to do with it.

Does ptr_vector iterator not require increments?

#include <boost/ptr_container/ptr_vector.hpp>
#include <iostream>
using namespace std;
class Derived
{
public:
int i;
Derived() {cout<<"Constructed Derived"<<endl;}
Derived(int ii):i(ii) {cout<<"Constructed Derived"<<i<<endl;}
~Derived() {cout<<"* Destructed Derived"<<i<<endl;}
};
int main()
{
boost::ptr_vector<Derived> pv;
for(int i=0;i<10;++i) pv.push_back(new Derived(i));
boost::ptr_vector<Derived>::iterator it;
for (it=pv.begin(); it<pv.end();/*no iterator increment*/ )
pv.erase(it);
cout<<"Done erasing..."<<endl;
}
Notice that the second for loop does not increment the iterator, yet it iterates and erases all elements. My questions are:
Is my technique of iteration and using the iterator correct?
If iterator increment is not required in the for loop, then where does the increment happen?
Is it better to use an iterator or will an ordinary integer suffice (ie: is there any value-add with using iterators)? (coz I can also erase the 5th element like pv.erase(pv.begin()+5);)
Is there any way to assign a new object to a specific position (let's say the 5th position) of ptr_vector, directly? I'm looking for something like pv[5]=new Derived(5);. Any way of doing that?
A ptr_vector::iterator increments just like a normal random access iterator. In your example, you are able to erase every element without actually incrementing because after you erase an element, every element after it is moved over in the array. So when you erase the 0th element, your iterator now points to the element which used to be the 1st element, but is now the 0th element, and so on. In other words, the iterator is staying in place while the whole vector is shifting over to the left.
This has nothing specifically to do with ptr_vector. Note the same behavior would occur with a plain std::vector.
Also note that using an iterator after you erase the element it points to is dangerous. In your case it works, but it's better to take the return value of ptr_vector::erase so you get a new iterator which is guaranteed to be valid.
for (it = pv.begin(); it != pv.end(); )
it = pv.erase(it);
As for your other questions:
If you only want to erase a specific element, then of course you should erase it directly using pv.erase(pv.begin() + N). To assign a new value to a specific element in the pointer vector, simply say pv[N] = Derived(whatever). You don't need to use new when reassigning a value. The pointer vector will invoke the assignment operator of the object at the index you assign the new value to.
Is my technique of iteration and using the iterator correct?
No, erasing from a container generally invalidates the iterator to the erased item. If it works, this is just a side-effect of the implementation details.
The correct way would be to use the return value of the erase method:
it = pv.erase(it);
However, for emptying the container, you can use the clear member function.
If iterator increment is not required in the for loop, then where
does the increment happen?
It doesn't happen, because you'll always be erasing the first item in the container (by chance, might not work out with other containers).
Is it better to use an iterator or will an ordinary integer suffice (ie:
is there any value-add with using
iterators)? (coz I can also erase the
5th element like
pv.erase(pv.begin()+5);)
In a random-access container you can do that, otherwise not (such as a list).
Is there any way to assign a new object to a specific position (let's
say the 5th position) of ptr_vector,
directly? I'm looking for something
like pv[5]=new Derived(5);. Any way
of doing that?
According to the boost reference:
pv.replace(5, new Derived(5));
This returns the existing pointer in a smart pointer, so it will be automatically deallocated.
(It's curious that this takes an index, not an iterator...).
Or:
pv[5] = Derived(5);
but this will just modify the stored object, not change the pointer.

Pointers to elements of std::vector and std::list

I'm having a std::vector with elements of some class ClassA. Additionally I want to create an index using a std::map<key,ClassA*> which maps some key value to pointers to elements contained in the vector.
Is there any guarantee that these pointers remain valid (and point to the same object) when elements are added at the end of the vector (not inserted). I.e, would the following code be correct:
std::vector<ClassA> storage;
std::map<int, ClassA*> map;
for (int i=0; i<10000; ++i) {
storage.push_back(ClassA());
map.insert(std::make_pair(storage.back().getKey(), &(storage.back()));
}
// map contains only valid pointers to the 'correct' elements of storage
How is the situation, if I use std::list instead of std::vector?
Vectors - No. Because the capacity of vectors never shrinks, it is guaranteed that references, pointers, and iterators remain valid even when elements are deleted or changed, provided they refer to a position before the manipulated elements. However, insertions may invalidate references, pointers, and iterators.
Lists - Yes, inserting and deleting elements does not invalidate pointers, references, and iterators to other elements
As far as I understand, there is no such guarantee. Adding elements to the vector will cause elements re-allocation, thus invalidating all your pointers in the map.
Use std::deque! Pointers to the elements are stable when only push_back() is used.
Note: Iterators to elements may be invalidated! Pointers to elements won't.
Edit: this answer explains the details why: C++ deque's iterator invalidated after push_front()
I'm not sure whether it's guaranteed, but in practice storage.reserve(needed_size) should make sure no reallocations occur.
But why don't you store indexes?
It's easy to convert indexes to iterators by adding them to the begin iterator (storage.begin()+idx) and it's easy to turn any iterator into a pointer by first dereferencing it, and then taking its address (&*(storage.begin()+idx)).
Just make them both store pointers an explicitly delete the objects when you don't need them.
std::vector<ClassA*> storage;
std::map<int, ClassA*> map;
for (int i=0; i<10000; ++i) {
ClassA* a = new ClassA()
  storage.push_back(a)
  map.insert(std::make_pair(a->getKey(), a))
}
// map contains only valid pointers to the 'correct' elements of storage
From one of the comments to another answer, it seems as if all that you want is centralize (ease) memory management. If that is really the case, you should consider using prepackaged solutions like the boost pointer container library and keep your own code as simple as possible.
In particular, take a look at ptr_map
for vectors no.
for lists yes.
how?
iterator works as a pointer to a particular node in the list.
so you can assign values to any struct like:
list mylist;
pair< list::iterator ,int > temp;
temp = make_pair( mylist.begin() , x );