Inheritance (part 1)

Introduction
- To introduce the concept of inheritance, let’s look at an example

- Consider two software developers working for a company creating computer games

- The company is developing a racing game with a variety of different vehicles

- Each developer is asked to design a C++ class to describe a different type of vehicle

- The first developer designs a class for a plane and the second a class for an automobile

- The initial designs for each of the racing vehicles look something like the following

 

Plane

Auto

class Plane

{

// What it knows

private:

  float speed;          // current speed

  float distance;       // distance traveled

  int numWings;         // number of wings

  float propAngle;      // angle of propellor

 

// What it does

public:

  Plane()

  {

    distance = speed = 0.0;

    numWings = 1;

    propAngle = 0.0;

  }

 

  // Compute time of travel

  float computeDuration()

  {

    return ( distance / speed );

  }

 

  // Move the propellor

  void MovePropellor( float angle )

  {

    propAngle += angle;

  }

 

  // Access methods

  float getSpeed() { return speed; }

  void setSpeed( float s ) { speed=s; }

  float getDistance() { return distance; }

  void setDistance( float d ) { distance=d; }

  int getNumWings() { return numWings; }

  void setNumWings( int w ) { numWings=w; }

  float getPropAngle() { return propAngle; }

  void setPropAngle( float a ) { propAngle=a; }

};

class Auto

{

// What it knows

private:

  float speed;          // current speed

  float distance;       // distance traveled

  float tireRadius;     // size of tires

  float airPressure;    // tire pressure

 

// What it does

public:

  Auto()

  {

    distance = speed = 0.0;

    tireRadius = 15.0;

    airPressure = 30.0;

  }

 

  // Compute time of travel

  float computeDuration()

  {

    return ( distance / speed );

  }

 

  // Inflate tires

  void InflatTires( float pressure )

  {

    airPressure += pressure;

  }

 

  // Access methods

  float getSpeed() { return speed; }

  void setSpeed( float s ) { speed=s; }

  float getDistance() { return distance; }

  void setDistance( float d ) { distance=d; }

  float getRadius() { return tireRadius; }

  void setRadius( float r ) { tireRadius=r; }

  float getPressure() { return airPressure; }

  void setPressure( float a ) { airPressure=a; }

};

 

- Looking closer at the designs, we notice that each class has some code in common

- The designs also contain code that is unique and specific to each individual class

- We highlight the code that is common to each class in red below

 

Plane

Vehicle

class Plane

{

// What it knows

private:

  float speed;          // current speed

  float distance;       // distance traveled

  int numWings;         // number of wings

  float propAngle;      // angle of propellor

 

// What it does

public:

  Plane()

  {

    distance = speed = 0.0;

    numWings = 1;

    propAngle = 0.0;

  }

 

  // Compute time of travel

  float computeDuration()

  {

    return ( distance / speed );

  }

 

  // Move the propellor

  void MovePropellor( float angle )

  {

    propAngle += angle;

  }

 

  // Access methods

  float getSpeed() { return speed; }

  void setSpeed( float s ) { speed=s; }

  float getDistance() { return distance; }

  void setDistance( float d ) { distance=d; }

  int getNumWings() { return numWings; }

  void setNumWings( int w ) { numWings=w; }

  float getPropAngle() { return propAngle; }

  void setPropAngle( float a ) { propAngle=a; }

};

class Auto

{

// What it knows

private:

  float speed;          // current speed

  float distance;       // distance traveled

  float tireRadius;     // size of tires

  float airPressure;    // tire pressure

 

// What it does

public:

  Auto()

  {

    distance = speed = 0.0;

    tireRadius = 15.0;

    airPressure = 30.0;

  }

 

  // Compute time of travel

  float computeDuration()

  {

    return ( distance / speed );

  }

 

  // Inflate tires

  void InflatTires( float pressure )

  {

    airPressure += pressure;

  }

 

  // Access methods

  float getSpeed() { return speed; }

  void setSpeed( float s ) { speed=s; }

