[1100] in java-interest
Re: Assertions in Java (& more)
daemon@ATHENA.MIT.EDU (jim frost)
Fri Aug 18 18:26:02 1995
To: John Barton <jjb@watson.ibm.com>
Cc: "John D. Mitchell" <johnm@CSUA.Berkeley.EDU>, anders@in.otc.com.au,
java-interest@java.sun.com
In-Reply-To: Your message of "Fri, 18 Aug 1995 08:58:29 EDT."
<9508181258.AA30168@jjb.watson.ibm.com>
Date: Fri, 18 Aug 1995 11:23:35 -0400
From: jim frost <jimf@world.std.com>
|John D. Mitchells writes:
|>MI, as it's is in e.g., C++ and Eiffel, blends the notion of the
|>inheritance of the implementation with the inheritance of the API (or just
|>plain 'interface'). This intertwinedness causes a number of painful
|>problems. By splitting those two concepts apart we can gain a lot back in
|>terms of performance, understandability, precision of expression, etc.
|
| This statement as applied to C++ is false. C++ supports derivation from
|interface base classes containing no implementation. C++ supports
|derivation from implementation base classes with or without interfaces.
|C++ supports arbitrary combinations of these kinds of classes.
|"Intertwinedness" is a property of code you may have read, not a property
|of C++; splitting interface from implementation is a standard programming
|practice in C++.
Intertwinedness does have unexpected problems. Consider:
class Foo {
public:
void Add(int);
};
class Bar : public Foo {
public:
void Add(double);
};
What is intended, and what would appear to be the case to an
inexperienced C++ programmer, is to inherit Foo's implementation and
extend its interface in Bar. But that's not what happens.
It was considered unsafe to allow overloading of inherited member
functions because it's too easy to accidentally overload them. Rather
than give an error message when this was done, which would have made
sense, C++ instead implicitly hides the conflicting member functions
in the superclass. Foo::Add() must be referenced explicitly:
Bar b;
b.Foo::Add(0);
or you must add a wrapper to Bar:
class Bar : public Foo {
public:
inline void Add(int i) { Foo::Add(i); }
void Add(double);
};
What's worse, this also applies to overloaded *virtual* functions, at
least in all of the compilers I tried (CFront and MSVC++). If you
override one inherited overloaded member function you must overload
all of them, making this a performance issue as well as an
ease-of-implementation issue.
It could be argued that the easiest way to avoid this problem is to
avoid method overloading -- which I do, although it's still all too
easy to accidentally overload something.
A related, but far more serious and subtle, problem is accidentally
overloading a virtual member function rather than overriding it:
class Foo {
public:
virtual void Add(unsigned int);
};
class Bar : public Foo {
public:
void Add(int);
};
The omission of "unsigned" hides Foo::Add(unsigned int). At best you
*might* get a signed/unsigned mismatch conflict from the compiler when
Bar::Add() is called -- but probably not. What I have gotten in cases
like this is some very hard-to-find bugs.
This particular mistake is *incredibly* easy to make -- so easy, in
fact, that I wish for "override" and "overload" keywords. "Override"
would indicate that you intend to override an inherited function (and
would trigger an error if there is no inherited function), and
"overload" would indicate deliberate overloading of an inherited
member function. By making these intentions explicit the compiler
could pick up mistakes like these as early as possible.
I've been using C++ in real-world projects long enough that I've run
into a lot of such problems. C++ gives you a loaded gun with no
safety switch, and it's oh-so-easy to shoot yourself in the head.
jim frost
jimf@world.std.com
-
Note to Sun employees: this is an EXTERNAL mailing list!
Info: send 'help' to java-interest-request@java.sun.com