inheriting from std::vector

Started by
23 comments, last by Fruny 17 years, 9 months ago
Allright, I decided I'll privatly inherit from std::vector then, and only use "using" on functions of std::vector that are actually needed, to avoid all possible problems described and any other possible things that could screw it up.

Thanks for the help people!

I learned a lot of new things about C++ today :)
Advertisement
Allright, here's the result! Do you think it's fine now?

The inheritance is now private, and now it'll also do delete on elements if you do clear(), erase(), pop_back() and resize()!

I didn't do "using" on the function "swap" because of the reason posted by deffer.

I drew inspiration from /usr/include/c++/4.0.3/bits/stl_vector.h to use the same syntax as them for some of the functions.

#ifndef pvector_h#define pvector_h#include <vector>/*pvector by Lode VandevenneA pvector works like an std::vector but it'll use the delete operator on all elements when it's destructed.The elements should always be pointers created with new.Only non-polymorphic use is allowed (because it's inherited from std::vector which has a non-virtual dtor).*/template<typename T>class pvector : std::vector<T>{     public:     ~pvector()     {         for(typename std::vector<T>::size_type i = 0; i < std::vector<T>::size(); i++) delete (*this);     }          void pop_back()     {         delete (*this)[std::vector<T>::size() - 1];         std::vector<T>::pop_back();     }          void resize(typename std::vector<T>::size_type __new_size)     {         for(int i = __new_size; i < std::vector<T>::size(); i++) delete (*this);         std::vector<T>::resize(__new_size);     }          void erase(typename std::vector<T>::iterator __position)     {         delete *__position;                  std::vector<T>::erase(__position);     }          void erase(typename std::vector<T>::iterator __first, typename std::vector<T>::iterator __last)     {         for(typename std::vector<T>::iterator __position = __first; __position != __last; __position++)         {             delete *__position;         }                  std::vector<T>::erase(__first,__last );     }          void clear()     {         erase(begin(), end());     }          using std::vector<T>::size;     using std::vector<T>::operator[];     using std::vector<T>::push_back;     using std::vector<T>::begin;     using std::vector<T>::end;     using std::vector<T>::rbegin;     using std::vector<T>::rend;     using std::vector<T>::at;     using std::vector<T>::back;     using std::vector<T>::capacity;     using std::vector<T>::empty;     using std::vector<T>::front;     using std::vector<T>::max_size;     using std::vector<T>::reserve;     using std::vector<T>::insert;     //using std::vector<T>::swap; //No!};#endif
Just to throw this out there, but I wouldn't be surprised if the Boost pointer containters can do just what you are after.

I'd consider this a learning exercise and then use Boost for future projects [smile]
Quote:Original post by phantom
Just to throw this out there, but I wouldn't be surprised if the Boost pointer containters can do just what you are after.

I'd consider this a learning exercise and then use Boost for future projects [smile]


It was learning exercise! It was inspired by the ptr_vector by the way, when I heard about its feature I immediatly knew I wanted it.

I also like having my code independent from boost, so I'm going to keep using this pvector from now on. Those 73 lines of code are the only boost-feature I need so I prefer using this over the huge thing that boost is. Really, Boost is all great and stuff, but, it's huge!

I don't need a list version. I don't really need to make pointers to the pointers kept by this type of container for now. But if I'd for some reason need a list later I'll make plist too.
The semantics of your class are completely screwy. By exposing the pointers directly to the clients of your class you basically forgo any protection that your class provides. Also, consider the effects if insert(), push_back() or a similar operation throws an exception: unless every call you make that will potentially enlarge the vector explicitly wraps the calls with try/catch blocks, you're going to have a memory leak. This is not kosher for an operation that implicitly assumes ownership of the pointed to object. Go look up the boost pointer container documentation and look at their interface; there's a reason why it's made the way it is.
Darn programming is hard, it's impossible to do something in a straightforward way :(

I'm getting discouraged but at the same time I find it interesting that I'm finally starting to see why so many complex pieces of code are to be found in the STL and Boost.
It depends on your definition of "straightforward".
boost::ptr_vector<MyClass> my_vector; // seems pretty straightforward to me
Quote:Original post by Lode
Darn programming is hard, it's impossible to do something in a straightforward way :(


