Phlip said:
Kai-Uwe Bux wrote:
[snip]
You claimed: "you should not inherit from a
class with no virtual methods".
And C++ is multi-paradigm, so within the template paradigm, you should not
inherit except to specialize a member.
The fourier example did not do that. Generally, when inheriting from
unary_function you will not specialize any of the members of
unary_function.
Your example "overrode" operator(). I am aware it was not virtual, and
might not exist in the parent.
You are using the term "override" strangely. In my book you cannot override
something that isn't there to begin with. I think the standard uses the
word in the way I do. BTW, since you say operator() "might" not exist in
the parent: There is no doubt as to what exists in std::unary_function.
It's defined by the standard, and operator() is not a member.
Yet what you did was very similar to an override, from the caller's point
of view.
I see, you generalized your position to: one should not inherit unless one
overrides something (virtual or not) or adds something (which is what you
call overriding with quotation marks). In other words, one should not do
class X : public y {
...
}
unless X behaves at least a little different from Y. Well, I might grant you
that. However, In my eyes,
a) your advice has moved quote a bit from the original and
b) is more or less vacuous now.
I think you are trying to rephrase a piece of sound advice from the OO
paradigm in very abstract terms in order to make it applicable in other
contexts and styles of programming. I think you are carrying this to far
and it would be better to just qualify the original advice roughly like so:
when programming the OO style, you should rarely have a need for inheriting
from a class unless that class has virtual methods that you want to
override in derived classes. In that case, it is good policy to make the
destructor virtual.
Templates are another form of polymorphism, such that a call to an object
with a method, such as foo->bar(), or foo->operator(), will dispatch to
any number of different implementations.
This does not work in the fourier class example: if you have a pointer
unary_function<double,double>* f_ptr;
a call f_ptr->operator() will not compile. Also, your use of pointers and
your focus on the operator() indicates that you may have misunderstood the
point of the fourier class inheriting from std::unary_function. You will
almost never have a pointer of type unary_function<double,double>* or even
of type fourier*. These classes are meant to have value semantics. The main
purpose of doing
class fourier : public std::unary_function<double,double> {
unsigned long frequency;
public:
fourier ( unsigned long f )
: frequency ( f )
{}
double operator() ( double x ) const {
return ( std::sin( x * frequency ) );
}
};
instead of
class fourier {
unsigned long frequency;
public:
fourier ( unsigned long f )
: frequency ( f )
{}
double operator() ( double x ) const {
return ( std::sin( x * frequency ) );
}
};
is not to do anything about operator(). It's just so that you can do
something like:
fourier s3 ( 3 );
std::transform( v.begin(), v.end(),
std:
stream_iterator< fourier::result_type >( std::cout, " " ),
s3 );
The gain for maintainance is that you avoid magic types as in
std::transform( v.begin(), v.end(),
std:
stream_iterator< double >( std::cout, " " ),
s3 );
(and of course, there are some other uses of the result and argument types
as pertaining to template programming).
Similar remarks apply to inheritance from std::iterator. Here the main
purpose is to ease template deduction, e.g., based upon the iterator_tag so
that specialized version of algorithms can be chosen for random access
iterators. This is not really a polymorphism thing as the specialized
algorithms are mere optimizations of the generic versions obeying the same
semantics. Again, no overriding of members of the std::iterator class will
take place in the client code.
To use the word polymorphism as though these techniques are parallel to the
use of virtual methods, is misleading at best.
A class that exists only to provide a minor tweak to an existing class,
regardless where or how an override occurs. Brainless example: Inherit
std::string, and add a method ToUTF_16(). This overrides nothing
(syntactically or semantically), it simply makes the string class easier
to use in an application that likes UTF-16. I'm also not saying one should
do that. Only do it if it's convenient.
Thanks. Note that the fourier class from my example is no such thing.
This great post by Tim Ottinger says it best:
----8<-------------------------------
No, though it ought to be. We're trying to make it so. But in most shops
where I've worked before OMA, most shops my friends work at, many
places where we're brought in to teach, and the majority of shops where
other teachers I know teach, abstraction is not understood in the light of
dependency management and risk management. It's just a way of taking a
wild swag at the structure of a system.
Inheritance is typically used too much, and usually wrongly. It's most
frequently used to denote inappropriate dynamic set memberships, create
inappropriate couplings of orthogonal aspects of objects, or even worse as
"compilable comments".
If most programmers are taught how to do this in Intro To Programming 101,
then 101 needs a serious reworking. For details of how people typically do
this incorrectly, read "What Every Programmer Should Know About OOD",
chapters 8-12.
...
Tim
----8<-------------------------------
He is talking about high-end shops using Big Design Up Front to draw lots
of diagrams of their domain objects, and then linking these by
inheritance, such that Truck inherits Vehicle, regardless if your software
is for a carwash, a skateboard designer, or a wind-tunnel simulator. Then
Truck might not override any methods in Vehicle, and nobody cares. The
code matches the design, so progress is being made!
I see. However, these issues seem to be unrelated to the points that I
raised about policy based design where your original advice "one should not
inherit from classes that have no virtual methods" simply does not apply and
where your modified advice "one should not inherit without
overriding/specializing members" is equally misguided.
Best
Kai-Uwe Bux