A derived class can inherit interface and/or implementation from a base class. Compared to a hierarchy designed for implementation inheritance, one designed for interface inheritance tends to have its functionality lower in the hierarchy—a base class signifies one or more functions that should be defined by each class in the hierarchy, but the individual derived classes provide their own implementations of the function(s). The inheritance hierarchy designed for the ATM system takes advantage of this type of inheritance, which provides the ATM
with an elegant way to execute all transactions “in the general.” Each class derived from Transaction
inherits some implementation details (e.g., data member accountNumber
), but the primary benefit of incorporating inheritance into our system is that the derived classes share a common interface (e.g., pure virtual
member function execute
). The ATM
can aim a Transaction
pointer at any transaction, and when the ATM
invokes execute
through this pointer, the version of execute
appropriate to that transaction (i.e., the version implemented in that derived class’s .cpp
file) runs automatically. For example, suppose a user chooses to perform a balance inquiry. The ATM
aims a Transaction
pointer at a new object of class BalanceInquiry
; the compiler allows this because a BalanceInquiry
is a Transaction
. When the ATM
uses this pointer to invoke execute
, BalanceInquiry
’s version of execute
is called.
This polymorphic approach also makes the system easily extensible. Should we wish to create a new transaction type (e.g., funds transfer or bill payment), we would just create an additional Transaction
derived class that overrides the execute
member function with a version appropriate for the new transaction type. We would need to make only minimal changes to the system code to allow users to choose the new transaction type from the main menu and for the ATM
to instantiate and execute objects of the new derived class. The ATM
could execute transactions of the new type using the current code, because it executes all transactions identically.
As you learned earlier in the chapter, an abstract class like Transaction
is one for which you never intend to instantiate objects. An abstract class simply declares common attributes and behaviors for its derived classes in an inheritance hierarchy. Class Transaction
defines the concept of what it means to be a transaction that has an account number and executes. You may wonder why we bother to include pure virtual
member function execute
in class Transaction
if execute
lacks a concrete implementation. Conceptually, we include this member function because it’s the defining behavior of all transactions—executing. Technically, we must include member function execute
in base class Transaction
so that the ATM
(or any other class) can polymorphically invoke each derived class’s overridden version of this function through a Transaction
pointer or reference.