Never, never, never do this. Never. Never!
Attempting to hide (eliminate, revoke, privatize) inherited public member functions is an all-too-common design error. It usually stems from muddy thinking.
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Because converting
C++ allows the conversion
In other words, if it were legal to convert
(BTW you'll need to use a pointer cast to get it to compile. Suggestion: try to compile it without a pointer cast to see what the compiler does. If you're really quiet when the error message appears on the screen, you should be able to hear the muffled voice of your compiler pleading with you, "Please don't use a pointer cast! Pointer casts prevent me from telling you about errors in your code, but they don't make your errors go away! Pointer casts are evil!" At least that's what my compiler says.)
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Nope.
I know it sounds strange, but it's true. You can think of this as a direct
consequence of the previous FAQ, or
you can reason it this way: if the kind-of relationship were valid, then
someone could point a parking-lot-of-Vehicle pointer at a
parking-lot-of-Car, which would allow someone to add any kind of
Vehicle to a parking-lot-of-Car (assuming parking-lot-of-Vehicle has a
member function like
Perhaps this will help: a container of Thing is not a kind-of container of Anything even if a Thing is a kind-of an Anything. Swallow hard; it's true.
You don't have to like it. But you do have to accept it.
One last example which we use in our OO/C++ training courses: "A Bag-of-Apple is not a kind-of Bag-of-Fruit." If a Bag-of-Apple could be passed as a Bag-of-Fruit, someone could put a Banana into the Bag, even though it is supposed to only contain Apples!
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Nope.
This is a corollary of the previous FAQ. Unfortunately this one can get you into a lot of hot water. Consider this:
The compiler thinks this is perfectly type-safe. Line 5 converts a
The root problem is that C++ can't distinguish between a pointer-to-a-thing and a pointer-to-an-array-of-things. Naturally C++ "inherited" this feature from C.
NOTE: If we had used an array-like class (e.g.,
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Yes, arrays are evil. (only half kidding).
Seriously, arrays are very closely related to pointers, and pointers are
notoriously difficult to deal with. But if you have a complete grasp of why
the above few FAQs were a problem from a design perspective (e.g., if you
really know why a container of Thing is not a kind-of container of
Anything), and if you think everyone else who will be maintaining your
code also has a full grasp on these OO design truths, then you should feel free
to use arrays. But if you're like most people, you should use a template
container class such as
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Depends. But not if Ellipse guarantees it can change its size asymmetrically.
For example, if Ellipse has a
This leaves two valid relationships between Circle and Ellipse:
In the first case, Ellipse could be derived from class AsymmetricShape, and
In the second case, class Oval could only have
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
(Note:
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
If you claim that all Ellipses can be squashed asymmetrically, and you
claim that Circle is a kind-of Ellipse, and you claim that
Circle can't be squashed asymmetrically, clearly you've got to revoke
one of your claims. You can get rid of
Here are the two most common traps new OO/C++ programmers regularly fall into.
They attempt to use coding hacks to cover up a broken design, e.g., they might
redefine
If it is important to you to retain the "Circle is a kind-of Ellipse"
inheritance relationship, you can weaken the promise made by Ellipse's
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
(Note:
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Actually, it doesn't mean any of these things. But I'll tell you what it does mean you may not like what I'm about to say: it means your intuitive notion of "kind of" is leading you to make bad inheritance decisions. Your tummy is lying to you about what good inheritance really means stop believing those lies.
Look, I have received and answered dozens of passionate e-mail messages about this subject. I have taught it hundreds of times to thousands of software professionals all over the place. I know it goes against your intuition. But trust me; your intuition is wrong, where "wrong" means "will cause you to make bad inheritance decisions in OO design/programming."
Here's how to make good inheritance decisions in OO design/programming:
recognize that the derived class objects must be substitutable for the
base class objects. That means objects of the derived class must
behave in a manner consistent with the promises made in the base
class' contract. Once you believe this, and I fully recognize that you might
not yet but you will if you work at it with an open mind, you'll see that
There are three ways to fix this problem:
Sorry, but there simply are no other choices.
You must make the base class weaker (weaken Ellipse to the point that it no longer guarantees you can set its width and height to different values), make the derived class stronger (empower a Circle with the ability to be both symmetric and, ahem, asymmetric), or admit that a Circle is not substitutable for Ellipse.
Important: there really are no other choices than the above three. In particular:
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
(Note: some people correctly point out that a constant Circle
is substitutable for a constant Ellipse. That's true, but
it's really not a fourth option: it's really just a special case of option #1,
since it works precisely because a constant Ellipse doesn't have a
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
If Circle is the base class and Ellipse is the derived class,
then you run into a whole new set of problems. For example, suppose
Circle has a
If you get over that hurdle, such as by having
Even if you get past that one, such as by having
Bottom line: you can make anything inherit from anything provided the methods in the derived class abide by the promises made in the base class. But you ought not to use inheritance just because you feel like it, or just because you want to get code reuse. You should use inheritance (a) only if the derived class's methods can abide by all the promises made in the base class, and (b) only if you don't think you'll confuse your users, and (c) only if there's something to be gained by using the inheritance some real, measurable improvement in time, money or risk.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Ahhh, there's the rub. You think the Circle/Ellipse example is just a silly example. But in reality, your problem is an isomorphism to that example.
I don't care what your inheritance problem is, but all yes all bad inheritances boil down to the Circle-is-not-a-kind-of-Ellipse example.
Here's why: Bad inheritances always have a base class with an extra capability (often an extra member function or two; sometimes an extra promise made by one or a combination of member functions) that a derived class can't satisfy. You've either got to make the base class weaker, make the derived class stronger, or eliminate the proposed inheritance relationship. I've seen lots and lots and lots of these bad inheritance proposals, and believe me, they all boil down to the Circle/Ellipse example.
Therefore, if you truly understand the Circle/Ellipse example, you'll be able to recognize bad inheritance everywhere. If you don't understand what's going on with the Circle/Ellipse problem, the chances are high that you'll make some very serious and very expensive inheritance mistakes.
Sad but true.
(Note: this FAQ has to do with public inheritance; private and protected inheritance are different.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
It's irrelevant that those terms are defined mathematically. That irrelevance is why "it depends."
The first step in any rational discussion is to define terms. In this case, the first step is to define the terms Circle and Ellipse. Believe it or not, most heated disagreements over whether class Circle should/shouldn't inherit from class Ellipse are caused by incompatible definitions of those terms.
The key insight is to forget mathematics and "the real world," and instead accept as final the only definitions that are relevant for answering the question: the classes themselves. Take Ellipse. You created a class with that name, so the one and only final arbiter of what you meant by that term is your class. People who try to mix "the real world" into the discussion get hopelessly confused, and often get into heated (and, sadly, meaningless) arguments.
Since so many people just don't get it, here's an example. Suppose your
program says
Simply put, when we are asking questions about words defined in your program, we must use your definitions of those terms, not Euclid's. That is why the ultimate answer to the question is "it depends." It depends because the answer to whether the thing your program calls Circle is properly substitutable for the thing your program calls Ellipse depends on exactly how your program defines those terms. It's ridiculous and misleading to use Euclid's definition when trying to answer questions about your classes in your program; we must use your definitions.
When someone gets heated about this, I always suggest changing the labels to terms that have no predetermined connotations, such as Foo and Bar. Since those terms do not evoke any mathematical relationships, people naturally go to the class definition to find out exactly what the programmer had in mind. But as soon as we rename the class from Foo to Circle, some people suddenly think they can control the meaning of the term; they're wrong and silly. The definition of the term is still spelled out exclusively by the class itself, not by any outside entity.
Next insight: inheritance means "is substitutable for." It does not mean "is a" (since that is ill defined) and it does not mean "is a kind of" (also ill defined). Substitutability is well defined: to be substitutable, the derived class is allowed (not required) to add (not remove) public methods, and for each public method inherited from the base class, the derived class is allowed (not required) to weaken preconditions and/or strengthen postconditions (not the other way around). Further the derived class is allowed to have completely different constructors, static methods, and non-public methods.
Back to Ellipse and Circle: if you define the term Ellipse to mean something that can be resized asymmetrically (e.g., its methods let you change the width and height independently and guarantee that the width and height will actually change to the specified values), then that is the final, precise definition of the term Ellipse. If you define the thing called Circle as something that cannot be resized asymmetrically, then that is also your prerogative, and it is the final, precise definition of the term Circle. If you defined those terms in that way, then obviously the thing you called Circle is not substitutable for the thing you called Ellipse, therefore the inheritance would be improper. QED.
So the answer is always "it depends." In particular, it depends on the behaviors of the base and derived classes. It does not depend on the name of the base and derived classes, since those are arbitrary labels. (I'm not advocating sloppy names; I am, however, saying that you must not use your intuitive connotation of a name to assume you know what a class does. A class does what it does, not what you think it ought to do based on its name.)
It bothers (some) people that the thing you called Circle might not be substitutable for the thing you called Ellipse, and to those people I have only two things to say: (a) get over it, and (b) change the labels of the classes if that makes you feel more comfortable. For example, rename Ellipse to ThingThatCanBeResizedAssymetrically and Circle to ThingThatCannotBeResizedAssymetrically.
Unfortunately I honestly believe that people who feel better after renaming the things are missing the point. The point is this: in OO, a thing is defined by how it behaves, not by the label used to name it. Obviously it's important to choose good names, but even so, the name chosen does not define the thing. The definition of the thing is specified by the public methods, including the contracts (preconditions and postconditions) of those methods. Inheritance is proper or improper based on the classes' behaviors, not their names.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Probably not.
The most important insight is that the answer depends on the details of the base class's contract. It is not enough to know that the public interfaces / method signatures are compatible; one also needs to know if the contracts / behaviors are compatible.
The important part of the previous sentence are the words "contracts /
behaviors." That phrase goes well beyond the public interface = method
signatures = method names and parameter types and constness. A
method's contract means its advertised behavior = advertised requirements and
promises = advertised preconditions and postconditions. So if the base class
has a method
The other important word is advertised. The intention here is to
differentiate between the code inside the method (assuming the base class's
method has code; i.e., assuming it's not an unimplemented pure virtual
function) and the promises made outside the method. This is where things get
tricky. Suppose
The derived class must do what the base class promises, not what it actually does.
The key is that we've separated the advertised behavior ("specification") from implemented behavior ("implementation"), and we rely on the specification rather than the implementation. This is very important because in a large percentage of the cases the base class's method is an unimplemented pure virtual the only thing that can be relied on is the specification there simply is no implementation on which to rely.
Back to SortedList and List: it seems likely that List has one or more methods that have contracts which guarantee order, and therefore SortedList is probably not a kind-of List. For example, if List has a method that lets you reorder things, prepend things, append things, or change the ith element, and if those methods make the typical advertised promise, then SortedList would need to violate that advertised behavior and the inheritance would be improper. But it all depends on what the base class advertises on the base class's contract.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised Mar 1, 2006