C++ (was RE: Python suitability)

Alex Martelli alex at magenta.com
Wed Dec 15 19:03:18 EST 1999


Boudewijn Rempt writes:

> > Specifically, if you follow the advice of Scott Meyers, in his
> > excellent "Effective C++" (CD Edition): never inherit from a
> > concrete class.
> 
> That makes C++ about as powerful as Visual Basic - in essence,
> no inheritance at all, just interfaces... Having just done a

No inheritance of implementation, yep -- not between
components, at least (how you split up one single
component is really a different issue than how separate
components interact -- concrete-class inheritance has
other problems, of course, but such tricks as "the
pattern without a name", depending on templates
as well as inheritance, are another kettle of fish).

C++ is just as powerful as VB (or Python, or machine
code) _today_ -- they're basically all Turing complete
in abstract terms, subject only to physical machines'
limitations.

C++ offers a lots of features that are there for various,
mostly historical, reasons -- e.g., just about all would
agree that public data members are a Bad Thing, yet
C++ lets you exposes them if you wish to; _protected_
data members are a feature Stroustrup considers to
have been a great mistake to put into the language, the
one he would take away if he could run history backwards.
Etc, etc.


> large project in Visual Basic, I've learnt how painfult that
> limitation can be - and I was kind of surprised when I read
> in Design Patterns that composition should be favoured over
> inheritance. There are no doubt good reasons, but not one
> I can think of.

Maybe Lakos, "Large Scale C++ Software Design", could
help you see why -- he's talking mainly about interaction
_between_ components, where the case is sharpest.

But basically, and reductively: inheritance of implementation
introduces a larger degree of coupling than one would like,
and strong dependencies.  Containment and delegation, aka
'composition', is a more 'normal' situation for client software,
easier to control.

The key benefit of inheritance, i.e. polymorphism (in a strictly
typed language) can be obtained by inheriting from abstract
classes, aka interfaces, only; indeed, it can be obtained
better.  Consider, in a C++ kind of world...:

Suppose function F takes a reference to an object of class C.
How is it relevant for F, what data items (not public!-) C has?
It's irrelevant implementation detail -- and so are methods
that are protected or private.  All F cares about is what it
can "legally" do with C, i.e. its public interface -- possibly,
other public interfaces the object might expose (as could
be tested for via dynamic_cast, etc).

Therefore, why would you ever want to design F to accept
a pointer to a NON-abstract class?  What extra benefit
would this non-abstractness provide?  (Some performance
through inlining etc, perhaps -- the kind of optimization
that should be left to a late, tuning stage for a design, and
only performed on truly performance-critical spots... a
tiny fraction of a whole design, hopefully:-).

So, interfaces are the base of the design.  When a class
is implementing an interface (which in C++ is expressed
by inheritance -- Java is cleverer, having an "implements"
keyword, but I suspect you dislike that since VB also
uses it:-) then you have some instruments for that
implementation.  You can do it with the least coupling
by containment and delegation -- but it takes lots of
boilerplate, because C++ lacks a smooth way to express
this key idiom (Python's ability to automate delegation
through the overloading of __getattr__ is almost a dream
come true, but, I digress:-).  What you can through
concrete inheritance is merely a modest reduction in
boilerplate (and possibly a modest performance gain).


A point has been made on this thread about the wonders
of prototyping, and I'll be the last to disagree with that.

But, the way I like to prototype is: sketch the key items
(the interfaces); slap up skeleton implementations of 
them; try writing the client code; refine the interface
design, iterate.  More and more these days many of
the interfaces are expressed in COM -- in a good part,
because this lets me prototype them faster (e.g., in
Python -- *bliss*!-) _and_ test more thoroughly that
their functionality is actually what is needed (since
the client code can also be coded in a powerful and
fluid language...).  When the time comes to move to
C++ implementations for some of the things (pretty
early, in some cases, e.g. things that are "known" will
be performance-critical, or involve certain low level
system interactions -- although I guess that, as I learn
Python better, the second half of this may decrease),
I tend to have the needed stub/skeleton collection or
"infrastructure".  And code reuse can often be gotten
through suitable templates ("generic programming")
rather than through inheritance of implementation...


Alex






More information about the Python-list mailing list