  float getDistance() { return distance; }

  void setDistance( float d ) { distance=d; }

  float getRadius() { return tireRadius; }

  void setRadius( float r ) { tireRadius=r; }

  float getPressure() { return airPressure; }

  void setPressure( float a ) { airPressure=a; }

};

 

- Rather than duplicating code, we can share what is common in each of the classes

- We would also want to retain code that is unique to each of the classes

- To accomplish this, we can design a general Vehicle class to describe what is common

- Each vehicle will be derived from the common class and contain only what is unique

- In this way, a relationship is formed between classes to describe different types of vehicles

- This is the concept behind the object-oriented feature known as inheritance

 



Definition
- Distinguishes object-oriented programming from traditional programming

- Provides a natural and intuitive technique for organizing classes
- Leverages the notion of abstraction (identify common attributes among classes)
- Provides a facility for sharing similarities yet maintaining uniqueness
- As an analogy, consider children inheriting parents' physical traits
- Children resemble their parents yet have their own unique traits
- Inheritance allows us to design classes in a similar manner

- Inheritance allows us to extend and reuse existing code without having to rewrite code
- Provides the ability to derive new classes from existing classes
- Derived class incorporates all the features (data + methods) of existing class
- Derived class inherits the features and can add features of its own
- Some inherited features are shared while others can become changed

 

 

 

Terminology
- Classes with common code to share are termed base or parent classes or a superclass
- Classes that inherit code are termed derived or child classes or a subclass



 

Advantages

 

Programming
- Expressing similarities among related classes avoids repetition of code
- Establishes foundation for future derived classes

 

Design
- Attempts to represent in software a model of the real world
- Reflect existence and organization of abstractions in problem domain

 

Software engineering
- Long lifetime, undergoes many changes and extensions during existence
- Extensibility: degree to which software can be modified to add new features

 

 

 

Class Derivation Syntax

 

class Child : public Parent { ... }

 

Parent      Base class

Child       Derived class

{ ... }     Derived class can inherit from multiple base classes

:                      Indicates derivation from one or more previously defined classes

public      Keyword indicating visibility of base class members in derived classes

class Child : public Parent
"Publicly derived class"
    - Treat all public members of the base as being public in derived class
    - Private base class members remain private
    - Most commonly used form of derivation

 

class Child : private Parent
"Privately derived class"
    - All base class members are treated as being private in derived class
    - True even if some of the members are public in the base class
    - Not as common - used to selectively indicate public members of base class

 

 

 

Base and Derived Classes

- Let’s return to our racing vehicle designs to demonstrate inheritance

- We can create a base Vehicle class to describe what is common to both classes

- We can then derive specific types of vehicles from our base Vehicle class

- The members in our base class can be used as if they were part of the derived classes
- In this way, derived classes inherit members from the base class
- No new code required to implement derived class except for those operations that...
        1) extend (or add to) the members inherited from the base class
        2) replace the members inherited from the base class

- We’ll begin by taking what is common in our example to design a base class

- Note that we only include state variables and methods that describe a general vehicle

 

 

class Vehicle

{

// What it knows

private:

  float speed;          // current speed

  float distance;       // distance traveled

 

// What it does

public:

  Vehicle()

  {

    distance = speed = 0.0;

  }

 

  // Compute time of travel

  float computeDuration()

  {

    return ( distance / speed );

  }

 

  // Access methods

  float getSpeed() { return speed; }

  void setSpeed( float s ) { speed=s; }

  float getDistance() { return distance; }

  void setDistance( float d ) { distance=d; }

};

 

 

- We can now derive specific types of vehicles from this base class

- We have a vehicle class describing general attributes (an abstraction)
- General attributes include both data (internal information) and behavior (methods)
- We'd like to design specific types of vehicles (i.e. planes, autos)
- Many attributes of the specific have already been described in the general class
- This is an ideal case to use inheritance (use general class to create more specific class)

- A Plane and an Auto are a specific type of Vehicle

