Boost logo

Boost :

From: Jonathan Wakely (cow_at_[hidden])
Date: 2005-02-25 13:26:22


On Fri, Feb 25, 2005 at 12:45:34PM -0500, christopher diggins wrote:

> ----- Original Message -----
> From: "Daniel James" <daniel_at_[hidden]>
> To: <boost_at_[hidden]>
> Sent: Friday, February 25, 2005 11:16 AM
> Subject: [boost] Re: Querying usefulness of a macro for inheriting from
> STLcontainers
>
>
> >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? Could we not

That's one reason, yes.

(btw, this is Item 35 in Sutter and Alexandrescu's C++ Coding Standards)

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

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.

One of my favourite C++ quotes is:

    [Inlines] are the third most-misused C++ feature
    (after inheritance and overloading).
            - Nathan Myers

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.

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.

> prevent that idiom by writing type-casts which force failure upon a pointer
> cast to the inherited type?
>
> For instance:
>
> public MyCustomContainer : container {
> operator container*() {
> throw exception("can not cast to container*");
> }
> ...
> }
>
> Doesn't this solve the virtual destructor problem?

I don't see how:

    container* mcc = new MyCustomContainer;
    delete mcc;

No chance for a conversion operator to be invoked here, you never even
dereference the pointer, but you invoke undefined behaviour because you
don't invoke the right destructor.

> Another approach perhaps would be to create inheritable versions of the STL
> containers which are thin wrappers which simply provide a virtual
> destructor. Would this not work as well?

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

> However what are the other problems of type-safety, slicing(?), etc. to
> which you allude? I am unfamiliar with them and I am not sure how to go
> about researching the topic.

Slicing refers to copying the base part of a class only, as in the
example above. You pass in a MyCustomContainer, but then copy only a
slice of it (the base part).

jon

--
"You know how dumb the average guy is?
 Well, mathematically, by definition,
 half of them are even dumber than that."
	- J.R. "Bob" Dobbs

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