Class Summary Notes

Abstract Base Classes

an abstract base class is a class where one, or more, of its methods are “= 0”, i.e. have no implementation

  • it’s possible that an abstract base class could have some methods that are not = 0
  • a base class is meant to have other classes inherit from it
  • an = 0 method is also known as a pure virtual method

for example:

class Stringable {
public:
  virtual string to_str() const = 0;
  virtual ~Stringable() { }
};

class Student :: public Stringable {
  // ...

public:
  // ...
  string to_str() const {
    return "<string representation of this object>";
  }
}

abstract base classes they are used to specify what methods an object must saying how they must be implemented

they are an example of interface inheritance, i.e. you can only inherit the header (signature) of methods from an abstract base class (there is no body to inherit)

see below for how interface inheritance contrasts with implementation inheritance

you cannot create objects of an abstract base class type (because they have unimplemented methods), but you can create pointers to objects of the abstract base class type, e.g. you cannot create a Stringable object, but you can create a pointer of type Stringable* that points to a Student object

always remember to include a virtual destructor so that inheriting classes are able to implement their own destructors if they need to

it’s possible (and useful) for one class to inherit from more than one abstract base class; this is known as multiple inheritance

  • however, we will not be using any multiple inheritance in this course

Implementation Inheritance

you can also inherit from classes that are not abstract base classes

i.e. you can inherit from a class that has an implemented method

this can sometimes be useful in practice, but multiple inheritance with classes that have implemented methods is more complicated than for abstract base classes

we will mostly avoid implementation inheritance in this course

Class Hierarchies and Class Diagrams

in big object-oriented programs, it is common to have multiple classes related to each other via inheritance

this is known as a class hierarchy, and is often represented using a class diagram where classes are in rectangles and arrows are used to indicate inheritance

designing good class hierarchies is surprisingly tricky, and requires careful thought (and experience)

Class Hierarchies

suppose you have a class called Circle that represents circles, and a class called Ellipse that represents ellipses

should Circle inherit from Ellipse, or should Ellipse inherit from Circle?

some programmers say Circle should inherit from Ellipse because, mathematically, a circle is an ellipse, i.e. the set of circles is a subset of the set of ellipses

  • but this can lead to serious problems!

  • if Ellipse has methods set_height(h) and set_width(w), then we can write code like this:

    Circle c(10);  // a circle of radius 10
    
    c.set_width(5);
    c.set_height(15);  // ???: a circle's width and height can't be different
    

some programmers say Ellipse should inherit from Circle because a Circle stores one radius, and an Ellipse stores two radii, and so Ellipse is extending Circle by adding one more radius

  • but this can also lead to serious problems!

  • consider this function:

    double area(Circle c) {
      return 3.14 * c.radius() * c.radius();
    }
    
  • since an Ellipse inherits from Circle, we can write code like this:

    Ellipse e(5, 10);  // an ellipse 5 wide, 10 high
    cout << area(c);   // this can't return the correct area!
    
  • the area function only checks one radius, but to correctly calculate the area of an Ellipse you need two radii

so it seems that Circle and Ellipse have no good way to be related by inheritance at all!

both ways of doing the inheritance can lead to serious problems