Inheritance, Virtual Functions, and Late Binding

Tom Kelliher, CS23

Apr. 7, 1997

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);

   cout << "Working with check:\n";
   check.inputTrans(DEPOSIT, 100.00);
   check.printBalance();
   check.inputTrans(WITHDRAWAL, 50.00);
   check.printBalance();

   cout << "\nWorking with joint:\n";
   joint.printSsn();
   joint.inputTrans(DEPOSIT, 39.00);
   joint.printBalance();

   cout << "\nEverything about check:\n";
   check.printEverything();   // Static binding.

   cout << "\nEverything about joint:\n";
   joint.printEverything();   // Static binding.

   return 0;
}

Virtual Functions

Consider this simple example:

/**********************************************************************
 * virtual.cc
 * Tom Kelliher
 *
 * This program demonstrates several advanced features of classes:
 *    o Public inheritance and the IS-A relationship.
 *    o Static re-definition of class methods.
 *    o Virtual functions and run-time re-definition of class methods.
 *      (Polymorphism)  ("Turn the way you want to turn.")
 **********************************************************************/


#include <iostream.h>


/**********************************************************************
 * class base
 **********************************************************************/

class base
{
 public:

   // Compile-time binding.
   void print1(void) { cout << "print1() of base class\n"; }

   // Run-time binding.
   virtual void print2(void) { cout << "print2() of base class\n"; }

   // Demonstrate "default" method feature of a base class.
   virtual void print3(void) { cout << "print3() of base class\n"; }
};


/**********************************************************************
 * class derived1
 **********************************************************************/

class derived1 : public base   // derived1 IS-A base.
                              // Try AS-A inheritance.
{
 public:
   void print1(void) { cout << "print1() of derived1 class\n"; }
   void print2(void) { cout << "print2() of derived1 class\n"; }

   // Note lack of re-definition of print3().
};


/**********************************************************************
 * class derived2
 **********************************************************************/

class derived2 : public base   // derived2 IS-A base.
{
 public:
   void print1(void) { cout << "print1() of derived2 class\n"; }
   void print2(void) { cout << "print2() of derived2 class\n"; }
   void print3(void) { cout << "print3() of derived2 class\n"; }
};


/**********************************************************************
 * main()
 **********************************************************************/

int main()
{
   base b;
   base *p = &b;
   derived1 d1;

   // Question: can q point to b or d2?
   derived1 *q = &d1;

   derived2 d2;

   cout << "The \"normal\" calls:\n";
   b.print1();
   b.print2();
   b.print3();
   d1.print1();
   d1.print2();
   d1.print3();
   d2.print1();
   d2.print2();
   d2.print3();

   cout << "\nWorking on instance b of base class through base*:\n";
   p->print1();
   p->print2();
   p->print3();

   cout << "\nWorking on instance d1 of derived1 class through base*:\n";
   p = &d1;
   p->print1();

   // Can I "fix" things this way?
//   p->derived1::print1();
   p->print2();
   p->print3();

   cout << "\nWorking on instance d1 of derived1 class through derived1*:\n";
   q->print1();
   q->print2();
   q->print3();

   // Will these work?
//   q = &b;
//   q = &d2;

   cout << "\nWorking on instance d2 of derived2 class through base*:\n";
   p = &d2;
   p->print1();
   p->print2();
   p->print3();

   return 0;
}



Thomas P. Kelliher
Sun Apr 6 15:26:35 EDT 1997
Tom Kelliher