C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 21:
[21.11] How could "it depend"??!? Aren't terms like "Circle" and "Ellipse" defined mathematically?

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 class Foo : public Bar { ... }. This defines what you mean by the term Foo: the one, final, unambiguous, precise definition of Foo is given by unioning the public parts of Foo with the public parts of its base class, Bar. Now suppose you decide to rename Bar to Ellipse and Foo to Circle. This means that you (yes you; not "mathematics"; not "history"; not "precedence" nor Euclid nor Euler nor any other famous mathematician; little old you) have defined the meaning of the term Circle within your program. If you defined it in a way that didn't correspond to people's intuitive notion of circles, then you probably should have chosen a better label for your class, but nonetheless your definition is the one, final, unambiguous, precise definition of the term Circle in your program. If somebody else outside your program defines the same term differently, that other definition is irrelevant to questions about your program, even if the "somebody else" is Euclid. Within your program, you define the terms, and the term Circle is defined by your class named Circle.

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.