Re: Deriving from concrete types
Alan McKenney wrote:
However, one of C++'s strengths is that it supports
multiple paradigms. And one real non-OOD use of
derivation is code re-use, where there is no expectation
of polymorphic use.
The OP's example is, to my mind, a perfect example
of derivation for code re-use: a new class which is
similar to an existing class.
I disagree. It's more like the perfect example of a misapplication of a
feature. I'm surprised nobody mentioned the problems of this approach.
If somebody wants a new class that is similar to an existing class, they
simply add functions taking an object of that class as an argument. That
way, all code that *already* manipulates objects of the existing class
has presto access to the newly-defined functionality without any change
to the existing code - no changes of local variables' types, no changes
to members' types, no changes to function signatures. This is the
baseline against which you want to compare the public inheritance approach.
Now: If, on the other hand, somebody defines cool_vector<T> to inherit
vector<T>, they'll face two problems. One is that, in order to banefit
of the newly-added functionality, they'll have to change an unbounded
amount of the existing code. For example, if you had a function:
void Fun(const vector<T> & v);
now if you wanted Fun to use cool_vector<T>'s functionality, you need to
either (a) change the signature and the code around Foo's call sites,
(b) construct a cool_vector from the vector inside Foo, or (c) cast v to
cool_vector<T>&. The first option is impractical, the second
inefficient, and the third incorrect.
The second problem is that whatever extra invariants the derived class
would like to hold (either about the vector itself or between the vector
and whatever extra member variables cool_vector might hold), that won't
be possible. Vector<T> doesn't have any virtual functions, so anybody
extracting the vector<T> & from a cool_vector<T> can use it in any way a
vector<T> could be used.
With these considerations in mind, if you want to add new functionality
to an existing class, you add it as free functions if no add of state is
neededed, or via private inheritantce if you need to add state and
maintain some invariant involving the base class and the extra state. If
you just want to add unsynchronized state, make that a separate class or
use pair.
Public inheritance for symbol injection is usable in many template
programming scenarios, the simplest being std::*_function.
By using public inheritance, the new class definition
contains only those things which distinguish it from
the existing class, thus making it clear to anyone
who reads it what is going on.
I have no doubt reading the new class should be easy enough. But many
simple things have messy implications. In this case, they are: extensive
and gratuitous changes to existing code, and inability to maintain
invariants for the new class.
(Yes, you can use private inheritance, but then
you have to put in a "using" declaration for every
base class function and operator.
Free functions are the baseline you may want to compare other approaches
against.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]