|
Boost : |
From: George A. Heintzelman (georgeh_at_[hidden])
Date: 2002-02-03 16:23:11
> From: "Rainer Deyke" <root_at_[hidden]>
> > It sounds like your 'auto_vector' avoids some of the problems of a
> > container of auto_ptrs by conceptually being a container of vanilla
> > pointers. Unfortunately this has its own set of problems. Consider:
> >
> > auto_vector<Cat> cats;
> > cats.push_back(new Cat);
> > cats[0] = new Cat; // Error: memory leak
>
> With my current version, yes this is unfortunately true. I know
> enough not to do that, and so it has never been a problem for me.
> I regard auto_vector as being convenient and not full proof.
>
> If more safety is required, I guess we could look at making
> operator[], front, and back return values rather than references.
> But it would still cause a leak if you did a
>
> *cats.begin() = new Cat;
How about having the interface always dealing with Cats, not pointers
to Cats? Then the above is a syntax error, as *cats.begin() will be a
reference to a Cat, not a reference to a pointer to a Cat. You would be
safe against anything short of:
*cats.begin() = *(new Cat);
and that I decline to protect programmers from their own stupidity
for...
My preference is to make the functions which look like vector's act
like vector's, so all of push_back, etc, pass by value (possibly
slicing). However, you should supply some other member functions which
'adopt' a passed-in pointer and place it in an appropriate place,
avoiding the copy ctor:
cats.adopt_back(new Cat);
The only issue I see with this interface is that the standard
push_back, insert, etc, won't behave polymorphically, and this
container supports polymorphic containment just fine. But since none of
those functions in STL containers behave that way, this is IMHO not a
big flaw, as long as functionality is supplied to allow the polymorphic
behavior when someone desires it. A second potential issue might be that
cats[0] = Calico(...);
would slice, perhaps non-intuitively. Again, I think anyone working
with polymorphism needs to be aware of slicing possibilities anyway, so
this is also not a big flaw.
An alternative safe interface would be to have all of the various
insertion statements (and operator[], which returns a non-const
reference to an auto_ptr in this scheme) take an auto_ptr instead of a
straight pointer. This makes the allowed polymorphism a little more
obvious, and means that accidental adoptions/leaks won't happen. The
problem as I see it is that begin() now has the semantics of a pointer
to a pointer to the contained object, which is completely at odds with
the STL; trying to use algorithms looping over Cats requires
boost::indirect_iterator when IMHO this should be contained in the
container's iterators. So while I would probably use this latter
interface, I prefer the other.
> Or I guess we could go the whole hog and return some sort of
> proxy that deleted the current pointer on assignment.
> But I don't know if the added safety of either is worth the
> trouble. What do others think? Keep it simple or make it safe?
I think there isn't any proxy necessary in the scheme I would propose.
The only necessity is to write new iterator types. For that, you can
use boost::indirect_iterator, though in this case and for orthogonality
I would rather see it written explicitly. You could I suppose write a
proxy which prevented (at least simple-minded) slicing possibilities.
That might be neat, and probably wouldn't be too hard to do, but as I
say I don't think it is necessary for a useful class, as long as
functions are provided which do not slice.
George Heintzelman
georgeh_at_[hidden]
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk