CS 218 - Object Oriented Programming with C++

Chapter 13 - Inheritance and Composition

Objectives:

This chapter introduces the student to inheritance. Objectives important to this chapter:

  1. What inheritance is
  2. Derived and base classes
  3. Redefining member functions
  4. Constructors
  5. Principles of Object Oriented Design
Concepts:

The text begins with a simple rule of thumb to recognize the basic points of inheritance and composition:

  • inheritance creates an "is-a" relationship (is a print() funtion, is an input() function, etc.)
  • composition creates a "has-a" relationship (has this kind of function, or has this kind of variable member)

A new class for your program,can be designed to be a variation on an existing class. When you do this, the new class will inherit the characteristics of the base class, the class that the new one is based on. In other words, it will have all the same members of the base class, plus whatever you add for the new, derived class.

If a derived class is based on more than one base class, the derived class has multiple inheritance (it gets properties from both parents). If a derived class has only one base class, it has single inheritance.

A class that is derived from another can be declared like this:

class derivedClassName: specifier baseClassName
{
};

The position marked by specifier in the example above can be left blank, or can be filled by one of these reserved words: public, private, or protected. If no specifier is used, the default specifier is private.

  • It is assumed that your base class may have public, protected, and private members. Private members of the base class are are not accessible by members of the derived class, no matter which specifier is used. (So you access them with the double colon method and a member of the base class.)
  • If you use public as your specifier, public members of the base class are inherited as public members of the derived class, protected members of the base class are inherited as protected members of the derived class, and private members of the base class are hidden in the dervived class.
  • If you use protected as your specifier, public members of the base class are inherited as protected members of the derived class, protected members of the base class are inherited as protected members of the derived class, and private members of the base class are hidden in the dervived class.
  • If you use private as your specifier, public members of the base class are inherited as private members of the derived class, protected members of the base class are inherited as private members of the derived class, and private members of the base class are hidden in the dervived class.
  • In general, if the base class has public or protected members, they will be accessible by members of the derived class.

In principle, private and protected members of a class cannot be accessed from outside the same class in which they are declared. However, this rule does not affect friends. (Some of you wanted to hear the song...)

Friends are functions or classes that have access to the private and protected members of a class. We declare a prototype of this external function within the class, and preceding it with the keyword friend.

It may be necessary to redefine functions in a derived class in order to process new variables that did not exist in a base class. The text describes redefining (overriding) a print function that printed out the value of variables in the base class so that it could print out more or different variables in a derived class.

The text makes a distinction between redefining and overloading:

  • redefining - rewriting a function in a derived class that existed in the base class so that it has the same name and receives the same parameter list.
  • overloading - rewriting a function so that it can receive a different parameter list in addition to the original parameter list. The parameter list passed to the function determines which version of the function runs.

We spent a lot of time in class discussing a simple concept. Let me try again here. It is a bad idea to load more than one copy of a header file. Doing so will result in declaring functions and variables a second time, which creates a compile-time error. To avoid doing this, you should place a preprocessor test at the beginning of each header file you write. It should be shaped like this:
#ifndef SPECIAL_WORD_FOR_THIS_FILE
#define SPECIAL_WORD_FOR_THIS_FILE

The content of your header file goes here.

#endif

This uses three preprocessor commands:

  • #ifndef means "if the following is not defined". It is followed by a special word that you should only use in THIS header file.
  • #define is followed by the special word for this file.
  • #endif is the end of the logical block of code.

The first time a header is loaded, the special word for that header is not yet defined in your program, so the preprocessor learns the word, and then learns everything else in the header file. If the header program tries to load the header file again, the preprocessor checks the special word, finds it already knows it, and skips the rest of the header file.

This is a simple error trap, but it depends on using a unique word for each header file. The author proposes H_NameOfFile. A suggestion online says to use NAMEOFFILE_H. Another source tells us that the stdlib.h file uses __STDLIB__. The way you make your special word does not matter: what matters is that it is never used for anything else in your programs.

The text discusses constructors with regard to whether they are with or without parameters. First, remember that default constructors have no parameters. Second, remember that the constuctor for a derived class should call the constructor for its base class. The text displays this in the ongoing example of boxType and rectangleType. This is the definition of the boxType constructor with parameters:
boxType::boxType(double l, double, w, double h) : rectangleType(l,w)
{ code goes here
};

This begins boxType::boxType(double l, double, w, double h). This means call the boxType constructor indirectly (because we are in a another file) and hand it three double values.
It continues with : rectangleType(l,w)
which means "but first, call the rectangleType constructor and hand it two values". (Why didn't we use the double colon notation? Because we were already calling the boxType constructor, and it has direct access to the constructor of its base class.)