Writing good library components is probably the one of the hardest programming task there is. Making sure it is safe yet easy to use is difficult and takes significant effort. Sacrificing either just because it saves on typing is not the way to write great code.

Quote:I'm getting discouraged but at the same time I find it interesting that I'm finally starting to see why so many complex pieces of code are to be found in the STL and Boost.


They've gone through the trouble of getting it right, might as well not duplicate the effort and focus on the things they don't provide.

Quote:I also like having my code independent from boost, so I'm going to keep using this pvector from now on. Those 73 lines of code are the only boost-feature I need so I prefer using this over the huge thing that boost is. Really, Boost is all great and stuff, but, it's huge!


And so is the Windows API. I don't really see where the library's size is a problem. It's not like it all gets added to your program. Once you have Boost installed on your machine you don't really have to worry about it anymore.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Lode
I also like having my code independent from boost, so I'm going to keep using this pvector from now on. Those 73 lines of code are the only boost-feature I need so I prefer using this over the huge thing that boost is. Really, Boost is all great and stuff, but, it's huge!


ptr_vector is a header only library. and if you're really that short on space that you don't want the rest of the libraries on your hard drive, use bcp to extract the files you need for ptr_vector.

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Allright, another hopefully improved version.

Now, you can't access the pointers anymore, note how the template type is T but the std::vector inherited from is of type T*, and how operator[] returns the element as reference and not the pointer.

The resize function is removed, you're supposed to push_back elements one by one, with the new operator used.

And finally, the push_back function will delete the element if memory is full (but I commented out two things that I didn't know what they do, it comes from the stl_vector.h file).

EDIT: and, now it also checks if the pointer isn't a null pointer, before deleting.
EDIT2: this test removed again, see next post by Fruny

#ifndef pvector_h#define pvector_h#include <vector>/*A pvector works like an std::vector but is for pointers to polymorphed objects.It'll use the delete operator on all elements that get destroyed.It returns the elements as references instead of pointers.The elements should always be pointers created with new.*/template<typename T>class pvector : std::vector<T*>{     public:     ~pvector()     {         for(typename std::vector<T*>::size_type i = 0; i < std::vector<T*>::size(); i++) delete &(*this);     }          void pop_back()     {         delete (*this)[std::vector<T*>::size() - 1];         std::vector<T*>::pop_back();     }          void erase(typename std::vector<T*>::iterator __position)     {         delete *__position;                  std::vector<T*>::erase(__position);     }          void erase(typename std::vector<T*>::iterator __first, typename std::vector<T*>::iterator __last)     {         for(typename std::vector<T*>::iterator __position = __first; __position != __last; __position++)         {             delete *__position;         }                  std::vector<T*>::erase(__first,__last );     }          void clear()     {         erase(begin(), end());     }          T& operator[](typename std::vector<T*>::size_type __n)     {         return *(std::vector<T*>::operator[](__n));     }          void push_back(const typename std::vector<T*>::value_type& __x)     {         if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)         {             delete __x; //just delete the thing already, seriously, normally memory shouldn't go full             //this->_M_impl.construct(this->_M_impl._M_finish, __x);             //++this->_M_impl._M_finish;         }         else         {             std::vector<T*>::_M_insert_aux(end(), __x);         }     }          using std::vector<T*>::size;     using std::vector<T*>::begin;     using std::vector<T*>::end;     using std::vector<T*>::rbegin;     using std::vector<T*>::rend;     using std::vector<T*>::at;     using std::vector<T*>::back;     using std::vector<T*>::capacity;     using std::vector<T*>::empty;     using std::vector<T*>::front;     using std::vector<T*>::max_size;     using std::vector<T*>::reserve;     //using std::vector<T*>::resize; //you're supposed to push_back "new" objects     //using std::vector<T*>::insert; //disabled     //using std::vector<T*>::swap; //No!};#endif


[Edited by - Lode on July 2, 2006 4:12:54 PM]

This topic is closed to new replies.

Advertisement