Does ptr_vector iterator not require increments? - c++

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

Related

Safety deleting elements of the container

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.

Erase element from std::list using a pointer?

class SororityBeerExpo{
public:
std::list<LaysCrankdPepChip> m_chipList;
void destroyChip(LaysCrankdPepChip * target)
{
// m_chipList needs to erase that which is pointed at by target but
// erase() requires an iterator which is not what we got to work with
// remove() needs a value which is not right either
}
}
My question, is how would one delete an element in a list, with a pointer that points to that element? I would like to do this without using Iterators in place of pointers.
You can do a [linear] search for the element in the list, comparing the address of each element to your pointer (std::find_if will fit the bill). Of course, you will still be using an iterator in the end, because that's what list::erase needs.
You can't do it directly (although see Benjamin's answer for an indirect way of doing it). A list node contains more data than just the object being contained (e.g. pointers to the previous and next nodes), but your raw pointer only points to the contained object.

Is it safe to hold pointers to iterators in C++?

I will ask the question first and the motivation next, and finally an illustrative code sample which compiles and executes as expected.
Question
If I can assure myself that an iterator will not get invalidated in the duration when I will be needing to use it, is it safe to hold a pointer to an iterator (e.g. a pointer to a list<int>::iterator).
Motivation
I have multiple containers and I need direct cross references from items held in one container to the corresponding items held in another container and so on. An item in one container might not always have a corresponding item in another container.
My idea thus is to store a pointer to an iterator to an element in container #2 in the element stored in container #1 and so forth. Why? Because once I have an iterator, I can not only access the element in container #2, but if needed, I can also erase the element in container #2 etc.
If there is a corresponding element in container #2, I will store a pointer to the iterator in the element in container #1. Else, this pointer will be set to NULL. Now I can quickly check that if the pointer to the iterator is NULL, there is no corresponding element in container #2, if non-NULL, I can go ahead and access it.
So, is it safe to store pointers to iterators in this fashion?
Code sample
#include <iostream>
#include <list>
using namespace std;
typedef list<int> MyContainer;
typedef MyContainer::iterator MyIterator;
typdef MyIterator * PMyIterator;
void useIter(PMyIterator pIter)
{
if (pIter == NULL)
{
cout << "NULL" << endl;
}
else
{
cout << "Value: " << *(*pIter) << endl;
}
}
int main()
{
MyContainer myList;
myList.push_back(1);
myList.push_back(2);
PMyIterator pIter = NULL;
// Verify for NULL
useIter(pIter);
// Get an iterator
MyIterator it = myList.begin();
// Get a pointer to the iterator
pIter = & it;
// Use the pointer
useIter (pIter);
}
Iterators are generally handled by value. For instance, begin() and end() will return an instance of type iterator (for the given iterator type), not iterator& so they return copies of a value every time.
You can of course take an address to this copy but you cannot expect that a new call to begin() or end() will return an object with the same address, and the address is only valid as long as you hold on to the iterator object yourself.
std::vector<int> x { 1, 2, 3 };
// This is fine:
auto it = x.begin();
auto* pi = &it;
// This is not (dangling pointer):
auto* pi2 = &x.begin();
It rarely makes sense to maintain pointers to iterators: iterators are already lightweight handles to data. A further indirection is usually a sign of poor design. In your example in particular the pointers make no sense. Just pass a normal iterator.
The problem with iterators is that there are a lot of operations on containers which invalidate them (which one depend on the container in question). When you hold an iterator to a container which belongs to another class, you never know when such an operation occurs and there is no easy way to find out that the iterator is now invalid.
Also, deleting elements directly which are in a container which belongs to another class, is a violation of the encapsulation principle. When you want to delete data of another class, you should better call a public method of that class which then deletes the data.
Yes, it is safe, as long as you can ensure the iterators don't get invalidated and don't go out of scope.
Sounds scary. The iterator is an object, if it leaves scope, your pointer is invalid. If you erase an object in container #2, all iterators may become invalid (depending on the container) and thus your pointers become useless.
Why don't you store the iterator itself? For the elements in container #1 that don't refer to anything, store container2.end().
This is fine as long as iterators are not invalidated. If they are, you need to re-generate the mapping.
Yes it is possible to work on pointers to iterators like it is to other types but in your example it is not necessary since you can simple pass the pass the original iterator as reference.
In general it is not a good idea to store iterators since the iterator may become invalid as you modify the container. Better store the containers and create iterators as you need them.

Checking whether element exists in a Vector

