views:

43

answers:

2
#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:

  1. Is my technique of iteration and using the iterator correct?
  2. If iterator increment is not required in the for loop, then where does the increment happen?
  3. 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);)
  4. 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?
+3  A: 

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.

Charles Salvia
Oh cool! Would appreciate an answer to questions 3 and 4 too :)
Nav
Nope. I tried pv[0]=Derived(888); and got this output: Constructed Derived888boostPtrVsSTL: /usr/include/boost/ptr_container/ptr_sequence_adapter.hpp:325: typename boost::ptr_container_detail::reversible_ptr_container<boost::ptr_container_detail::sequence_config<T, VoidPtrSeq>, CloneAllocator>::reference boost::ptr_sequence_adapter<T, VoidPtrSeq, CloneAllocator>::operator[](typename boost::ptr_container_detail::reversible_ptr_container<boost::ptr_container_detail::sequence_config<T, VoidPtrSeq>
Nav
[continued...], CloneAllocator>::size_type) [with T = Derived, VoidPtrSeq = std::vector<void*, std::allocator<void*> >, CloneAllocator = boost::heap_clone_allocator]: Assertion `n < this->size()' failed.Aborted (core dumped)
Nav
@Nav: You didn't try doing 4) after you cleared the container? The assertion says that the index (0) is out of range.
UncleBens
@Nav, you can only assign an element to a valid index. If you erase everything in the vector, `pv[0]` is no longer a valid index. You need to use `push_back` to add a new element if the vector is empty.
Charles Salvia
Ok. Thanks. I thought that since the capacity() of the vector was 10, I could assign values to it anywhere. I stand corrected.
Nav
+1  A: 

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.

UncleBens