- Note that we need to include the base header file in each derived header

 

 

#include “Vehicle.h”

 

class Plane : public Vehicle

{

// What it knows

private:

  int numWings;         // number of wings

  float propAngle;      // angle of propellor

 

// What it does

public:

  Plane()

  {

    numWings = 1;

    propAngle = 0.0;

  }

 

  // Move the propellor

  void MovePropellor( float angle )

  {

    propAngle += angle;

  }

 

  // Access methods

  int getNumWings() { return numWings; }

  void setNumWings( int w ) { numWings=w; }

  float getPropAngle() { return propAngle; }

  void setPropAngle( float a ) { propAngle=a; }

};

 

 

#include “Vehicle.h”

 

class Auto : public Vehicle

{

// What it knows

private:

  float tireRadius;     // size of tires

  float airPressure;    // tire pressure

 

// What it does

public:

  Auto()

  {

    tireRadius = 15.0;

    airPressure = 30.0;

  }

 

  // Inflate tires

  void InflatTires( float pressure )

  {

    airPressure += pressure;

  }

 

  // Access methods

  float getRadius() { return tireRadius; }

  void setRadius( float r ) { tireRadius=r; }

  float getPressure() { return airPressure; }

  void setPressure( float a ) { airPressure=a; }

};

 


- Plane and Auto are "publicly derived" classes from the base class Vehicle
-
Plane and Auto objects can use Vehicle class public methods
- Publicly derived classes can directly access the base class members

- Note how derived objects access their members and base members in this example

 

#include "Plane.h"

#include "Auto.h"

#include <iostream>

 

using namespace std;

 

main()

{

  Plane p;                      // Plane

  Auto a;                       // Auto

 

  // Class methods

  p.setNumWings( 2 );

  p.setPropAngle( 90 );

 

  // Base methods

  p.setSpeed( 200 );

  p.setDistance( 400 );

  cout << "Duration of plane: " << p.computeDuration() << endl;

 

  // Class methods

  a.setRadius( 20 );

  a.setPressure( 40 );

 

  // Base methods

  a.setSpeed( 55 );

  a.setDistance( 400 );

  cout << "Duration of auto: " << a.computeDuration() << endl;

}

 

Output

Duration of plane: 2

Duration of auto: 7.27273

 

 

Class Hierarchy
- Base and child classes have a hierarchical relationship

- Such relationships are often designated with class hierarchy diagrams

- For example, our class hierarchy above can be represented as follows

 

 

- Derived classes can be base classes for further related derivations
- Deriving classes from other derived classes sets up a chain of classes
- Classes can have multiple children, which can also have children

- The result could be a rather complex class hierarchy with multiple levels
- A derived class inherits members from all classes proceeding up the chain(s)
- We might, for example, want to re-design our vehicle hierarchy to make it more general
- We might recognize that there are other types of vehicles with wheels in addition to an Auto

- Notice that when the term “type of” is used in design, it is an excellent candidate for inheritance

- Examples of wheeled vehicles might include bicycles, motorcycles, trucks, and automobiles

- Wheeled vehicles have wheels in common and road friction (that might affect the speed)

- From this class, we could then derive types of wheeled vehicles

- We might consider adding another level to our hierarchy as follows

 

 

- The design of the WheelVehicle class would derive from the Vehicle class as follows

 

#include "Vehicle.h"

 

class WheelVehicle : public Vehicle

{

// What it knows

private:

  int numWheels;        // number of wheels;

  float friction;       // measure of road friction

 

// What it does

public:

  WheelVehicle()

  {

    numWheels = 2;

    friction = 0.1;

  }

 

  // Effect of friction on speed

  void frictionEffect()

  {

    speed -= (numWheels * friction );

  }

 

  // Access methods

  int getNumWheels() { return numWheels; }

  void setNumWheels( int c ) { numWheels=c; }

  float getFriction() { return friction; }

  void setFriction(float f) { friction=f; }

};

 

- We would also want to change the derivation of our Auto class

 

