C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 21:
[21.8] But I have a Ph.D. in Mathematics, and I'm sure a Circle is a kind of an Ellipse! Does this mean Marshall Cline is stupid? Or that C++ is stupid? Or that OO is stupid?

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 setSize(x,y) violates this substitutability.

There are three ways to fix this problem:

  1. Soften the promises made by setSize(x,y) in base class Ellipse, or perhaps remove that method completely, at the risk of breaking existing code that calls setSize(x,y).
  2. Strengthen the promises made by setSize(x,y) in the derived class Circle, which really means allowing a Circle to have a different height than width — an asymmetrical circle; hmmm.
  3. Drop the inheritance relationship, possibly getting rid of class Circle completely (in which case circleness would simply be a temporary state of an Ellipse rather than a permanent constraint on the object).

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:

  1. PLEASE don't write me and tell me that a fourth option is to derive both Circle and Ellipse from a third common base class. That's not a fourth solution. That's just a repackaging of solution #3: it works precisely because it removes the inheritance relationship between Circle and Ellipse.
  2. PLEASE don't write me and tell me that a fourth option is to prevent users from changing the dimensions of an "Ellipse." That is not a fourth solution. That's just a repackaging of solution #1: it works precisely because it removes that guarantee that setSize(x,y) actually sets the width and height.
  3. PLEASE don't write me and tell me that you've decided one of these three is "the best" solution. Doing that would show you had missed the whole point of this FAQ, specifically that bad inheritance is subtle but fortunately you have three (not one; not two; but three) possible ways to dig yourself out. So when you run into bad inheritance, please try all three of these techniques and select the best, perhaps "least bad," of the three. Don't throw out two of these tools ahead of time: try them all.

(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 setSize(x,y) method.)