|
Boost : |
From: Luigi Ballabio (ballabio_at_[hidden])
Date: 2002-01-15 06:01:40
At 03:47 PM 1/14/02 -0500, David Abrahams wrote:
>You should have read past my first sentence:
>
> > > However, since shared_ptr deletes "implicitly",
> > > allowing implicit conversion of shared_ptr<Derived>
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > > to shared_ptr<Base> can be problematic.
>
>There is no upcast, only an implicit conversion.
Dave,
you got me puzzled about what you mean by that. From what I see
from the shared_ptr code, (implicit) conversion of shared_ptr<Derived> to
shared_ptr<Base> results in the latter storing the address of a Derived
instance into a Base*. That is what I referred to as 'upcast'. I do agree
that the user does not explicitly upcast the pointer; however, the effect
is the same. But that is not my point anyway, please read ahead :)
>When a deadly usage silently compiles, it's a problem that should be
>corrected, especially if we have the tools to do so.
Absolutely. But as I pointed out in my previous post, calling the wrong
destructor is not the only problem. Here's a somewhat contrived example:
class Base {
public:
Base(int i = 0) : i_(i) {}
~Base() {} // not virtual
int value() const { return i_; }
void setValue(int i) { i_ = i; } // not virtual
private:
int i_;
};
class Derived : public Base {
public:
Derived(int i = 0) : Base(i), twice_(i*2) {}
~Derived() {}
int twiceTheValue() const { return twice_; }
void setValue(int i) { Base::setValue(i); twice_ = i*2; }
private:
int twice_; // additional state
};
void wrongUsage() {
shared_ptr<Derived> d(new Derived);
shared_ptr<Base> b(d);
b->setValue(3); // calls Base::setValue, not Derived::setValue
std::cout << d->value() << std::endl; // outputs 3
std::cout << d->twiceTheValue() << std::endl; // outputs 0 (!)
}
The above silently compiles, is not deadly, but it is just plain wrong.
My point being: alas, you cannot entirely shield a user from not knowing
what he's doing. If a class is not meant to be used polymorphically (i.e.,
through a pointer to its base) then the addresses of its instances should
not be stored in pointers to base---hence shared_ptr<Derived> should not be
(implicitly) converted to shared_ptr<Base>. Please note that I'm _not_
criticizing shared_ptr: when I say "should not be converted" I don't mean
that shared_ptr should be modified to disallow this, but rather that the
shared_ptr user should know what he's doing when he converts
shared_ptr<Derived> to shared_ptr<Base>, and should be aware of what he can
and cannot do with the latter. In my opinion, the wrongUsage() function
above does not show a shared_ptr flaw, but rather a logic error in
wrongUsage() itself.
Bye,
Luigi
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk