Boost logo

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