10.4 Introduction to Inheritance

One of the most powerful features of C++ is the use of derived classes. The word inheritance is just another name for the topic of derived classes. When we say that one class was derived from another class, we mean that the derived class was obtained from the other class by adding features. For example, suppose we define a class for vehicles that has member variables to record the vehicle’s number of wheels and maximum number of occupants. The class also has accessor and mutator functions. Imagine that we then define a class for automobiles that has member variables and functions just like the ones in the class of vehicles. In addition, our automobile class would have added member variables for such things as the amount of fuel in the fuel tank and the license plate number and would also have some added member functions. Instead of repeating the definitions of the member variables and functions of the class of vehicles within the class of automobiles, we could use C++’s inheritance mechanism and let the automobile class inherit all the member variables and functions of the class for vehicles.

Inheritance allows you to define a general class and then later define more specialized classes that add some new details to the existing general class. This saves work because the more specialized, or derived, class inherits all the properties of the general class and you, the programmer, need only program the new features. This section will first introduce the notion of inheritance and a derived class and then we briefly describe how to create your own derived classes. Details of inheritance are left to Chapter 15. It may take a while before you are completely comfortable with the idea of a derived class, but you easily can learn enough about derived classes to start using them in some simple, and very useful, ways.

Derived Classes

Consider the BankAccount class defined in Display 10.7. This class keeps track of an amount and interest rate for a bank account—fairly generic features that apply to any interest-bearing account. If we would like to implement more specific types of bank accounts, then there is a natural hierarchy for grouping the account types. Display 10.8 depicts a part of this hierarchical arrangement for bank accounts, checking accounts, money market accounts, savings accounts, and Certificate of Deposit (CD) accounts. In the hierarchy, BankAccount is the most general type of account; more specific types of accounts are shown underneath. An arrow points from a specific account type to a more general account type. In addition to representing different types of bank accounts, each box also corresponds to a class that we can implement in C++.

Display 10.8 A Class Hierarchy

Flowchart showing a class hierarchy. Arrows indicate a flow  of boxes from bottom to top, as follows: Money Market Account points to Checking Account which points to Bank Account. CD Account points to Savings Account which points to Bank Account.

For example, a checking account does everything a bank account can do (store an amount and interest rate) but in addition allows customers to make deposits and write checks. Similarly, a savings account does everything a bank account can do but in addition allows customers to make deposits and withdrawals. Unlike a checking account, a savings account may not allow customers to write checks. Since both checking accounts and savings accounts are types of bank accounts they are shown in Display 10.8 directly underneath the BankAccount class. When we say that some class A is a derived class of some other class B, it means that class A has all the features of class B but it also has added features. The convention for indicating this relationship in a diagram is to draw an an unfilled arrow from the specific to the more general class. For example, in Display 10.8 the CheckingAccount and SavingsAccount classes are derived classes of the BankAccount class.

In C++, some class A can be a derived class of some other class B, which in turn can be a derived class of some other class C, and so on. For example, a CD account is similar to a savings account except the funds and any accrued interest must not be withdrawn until after a “maturity” date. If the funds are withdrawn prior to the maturity date, then there is a penalty. Due to these restrictions, a CD account normally accrues interest at a higher rate than a savings account. In the hierarchy, this is shown by deriving CDAccount from SavingsAccount. Similarly, a money market account is a special type of checking account in which the customer normally has a limit on the number of checks that can be written, along with higher minimum balances, but pays a higher interest rate. In the hierarchy, this is shown by deriving MoneyMarketAccount from CheckingAccount.

Derived classes are often discussed using the metaphor of inheritance and family relationships. If class B is a derived class of class A, then class B is called a child of class A and class A is called a parent of class B. The parent class is also referred to as the base class. The derived class is said to inherit the member functions of its parent class. For example, every convertible inherits the fact that it has four wheels from the class of all automobiles. This is why the topic of derived classes is often called inheritance.

Defining Derived Classes

If we want to create a class to represent a savings account, we could start by making a copy of the BankAccount class and renaming it to SavingsAccount. We would need to add new public member functions to deposit and withdraw funds. While this approach would work, it would be very inefficient, because the SavingsAccount class would duplicate most of the functionality in the BankAccount class. Not only does this waste memory space, it also becomes more difficult to make modifications. For example, if we later decide to change the update( ) function to accrue interest daily instead of annually, then we would have two places to make the change: in the SavingsAccount class and also in the BankAccount class. These problems can be solved by defining the SavingsAccount class as a derived class of the BankAccount class. The SavingsAccount class then can share member variables and functions defined in the BankAccount class. We specify this relationship when defining the derived class by adding a colon followed by the keyword public and the name of the parent or base class:

An illustration shows a code segment of derived class.

Notice that we only defined functions and data that specifically relate to savings accounts, in this case, functions to deposit and withdraw money. We don’t need to redefine all of the variables and functions relating to bank accounts—such as storing the interest rate, dollars, cents, or defining the update( ) function—because those members will be inherited from the BankAccount class and are automatically created when we construct a SavingsAccount object. For example, if we create a SavingsAccount object, we could invoke the following functions:

An illustration shows:

In this example, inheritance allowed us to reuse code defined in the parent class from the context of the derived class. Moreover, if we later change one of BankAccount's functions—such as update( )—then the new code automatically will be used from the context of its derived classes when the program is recompiled and linked. An implementation of the SavingsAccount class along with a main function to test the deposit and withdraw functions is given in Display 10.9. For simplicity, we have left verification out of the deposit and withdraw functions, for example, checking for negative amounts, but you should be able to add them easily with some if statements.

Display 10.9 A SavingsAccount Derived Class

An illustration shows a code segment usin a derived class “SavingsAccount.”
An illustration shows the continued part of the code segment usin a derived class “SavingsAccount.”

Screen Output

Account balance $100.50
Interest rate 5.50%
Depositing $10.25.
Account balance $110.75
Interest rate 5.50%
Withdrawing $11.80.
Account balance $98.95
Interest rate 5.50%

Once the SavingsAccount class is defined we can go one step further and derive more specialized classes from the SavingsAccount. For example, to define the CD account class we need a new private member variable to store the days until maturity and define functions to access this variable:

class CDAccount : public SavingsAccount
{
public:
    CDAccount(int dollars, int cents, double rate,
              int daysToMaturity);
    <Other constructors would normally go here>
    int getDaysToMaturity( );
    //Returns the number of days until the CD matures
    void decrementDaysToMaturity( );
    //Subtracts one from the daysToMaturity variable
private:
    int daysToMaturity; //Days until the CD matures
};

Once again, we only defined functions and data that specifically relate to CD accounts, in this case, storing and manipulating the number of days to maturity. We don’t need to redefine all of the variables and functions relating to bank accounts or savings accounts because those members will be inherited from the parent classes. For example, once the functions in the CDAccount class are implemented, we could invoke the following functions from the CDAccount, SavingsAccount, or BankAccount classes given a CDAccount object:

This short example has only scratched the surface of what is possible using inheritance. Additional details are described in Chapter 15. While it does take some effort to learn how to effectively design classes using inheritance, the effort will pay off in the long run. You will end up writing less code that is easier to understand and maintain than code that does not use inheritance.

Self-Test Exercises

  1. How does inheritance support code reuse and make code easier to maintain?

  2. Can a derived class directly access by name a private member variable of the parent class?

  3. Suppose the class SportsCar is a derived class of a class Automobile. Suppose also that the class Automobile has public member functions named accelerate and addGas. Will an object of the class SportsCar have member functions named accelerate and addGas?