Boost logo

Boost :

From: Jeff Garland (jeff_at_[hidden])
Date: 2005-02-26 21:55:18


On Fri, 25 Feb 2005 18:26:22 +0000, Jonathan Wakely wrote

> > >christopher diggins wrote: I think publicly inheriting from a class that
> > >isn't designed for inheritance is a bad idea. There are type safety
> > >problems, non-virtual destructors, slicing etc.
> >
> > Hi Daniel thanks for the help and suggestions ( and Kevin and Jonathan as
> > well),
> >
> > I have heard here and there, inheriting from STL container is a "bad idea",
> > but I don't specifically know all the reasons why. I am not an expert in
> > this area (heck or any part of C++ for that matter). Isn't it only bad if
> > someone tries to delete the object using a base class pointer?
>
> That's one reason, yes.
>
> (btw, this is Item 35 in Sutter and Alexandrescu's C++ Coding
> Standards)

At the risk of going against the entrenched dogma here, I'd suggest there are
significant cases where this is a good and useful thing to do. I think the
original guidance came from Scott Meyers Effective C++ #14 -- make destructors
virtual in base classes. The problem I have with this is that I find there
are plenty of situations where I might want to add some convenience functions
to an stl container in which the subclass has a trivial destructor (eg: no
allocated resources) and as such there is no issue with deriving from
container. In all cases I know the base class destructor is called and hence
anything the container allocates will be deallocated.

> It's generally a bad idea to inherit from concrete classes that weren't
> designed to be inherited from, not just STL containers.

Well again, to me there are other cases in the design of concrete classes
where there is some advantage in inheriting from a base class with a
non-virtual destructor. For example in date-time some time duration classes
are derive from a base class with no virtual destructor. These subclasses are
simply a constructor the adjust for precision. So for example:

class time_duration {
  time_duration(int hour, int minute, int second, int millisecond)
   {
     //calc duration in terms of millisconds
   }
   //gobs of other complex stuff like operator+, operator-, etc
private:
  int total_milliseconds;
};

Nothing wrong with this, but there's lots of places where writing constructing
one of these time_durations is just ugly. For example, suppose I want to add
10 seconds to 10 milliseconds:

//ugly code....
time_duration td = time_duration(0,0,10,0) + time_duration(0,0,0,10);

So a few simple conversion classes help our cause:

class seconds : public time_duration
{
   seconds(int s) : time_duration(0,0,s,0) {}
};

class milliseconds : public time_duration
{
   milliseconds(int s) : time_duration(0,0,0,s) {}
};

Now we can write:

//code with obvious meaning
time_duration td = seconds(10) + milliseconds(10);

Again, we've derived from a class where we don't want virtual functions, we do
want to substitite the base class, and it is totally safe because of no
allocation / trivial destructor in the sub-class.

> In fact, it's generally a bad idea to use public inheritance to model
> anything except substitutablity. Inheritance creates one of the
> strngest coupling between two classes you can have, and reducing
> coupling is usually a good idea.
>
> If the class wasn't intended to be a base class it probably doesn't have
> any protected members, only public and private. If that's the case
> you don't gain anything by inheriting from it, except the
> convenience of not havng to declare forwarding functions for all the
> members - but that's laziness, not good design.

I'd say there is something about writing less code (not laziness) that makes
it a better design. Having to forward or reimplement the whole interface of
an stl container is non-trivial and probably a bad design.

> By inheriting you _do_ get implicit conversions to the base class, which
> may not be intentional and may not be safe:
>
> void f(const container& c)
> {
> container copy(c);
> // ...
> }
>
> void f(MyCustomContainer& c)
> {
> // ...
> }
>
> const MyCustomContainer c;
> f(c);
>
> you probably intended to call the overload taking MyCustomContainer
> here, but because it requires a const you can't - but instead of an
> error you get a conversion to base reference, and then the object
> gets sliced.

Which may be just fine -- again, it mostly depends on what the subclass does
not the base. So if you are careful...

> ... snip...
> If you want to inherit from, say, vector, do you _really_ want _all_
> its public member functions?

Yes, I frequently do.

Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk