Inheritance: The is-a relation

Steven Zeil

Last modified: Feb 19, 2014

Contents:
1. Generalization & Specialization
1.1 Inheritance
1.2 Subtyping
2. Inheritance & Subtyping in C++
2.1 Inheritance Example - Values in a Spreadsheet
3. Overriding Functions
4. Example: Inheritance and Expressions

1. Generalization & Specialization

In an earlier lesson, we introduced the idea of a generalization/specialization relationship between classes.


Specialization Example

For example, a check and a deposit are actually specializations of the more general concept of a “transaction”.

If I were to assert, therefore, that every transaction has a date and an amount, we would understand that the same is true for Checks and Deposits.

Or, from the interface point of view, if I were to assert that every Transaction has a function member apply that takes a Balance as its only parameter, then Checks and Deposits must support the same operation.

1.1 Inheritance

In programming languages, generalization is denoted by inheritance and/or subtyping.

A class C inherits from class D if C has all the data members and messages of D.

D is called a base class of C.


Inheritance Example


Inheritance Example


Multiple Inheritance

Inheriting from multiple base classes is called multiple inheritance.

It’s reasonably common in domain and analysis models, but designers often try to remove it before they get to the stage of coding. That’s because multiple inheritance can lead to complications.

It’s pretty obvious that the TA’s name is not changed depending on whether they are doing teacher stuff or student stuff at the time. But it’s not hard to imagine that some Universities might use distinct ranges of numbers for teachers than for students, requiring a TA to actually have two different numbers. And, how would you indicate your preference for inheriting one or two copies of a data member in a programming language.

It gets a bit messy, but it can be done in C++. On the other hand, Java disallows multiple inheritance as an unnecessary complication (partly because, as we will see, Java’s interface construct let’s us achieve much of the same flexibility with fewer complications.

1.2 Subtyping

A closely related idea:

D is called a superclass or supertype of C.


What’s the Difference?


Effects of Subtyping and Inheritance

applyToBal.cpp


C++ Combines Inheritance & Subtyping

In most OOPLs, including C++, inheritance and subtyping are combined.

That makes the distinction between inheritance and subtyping moot in C++.

The same does not hold, however, of Java, where only the first two of the four above statements are true.

2. Inheritance & Subtyping in C++

The construct

class C : public Super {

indicates that

2.1 Inheritance Example - Values in a Spreadsheet

cell.h


Values

The interface for values is

value.h


Numeric Values

Numeric values hold numbers.

numvalue.h

 void foo (NumericValue nv) {
     cout << nv.render(8) << endl;
 }
 
void foo (Value v; NumericValue nv) {
    if (v == nv) {
       ⋮


String Values

String values hold numbers.

strvalue.h


Error Values

Error values store no data at all, but are used as placeholders in a cell whose calculations have failed for some reason.

errvalue.h

3. Overriding Functions

When a subclass inherits a function member, it may


Overriding: Declaring Your Intentions


class A {
public:
  void foo();
  void bar();
  void baz();
};

class B: public A {
public:
  void foo();       ➊
  void bar(int k);  ➋
  void bar() const; ➌
};                  ➍


  • B declares that it will override A::foo(). B inherits the declaration of foo() but will provide its own body.

  • This does not override A::bar().
    • Changing parameters overloads a function, but (access to) the original function is unaffected.
  • This does not override A::bar(), either. It also overloads it with different parameter types.
    • The implicit parameter this is a const B* instead of an A*.
  • Because B did not override A::bar() or A::baz(), it inherits those declarations and their bodies from A.


Access to Functions

B b;
b.foo();     // calls B::foo()
b.A::foo();  // calls the original A::foo()

void B::foo()
{
  A::foo();
  doSomethingExtra();
}


Example of Overriding

As an example of overriding, consider these four classes, which form a small inheritance hierarchy.

animalOv.cpp

Note that several of the inheriting classes override one or both functions in their base class.

Question: Now, suppose we run the following code. What will be printed by each of the show calls?

 Animal a;
 Carnivore c;
 Herbivore h;
 Ruminant r;
 show(a.name(), a.eats());        // AHRC fpgm
 show(c.name(), c.eats());        // AHRC fpgm
 show(h.name(), h.eats());        // AHRC fpgm
 show(r.name(), r.eats());        // AHRC fpgm

(Try to work this out for yourself before looking ahead.)

Answer:


Inheritance and Encapsulation

An inheriting class does not get access to private data members of its base class:

class Counter {
   int count;
public:
   Counter() {count = 0;}
   void increment() {++count;}
   int getC() const {return count;}
};

class HoursCounter: public Counter {
public:
   void increment() {
     counter = (counter + 1) % 24; // Error!
   }
};


Protected Members

Data members marked as protected are accessible to inheriting classes but private to all other classes.

class Counter {
protected:
   int count;
public:
   Counter() {count = 0;}
   void increment() {++count;}
   int getC() const {return count;}
};

class HoursCounter: public Counter {
public:
   void increment() {
     counter = (counter + 1) % 24; // OK
   }
};

4. Example: Inheritance and Expressions

Inheritance also plays a part in the spreadsheet in taking care of Expressions


Expression Trees


Expression Inheritance Hierarchy

We would expect the lower-level classes like PlusNode and TimesNode to override the evaluate function to do addition, multiplication, etc., or whatever it is that distinctively identifies that particular “kind” of expression from all the other possibilities.