Inheritance, Virtual Functions, and Late Binding

Tom Kelliher, CS23

Mar. 27, 1996

Terminology:

Inheritance --- A Banking Example

checking.h

/**********************************************************************
 * checking.h --- Class declaration for a simple checking account.
 * Tom Kelliher
 *
 * A Checking object consists of an account number and balance.
 * Allowed operations:
 *    Constructor: Create a checking account with a given account
 *       number and balance.
 *    inputTrans: Apply a transaction (from enum TransactionType) to
 *       the account.
 *    printBalance: Print the account number and balance.
 *    printEverything: print all information associated with an
 *       account.
 *
 * This class is a base class for a joint checking class.
 **********************************************************************/


#ifndef __CHECKING_H
#define __CHECKING_H


// Types of allowed transaction.
enum TransactionType { DEPOSIT = 'D', WITHDRAWAL = 'W' };


class Checking
{
 private:
   int accntNumber;
   double accntBalance;

 public:
   Checking(int number, double balance);
   void inputTrans(TransactionType type, double amount);
   void printBalance(void);
   void printEverything(void);
};


#endif

checking.cc

#include <iostream.h>
#include <iomanip.h>
#include "checking.h"


// These methods are documented in checking.h


/**********************************************************************
 * Checking::Checking
 **********************************************************************/

Checking::Checking(int number, double balance)
: accntNumber(number), accntBalance(balance)
{
}


/**********************************************************************
 * Checking::inputTrans
 **********************************************************************/

void Checking::inputTrans(TransactionType type, double amount)
{
   switch (type)
   {
    case DEPOSIT:
      accntBalance += amount;
      break;

    case WITHDRAWAL:
      accntBalance -= amount;
      break;

    default:
      cout << "Illegal transaction code: " << char(type) << endl;
      break;
   }
}


/**********************************************************************
 * Checking::printBalance
 **********************************************************************/

void Checking::printBalance(void)
{
   // This method twiddles with output formatting.  We need to save
   // the output formatting and then restore it before returning.
   long oldPrecision;
   long oldFixed;

   cout << "The balance for account " << accntNumber << " is ";

   // Save previous formatting.
   oldPrecision = cout.precision(2);
   oldFixed = cout.setf(ios::fixed, ios::floatfield);

   cout << accntBalance << ".\n";

   // Restore previous formatting.
   cout.precision(oldPrecision);
   cout.setf(oldFixed, ios::floatfield);
}


/**********************************************************************
 * checking::printEverything --- just call printBalance which, for
 * this class, prints everything.
 **********************************************************************/

void Checking::printEverything(void)
{
   printBalance();
}

joint.h

/**********************************************************************
 * joint.h --- Class declaration for a simple joint checking account.
 * Tom Kelliher
 *
 * A Joint object consists of a Checking base class with appendant
 * owner and joint owner SSNs.
 * Allowed operations:
 *    Constructor: Create a joint checking account with owner and
 *       joint owner SSNs, an account number, and an account balance.
 *    printSsn: Print the SSNs of the two owner.
 *    printEverything: print all information associated with the
 *       account.
 *
 * Public inheritance of the Checking class is used, so see checking.h
 * for other allowed operations.
 **********************************************************************/


#ifndef __JOINT_H
#define __JOINT_H


// Read header file for base class.
#include "checking.h"


class Joint : public Checking   // Checking is inherited with public
                                // access.
{
 private:
   long ownerSsn;
   long jointOwnerSsn;

 public:
   Joint(long oSsn, long jSsn, int number, double balance);
   void printSsn(void);
   void printEverything(void);   // Static override of 
                                 //Checking::printEverything
};

#endif

joint.cc

#include <iostream.h>
#include "joint.h"


// These methods are documented in joint.h


/**********************************************************************
 * Joint::Joint
 **********************************************************************/

Joint::Joint(long oSsn, long jSsn, int number, double balance)
: Checking(number, balance),   // Invoke constructor for base class.
ownerSsn(oSsn), jointOwnerSsn(jSsn)
{
}


/**********************************************************************
 * Joint::printSsn
 **********************************************************************/

void Joint::printSsn(void)
{
   cout << "Owner SSN: " << ownerSsn << endl;
   cout << "Joint SSN: " << jointOwnerSsn << endl;
}


/**********************************************************************
 * Joint::printEverything --- Prints all information associated with
 * the account.
 **********************************************************************/

void Joint::printEverything()
{
   printSsn();
   Checking::printEverything();   // Why not just printEverything()?
}

A Simple Driver Program

#include <iostream.h>
#include "checking.h"
#include "joint.h"

int main()
{
   Checking check(569248, 1000.00);
   Joint joint(111223333, 444556666, 987234, 500.00);

   check.inputTrans(DEPOSIT, 100.00);
   check.printBalance();
   check.inputTrans(WITHDRAWAL, 50.00);
   check.printBalance();

   cout << endl;

   joint.printSsn();
   joint.inputTrans(DEPOSIT, 39.00);
   joint.printBalance();

   cout << endl;

   check.printEverything();   // Static binding.

   cout << endl;

   joint.printEverything();   // Static binding.

   return 0;
}

Driver Output

Virtual Functions

Consider a linked list of checking accounts:

...
List<Checking> accounts;

accounts.insert(new Checking(...));
accounts.insert(new Joint(...));
accounts.display();
...


template <class T> void List<T>::display(void)
{
   listnode<T>* tmp = head;

   while (tmp != NULL)
   {
      tmp->printEverything();   // Pointing to Checking or Joint?
      cout << endl;
   }
}

Which printEverything() should be called?

Cannot be determined until runtime.

Communicated to compiler by virtual attribute of printEverything() in base class
( Checking).

Abstract Base Classes

A class with at least one pure virtual function, that is, a function which is not defined. Such a class cannot be instantiated.

Why allow such a thing?

Example:

#include <iostream.h>

class twoInts
{
 protected:
   int first;
   int second;

 public:
   twoInts(int a, int b) : first(a), second(b) {}

   virtual void display(void) = 0;

   // ...
};


class rational : public twoInts
{
   // ...

 public:
   rational(int a, int b) : twoInts(a, b) {}

   void display(void)
   {
      cout << first << "/" << second;
   }

   // ...
};


int main()
{
   rational rat = rational(1, 3);

   rat.display();
   cout << endl;

   return 0;
}



Thomas P. Kelliher
Tue Mar 26 11:59:29 EST 1996
Tom Kelliher