I have a vector and it is going to hold 10 objects maximum. When I create the vector I believe it creates 10 "blank" objects of the class it will hold. Therefore I have a problem because my code relies on checking that a vector element is null and obviously it is never null.
How can I check whether a vector object contains an element I inserted, or one of the default constructor "blank" objects upon initialisation?
Is there a technique round this?
(I need to check for null because I am writing a recursive algorithm and the termination point, when the recursive function returns is when an object in the vector is null)
An instance of a class cannot be null. Only a pointer.
You do, however, have size() that you can use.
typedef stdd::vector<SomeClass> vec;
//define some vec, v
for (vec::size_type i = 0, s = vec.size(); i < s; ++i) {
//do something with v[i]
}
With a recursive function you could use this idea by passing along a maximum index.
void recursiveFunc(vec& v, vec::size_type s);
Then when checking your condition to recurse, you would need to check "am I at the end of the vector?"
Alternatively, instead of working on indexes, you could use iterators:
template <typename Iterator>
void recursiveFunc(Iterator begin, const Iterator& end);
If done correctly (and if possible in your situation), this could decouple your manipulation from being aware of the underlying data being stored in a vector.
The loop to go over the vector would then look like:
while (begin != end) {
//do something with *begin
++begin;
}
std::vector only inserts "real" objects. It (at least normally) allocates raw memory, and uses placement new to construct objects in that memory as needed. The only objects it'll contain will be the ones you put there.
Of course, if you want to, you can create a vector containing a number of copies of an object you pass to the constructor. Likewise, when you resize a vector, you pass an object it'll copy into the new locations if you're making it larger.
Neither of those is really the norm though. In a typical case, you'll just create a vector, which will start out containing 0 objects. You'll use push_back to add objects to the vector. When you search through the vector, the only objects there will be the ones you put there with push_back, and you don't need to worry about the possibility of it containing any other objects.
If you just want to check whether the vector is empty, you can just use:
if (your_vector.empty())
...which will (obviously enough) return true if it's empty, and false if it contains at least one object.
As #Corbin mentioned, size() will return the number of elements in the vector. It is guaranteed not to have any holes in between(contiguous), so you assured vector[vector.size()] is empty.

Can I delete a item from std::list based on pointer value?

This code is written on fly, plz ignore syntax mistakes if any.
std::list<MY_STRUCT> myList;
MY_STRUCT theStruct;
myList.push_back( theStruct );
myList.push_back( theStruct );
// assume I store the pointer of the last item (the 2nd item in this case).
MY_STRUCT * item2 = &myList.back();
// I added another item
myList.push_back( theStruct );
// now I want to delete item2 that I stored bases on its pointer.
// Can myList.remove_if(...) help if so how?
I want to delete the middle item in the list by its pointer (assume I have the pointer value).
I know I can iterate through the list and look for this pointer but is there a better way? Does STL provide a function to do it..Can I use remove_if() in this case to delete the item?
Instead of keeping a pointer to the object you want to remove, why not keep an iterator?
std::list<MY_STRUCT>::iterator item2 = --mylist.end();
The remove_if algorithm doesn't actually remove anything, it just shifts stuff around. It has no knowledge of the container that the iterators point to. Of course the member function remove_if of std::list is a different thing altogether as pointed out in the comments.
Sure, list::remove_if uses whatever condition you give it. For example
template <typename T>
struct AddressIs {
T *ptr;
AddressIs(T *ptr) : ptr(ptr) {}
bool operator()(const T &object) const {
return ptr == &object;
}
};
myList.remove_if(AddressIs<MY_STRUCT>(item2));
Mankarse's point is good though - if you can use an iterator instead of a pointer to identify the item you're interested in, then you don't need to mess about with this.
Beware also that we're relying here on the fact that the address of an item in a list stays the same forever. That isn't always true of all collections, for example vector might have to relocate all the data when you call push_back. If it does, then your middle item is no longer pointed to by item2. Each collection documents which operations can invalidate iterators and/or references to elements.
Instead of getting the back item, you could get the end iterator, make sure it's not begin, decrement by one to point to the last item, and then erase that iterator directly whenever you want.
I think remove_if is a little bit of overkill for what zadane is trying to do. All that needs to be accomplished is to save the location or value of an item in order to delete that specific item later.
As Mark suggested you can store the iterator to the object and use it to delete the item with an erase call, like below:
MY_STRUCT struct;
myList.push_back(struct);
myList.push_back(struct);
std::list<MY_STRUCT>::iterator del_it = myList.end() - 1;
myList.erase(del_it);
Or, if your structure has the == operator defined for MY_STRUCT, you can store the value of the object itself and use the remove method
MY_STRUCT struct1;
MY_STRUCT struct2;
myList.push_back(struct1);
myList.push_back(struct2);
myList.remove(struct2);
Of course if you make your list a list of pointers then you don't have to worry about the == operator as it is already defined for pointer types. Just make sure that if you're iterating through the list and call erase, you need to update your iterator with the returned value.
Also, the remove method removes all elements of the passed value, so if you only want to remove 1 item at a time save the iterator and not the value.
This code is untested so I welcome any corrections.