ECE 161: Programming Languages

Fall 2004

Monday 3:00 - 4:00 Rm 343, Friday 2:00 - 4:00 Rm 343

Click here for main page of course...

Topic #10: Derived Classes, Inheritance, and Polymorphism

Derived classes allow us to represent hierarchical relations between classes. For example, triangles and circles are types of shapes, and some employees are also managers. These examples have been used in our lectures to explain the benefits of derived classes. A class that is derived from another is declared something like this:

class Derived:public Base {
...
}

In this case, we are defining a class named "Derived" which is derived from a class named "Base". The derived class is also called a subclass, and the base class is also called a superclass (which might seem a bit confusing, since the derived class is actually a superset of the base class). The use of the keyword "public" after the ":" in the class declaration of the derived class will be discussed shortly.

A derived class is said to "inherit" properties of the base class, and this functionality is called "inheritance". What this means is that member variables and member functions of the base class are also part of the derived class. However, not all member variables and member functions of the base class can be "seen" by the derived class. Private members of the base class can not be seen by the derived class. The reason is that if the opposite were true, any programmer could gain access to private members of a provided class (e.g. from a library) by simple declaring their own class to be derived from that class. However, a member variable or function can be declared to be "protected" instead of public or private, which means it is private (and can not be seen) to things outside the class, but public (and thus can be seen) to derived classes.

The use of "public" after the ":" in the class definition of a derived class is called an access specifier, which means that public members in the base remain public, and protected members in the base remain protected. If protected or private is used as the access specifier, it means that this becomes the new minimum level of protection for the derived class. For example, if protected is used as the access specifier, then public members in the base become protected. If private is used, then everything becomes private, and can not be seen in classes derived from this one. It is most common to use "public" access specifiers in class definitions of derived classes.

A derived class can also be a base class. That is to say, you can define yet another class which is derived from a class that is derived from another class. Multiple classes can be derived from the same class; e.g. classes for many different shapes can all be derived from a class for shapes in general. Unlike several other languages including Java, C++ also allows a class to be derived from multiple base classes; this is called multiple inheritance. It can become quite confusing, and it is debatable whether or not it is a good thing to include this capability in the language to begin with.

If a base class includes a required constructor, then the derived class is also required to have a constructor which calls the constructor of the base class. Basically, this call sets up the part of the object that is common to the base class and derived class, and then the rest of the constructor for the derived class can set up the rest of the object. The syntax used to do this sort of thing has been explained in our lectures.

An object of a derived class is also considered an object of the base class, and can be assigned to an object declared that way. For example, if Manager is derived from Employee (since a manager is a type of employee), a Manager object can be assigned to an Employee object; only the common part of the class is copied. More importantly, a variable assigned to be a pointer to the base class can point to an object of the derived class. So, for example, a vector of pointers to Employee objects may include pointers to regular Employee objects and also pointers to Manager objects.

If one or more member functions of the base class is defined as "virtual", then "polymorphism" takes effect. Such a class is called a polymorphic type, and all classes derived from it are also polymorphic. What this means is that the compiler adds overhead to every object of the base class and all derived classes of the base; this overhead keeps track of what type of object we are dealing with. This is useful in the case that a pointer to the base class is used to access a virtual member function; the compiler adds code to the executable that looks at the overhead of the object to determine what is the actual type of the object at run-time, and it assures that the proper version of the member function will be called. That is to say, if the object is actually an object of a derived class for which the virtual member function has been redefined (overloaded), this more specific version of the member function will be called; otherwise, the member function of the base class will be called. We also looked at a method of specifying that the member function of the base class should be called regardless.

A pure virtual function is a virtual function that has been made pure be defining it to be zero in the class definition. A class with one or more pure virtual functions is called an abstract class. No objects can be instantiated (declared) for such a class! All derived classes must define these functions, or they will also be abstract classes. Using abstract classes makes sense when there is a notion shared by many types of objects, but all objects will be of one of the subtypes. The common example we discussed in our lectures is that of shapes. If you are writing some sort of graphical drawing program, you may want to define a type called Shape that defines all of the things that different shapes share in common, but you may not want to allow a generic Shape object to be declared. Classes for actual shapes (circles, rectangles, triangles, etc.) must redefine any pure virtual functions for themselves, and they may also, of course, add any member variables or member functions that are specific to the shape; meanwhile, they can share the common member variables and member functions that are defined in the base.