CS105: Introduction to Computer Programming: C++

C++: Inheritance and Polymorphism

The most common form of inheritance in C++ is public inheritance:

/**
 * Vehicle is the base (or parent) class
 */
class Vehicle {
public:
  Vehicle() {
    cout << "Vehicle()" << endl;
  }
  float getPrice() const {
    return 10000;
  }
};

/**
 * Car is a sub-class (or child class) of Vehicle
 */
class Car : public Vehicle {
public:
  Car() {
    cout << "Car()" << endl;
  }
  float getPrice() const {
    return 13000;
  }
};
There is also private and protected inheritance, but they are not frequently used.

A child class can call function in one of its parent classes like this:

  class Car : public Vehicle {
    ...
    // a different getPrice() function for the Car class
    float getPrice() const {
      return Vehicle::getPrice() + 3000;
    }

Using Virtual Functions

You can deal with objects polymorphically in C++. For example, here we have a pointer to a Vehicle that actually points to an instance of the Car class:

  Vehicle* v = new Car();
  cout << v->getPrice() << endl;

In the above example, a Java programmer might expect that Car's getPrice method would be called. However, in C++, the variable v is a pointer to a Vehicle, so Vehicle's getPrice method is called.

In order to get "the right" function to be called, you must use the virtual keyword:

/**
 * Vehicle is the base (or parent) class
 */
class Vehicle {
public:
  Vehicle() {
    cout << "Vehicle()" << endl;
  }
  virtual float getPrice() const {
    return 10000;
  }
};

/**
 * Car is a sub-class (or child class) of Vehicle
 */
class Car : public Vehicle {
public:
  Car() {
    cout << "Car()" << endl;
  }
  virtual float getPrice() const {
    return 13000;
  }
};

Since the getPrice function has been declared virtual, any polymorphic use of the getPrice function will result in the "right" getPrice function being called. In the above example, Car's getPrice would be called instead of Vehicle's.

Once you declare a class with virtual methods, you should make the destructor virtual as well. This ensures that when the object is destroyed, the "right" destructor for the object is called.

Pure Virtual Functions

Another way to specify functions is to make them "pure virtual", using the virtual keyword and appending = 0 to the end of the function declaration. A pure virtual function isn't defined in the current class -- it is simply a contract that requires any sub-classes of the current class that want to be instantiated should implment that function. In the next example, the Vehicle class leaves it up to the Car class to actually implement the getPrice function:

/**
 * Vehicle is the base (or parent) class
 */
class Vehicle {
public:
  Vehicle() {
    cout << "Vehicle()" << endl;
  }
  virtal float getPrice() const = 0;
};

/**
 * Car is a sub-class (or child class) of Vehicle
 */
class Car : public Vehicle {
public:
  Car() {
    cout << "Car()" << endl;
  }
  virtual float getPrice() const {
    return 13000;
  }
};

When a class contains one or more pure virtual functions, the class is "abstract". Abstract classes cannot be instantiated. In the above example, we can no longer create a new Vehicle(). A class with only pure virtual methods is said to be an "interface".

The concepts of abstract classes and interfaces are similar to what you'd find in Java.