#include “WheelVehicle.h”

 

class Auto : public WheelVehicle

...

 

- Note in the example below, the Auto object calls both WheelVehicle and Vehicle methods

 

#include "Auto.h"

#include <iostream>

 

using namespace std;

 

main()

{

  Auto a;

 

  // Class methods

  a.setRadius( 20 );

  a.setPressure( 40 );

 

  // WheelVehicle method

  a.setNumWheels( 4 );

 

  // Vehicle methods

  a.setSpeed( 55 );

  a.setDistance( 400 );

  cout << "Duration of auto: " << a.computeDuration() << endl;

}

 

 

- Upon compilation of this example, however, we get the following error

 

In file included from Auto.h:4,

                 from test.C:2:

Vehicle.h: In member function `void WheelVehicle::frictionEffect()':

Vehicle.h:8: error: `float Vehicle::speed' is private

WheelVehicle.h:24: error: within this context

 

- In order to analyze this error, we’ll look a new type of access privilege for classes

 

 

 

Protected members
- Let’s examine our classes in the above example a bit more closely

- Specifically, look at the access keyword for the state variables in Vehicle and WheelVehicle

 

class Vehicle

{

// What it knows

private:

  float speed;          // current speed

  float distance;       // distance traveled

 

 

class WheelVehicle : public Vehicle

{

// What it knows

private:

  int numWheels;        // number of wheels;

  float friction;       // measure of road friction

 

- As we’ve seen, any variable/method that is private can ONLY be used by the class itself

- Not even classes derived from the class have access to private information

- In some cases, however, it would make sense to grant access to derived classes

- We would still, however, want to restrict access to class users in this case

- A new keyword, protected, serves just this purpose

- protected members are accessible by derived classes only, but still restricted to class users

- This is the nature behind the compiler error above

- The WheelVehicle method, frictionEffect, is trying to access the Vehicle variable, speed

- Since this is private, is does not have access and the program will not compile

- When a class is an object of derivation (Vehicle), its state variables should be protected

- This is a good rule of thumb to use when designing class hierarchies

- Making Vehicle class information protected, our example compiles successfully

- Since WheelVehicle also has classes derived from it, we make its information protected as well

 

class Vehicle

{

// What it knows

protected:

  float speed;          // current speed

  float distance;       // distance traveled

 

 

class WheelVehicle : public Vehicle

{

// What it knows

protected:

  int numWheels;        // number of wheels;

  float friction;       // measure of road friction

 

 

- With private, public, protected, we have a variety of different access privileges

- We can summarize these levels with the chart below

- Consider the accessibility chart for base class members, derived members, and users of classes

 

Keyword

Base members

Derived members

Class users

private

*

 

 

protected

*

*

 

public

*

*

*

 

 

- Consider another example demonstrating the protected access keyword

- A class to compute averages (mu) is derived from a class to compute sums (sigma)

 

#include <iostream>

 

using namespace std;

 

// Class to compute sums

class sigma

{

// Note protected keyword

protected:

  float sum;

 

public:

  sigma() { sum = 0.0; }

  void input( float d );

  void print();

};

void sigma::input( float d )

{

  sum += d;

}

void sigma::print()

{

  cout << "The sume is: " << sum << endl;

}

 

// Derived from sigma, to compute averages

class mu : public sigma

{

private:

  int n;

 

public:

  mu() { n = 0; }

  void input( float d );

  void print();

};

void mu::input( float d )

{

  // Base method called

  sigma::input(d);

  n++;

}

void mu::print()

{

  // Base method called

  sigma::print();

  float avg = n ? sum / n : 0;

  cout << "The average is: " << avg << endl;

}

 

main()

{

  mu y;

  y.input(17.0);

  y.input(25.0);

  y.print();

} 

Output
The sum is: 42.00
The average is: 21.00

 

 

- Note the use of the protected keyword in the base class

- Note finally how derived classes can call base methods using the scoping operator

 

sigma::input(d);

sigma::print();