Reliable Software Logo

C++ In Action: Language


The Meaning of is-a

The fact that a Star is a CelestialBody means not only that a Star has all the member variables and methods of a CelestialBody (and more). After all that would be just syntactic sugar over straight embedding. One could embed a CelestialBody as a public member of Star and, apart from awkward semantics of the has-a relationship, one could imitate inheritance. (As a matter of fact, this is probably how your compiler implements inheritance.)

There is however one big difference. Due to inheritance, a Star can pass as a CelestialBody. What does it mean? It means that a function that expects a reference or a pointer to a CelestialBody will happily accept a reference or a pointer to a Star. Here’s an example: The class BlackHole has a method Gobble that accepts any CelestialBody

void BlackHole::Gobble (CelestialBody * pBody);

Since a Star is a CelestialBody, it can be Gobbled by a BlackHole:

Star* pStar = new Star (1, 2);
TheBlackHole.Gobble (pStar);  // Yumm

How is the CelestialBody treated inside a BlackHole?

void BlackHole::Gobble (CelestialBody* pBody)
    delete pBody;

It is destroyed. Now the big question is: whose destructor is called? On the one hand, we know that we sent a Star to its fiery death, so we should hear "Destroying a star ..." On the other hand, the BlackHole has no idea that it is swallowing a Star. It will expect to hear "Destroying celestial body..." What’s more, at compile time, the compiler has no idea what will be sent into the BlackHole. Imagine a situation like this:

Star * pStar = new Star (1, 2);
Planet * pPlanet = new Planet (3, 4);

TheBlackHole.Gobble (pStar);  // Yumm
TheBlackHole.Gobble (pPlanet);  // Yumm, yumm

In both cases the same method Gobble is called, the same code is executed, therefore it is obvious that inside Gobble the compiler may only put the call to (or expand inline) CelestialBody’s destructor. The compiler dispatches the call based on the type of the pointer. Notice that the same would apply to any other method of CelestialBody overridden by Star or Planet. If Gobble called any of these, the compiler would call the CelestialBody’s implementation and not the one provided by Star or Planet.

This solution is cheap and in many cases adequate. However, for a small additional fee, C++ can offer a very powerful feature called polymorphism. If you want to hear different final cries from the insides of a BlackHole depending on what has fallen into it, you must declare the CelestialBody’s destructor virtual. When a virtual function is overridden in a derived class, the dispatching of calls to that function is done by actual type of the object pointed to, rather than by the type of the pointer.

NextNext: Parse Tree