Topic: Polymorphic containers using the STL
Author: sbnaran@fermi.ceg.uiuc.edu (Siemel Naran)
Date: 1998/10/23 Raw View
On 23 Oct 1998 15:08:22 GMT, Aur=E9lien Geron <tessier.geron@wanadoo.fr> =
wrote:
>1) Store pointers to Base :
Good. But you have to remember to delete the objects manually, as you've
pointed out. Now if the container is a private member (note: not public
or protected) of some useful class, the end user of your useful class
does not have to know about these messy details. The deletion of the
pointed to objects can be done in the dtor of your useful class. To ease
the notation, we can use a sentry object in the useful class:
template <class Container>
class pointer_container
{
private:
Container& container;
=20
typedef typename Container::value_type pointer;
struct kill { void operator(pointer p) { delete p; =
} };
struct clone { pointer operator(pointer p) { return p.clone(); =
} };
public:
pointer_container(Container&);
~pointer_container()
{
for_each(container.begin(),container.end(),kill());
}
=20
void clone_in_place()
{
transform(container.begin(),container.end(),clone());
}
private:
pointer_container(const pointer_container&); // not implemented
void operator=3D(const pointer_container&); // not implemented
};
class UsefulClass
{
private:
typedef std::vector<Base*> container_type;
container_type d_container;
std::pointer_container<container_type> u_container;
=20
public:
UsefulClass() : d_container(), u_container(d_container) { }
UsefulClass(const UsefulClass& that)
d_container(that.d_container), u_container(d_container)
{
u_container.clone_in_place();
}
};
It's a little annoying that we can't rely on the compiler generated copy
ctor and operator=3D. The copy ctor sequence above is a little inefficie=
nt
as we first copy the pointers and then clone each. We could just copy
cloned pointers.
>2) Store smart pointers to Base :
>list<auto_ptr<Base> > mySmartPtrList;
A container of auto_ptr's is not allowed. Conceptually, this is because
auto_ptr copies are not identical (the original auto_ptr is nullified in
the copy). Technically, this is not allowed because auto_ptr's copy ctor
takes a non-const reference, whereas the std:: containers require it to
take a const reference. Similar remarks apply for operator=3D. However,
list<SmartPointer> or list<RefCountedPointer> is allowed. Whether you
want to do this is another matter. It might be overkill.
>3) Develop a new template class :
Painful, as you've pointed out.
>4) Define a special allocator :
An idea. BTW, as your new allocator redefines some non-virtual funcs in
the std::allocator class, it should not derive from std::allocator.
Specifically, the destroy func of your allocator class calls the dtor for
the pointed to object -- std::allocator calls the dtor for the pointer
itself. This func is non-virtual.
>myList.push_back (der); // Derived::clone() is called so the list handl=
es a
>complete copy of der
You're sure you want to have the allocator call clone()? After all, one
advantage of pointers is that copying pointers is cheap. Suppose you
have a vector<Object*> and you resize the vector. This may relocate the
vector. Copying pointers from the old vector to the new is cheap, but
copying the objects themselves is much more expensive. Also, suppose you
have a read func that returns a pointer to a newly created object. There=
's
no need to clone the pointed to object.
Base* read(istream&); // returns a Derived1 or Derived2
container.push_back(read(cin)); // no need to call Base::clone()
But this leads to a problem. What if the pointers in a vector are
relocated as in vector::operator=3D? Suppose copying a vector just copie=
s
pointers. But then destroy is called on each element in the original
vector, which destroys all the pointed-to objects. So the pointers in
the new vector point to deallocated objects. This means that your new
allocator's construct function should indeed call clone. But then,
we lose one of the advantages of pointers -- namely cheap copies.
>My conclusion is that it's a pitty the standard doesn't define an alloca=
tor
>of this kind, don't you ?
No, it would cause an efficiency problem, as noted above. The sentry
object solution is better in that the copying and destroying of the
pointed to objects are done not by the container itself, but by the
higher level context that owns the container.
--=20
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: brownsta@concentric.net (Stan Brown)
Date: 1998/10/24 Raw View
[posted and emailed]
tessier.geron@wanadoo.fr (Aur lien Geron) wrote:
>
>I am trying to figure out what are the different possibilities to have
>polymorphic containers using the STL. By default, 'normal' STL containers,
>like vectors or lists, do not handle polymorphism. That is, the following
>code will NOT work properly :
True.
Both Koenig's /Ruminations on C++/ and Meyers' /More Effective C++/ have
extended essays on how to implement an STL container to hold objects of
several types.
--
Stan Brown, Oak Road Systems, Cleveland, Ohio, USA
http://www.concentric.net/%7eBrownsta/
My reply address is correct as is. The courtesy of providing a correct
reply address is more important to me than time spent deleting spam.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Aur lien Geron" <tessier.geron@wanadoo.fr>
Date: 1998/10/23 Raw View
Hi,
I am trying to figure out what are the different possibilities to have
polymorphic containers using the STL. By default, 'normal' STL containers,
like vectors or lists, do not handle polymorphism. That is, the following
code will NOT work properly :
list <Base> myList;
Derived der;
myList.push_back (der); // only the Base part of der is copied and stored
in the list.
I have thought of different solutions, presented below. Does anyone know of
any other ?
******************************
1) Store pointers to Base :
____________________
list <Base *> myPtrList;
Derived * derPtr = new Derived;
myPtrList.push_back (derPtr);
delete myPtrList.front(); // the container will not destroy the object
automatically
This works correctly, but memory managment problems can be difficult to
handle. This leads to the following solution :
**************************************
2) Store smart pointers to Base :
_________________________
#include <list>
#include <memory> // for the auto_ptr template class
list<auto_ptr<Base> > mySmartPtrList;
mySmartPtrList.push_back (auto_ptr<Base> (new Derived()));
This handles memory and polymorphism pretty well but it gets ugly and
complicated to use.
***************************************
3) Develop a new template class :
__________________________
template <class T,...>
class polymorphicList {
private:
list<T *,...> thePtrList; // should I rather use inheritance ?
public:
... // redefine functions to have a better interface
};
This is quite a disappointing solution : it means you have to program this
for every container. Maybe this could be solved by defining a template with
one parameter indicating the STL container to imitate... I haven't tried
that, because it would be hell to write.
**********************************
4) Define a special allocator :
_______________________
template <class T>
class polymorphicAllocator : public allocator<T> { ... };
list<Base, polymorphicAllocator<Base> > myList;
Derived der;
myList.push_back (der); // Derived::clone() is called so the list handles a
complete copy of der
This might certainly be the best solution, but I haven't succeeded in
writing it. Does anybody have any ideas ?
My conclusion is that it's a pitty the standard doesn't define an allocator
of this kind, don't you ?
Bye,
Aurelien Geron.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]