You are on page 1of 10

CS108, Stanford Winter, 98-99

Handout #14 Nick Parlante

Inheritance Examples
Inheritance The Account and Instructor examples demonstrate the most interesting features of OOP programming with inheritance. These are both old CS107 problems of mine... Clever factoring With cleverness on the part of the programmer, common behavior can be factored from subclasses up to their superclasses to avoid ever repeating code. See the Instructor::GetMail() method for a good example of how clever factoring can be used to split behavior between the super and sub classes. Objects never forget their class No matter where the flow of control goes, the receiver object never forgets its true run-time class (so long as methods are declared virtual) Substitution Rule A pointer with the compile-time type of a superclass such as Student* (or Bear* or Account* or Instructor*) may, at run-time, point to an object which is of that class or any of its subclasses. Banking Problem Consider an object-oriented design for the following problem. You need to store information for bank accounts. For purposes of the problem, assume that you only need to store the current balance, and the total number of transactions for each account. The goal for the problem is to avoid duplicating code between the three types of account. An account needs to respond to the following messages:
Constructor or Init message Initialize a new account void Deposit(float amt) add the amt to the balance and increment the number of transactions void Withdraw(float amt) subtract the amt to the balance and increment the number of transactions float Balance(); return the current balance void EndMonth() the account will be sent this message once a month, so it should levy any monthly fees at that time and print out the account monthly summary

There are three types of account: Normal: deposit and withdraw just affect the balance. There is a $5.00 monthly fee which counts as a normal withdrawal transaction. Nickle 'n Dime: Each withdrawal generates a $0.50 fee. Both the withdrawal and the fee count as a transaction. The Gambler: A withdrawal returns the requested amount of money- however the amount deducted from the balance is as follows: there is a 0.49 probability that no money will actually

}. void EndMonthUtil().51 probability that twice the amount actually withdrawn will be subtracted. virtual void Withdraw (float amt). */ #pragma once class Account { public: Account(). virtual void Deposit (float amt). It does not have a full implementation of EndMonth() behavior which should be filled in by its subclasses. int transactions.h**** */ /* Account is an abstract super class with the default characteristics of a bank account. There is a 0. // force subclasses to implement protected: float balance.2 be subtracted from the balance. You may use an abstract super class or you may subclass some of the account from others. Propose some classes to store the idea of an account. virtual float GetBalance() const. . virtual void EndMonth() = 0. // save some code repetition in the subclasses // Static class function public: static void TestOneMonth(void). It maintains a balance and a current number of transactions. Where are the instance variables declared? Give the definitions of the withdraw: methods Where do you use overriding? How difficult would it be to extend your scheme so each account also stored its minimum monthly balance? What ramifications does this have for using (inherited withdraw) versus just changing the balance instance variable directly? /***** account.

}.3 //***** // Subclass headers // The Monthly Fee type of account class MonthlyFee : public Account { void EndMonth(). class NickleNDime : public Account { void EndMonth(). void EndMonth() { EndMonthUtil().0.h" Account::Account() { balance = 0. forcing everyone to go through these to change the balance. transactions++. } void Account::Deposit (float amt) { balance = balance + amt. we maintain the number of transactions automatically. } }. }.cp #include "Account. transactions = 0. // cheezy syntax to put def in . // The Nickle 'n' Dime type of account. } /* Deposit/Withdraw are a good example of a "bottleneck" accessor -. } . // The Gambler type of account class Gambler : public Account { void Withdraw (float amt).amt. */ void Account::Withdraw (float amt) { balance = balance . } float Account::GetBalance(void) const { return(balance).h // Account.

} void NickleNDime::EndMonth(void) { Withdraw(transactions * 0. */ void Account::EndMonthUtil() { cout << "transactions: ". } void MonthlyFee::EndMonth() { Withdraw(5. // Ordinary helper function static int RandomNum(int num) { return(rand() % num). } .cp definitions. break. } void Gambler::Withdraw (float amt) { if (RandomNum(100) <= 50) Account::Withdraw(2 * amt).00). } assert(0). case 2: return(new MonthlyFee). return(0). Account::EndMonthUtil(). else Account::Withdraw(0. Account::EndMonthUtil(). this would be easier. transactions = 0.. cout << transactions. cout << " balance: ". */ static Account* RandomAccount() { switch (RandomNum(3)) { case 0: return(new Gambler). break. case 1: return(new NickleNDime). cout << endl.50).00). } /*------------Support Functions----------------------*/ /* If C++ kept class name information around at run-time.4 /* Factors some common behavior up which will occur in the subclasses EndMonth().. cout<< balance. break. } //***** // Subclass .

} /* do a months worth of random transactions */ for (day=1.5 #define NUMACCOUNTS 20 /* This is a static class function. accountNum). accountNum++) { accounts[accountNum] = RandomAccount(). int day.5 balance: 52 balance: 71. // yeah! polymorphism!! } for (accountNum=0.5 balance: 274 balance: -372 balance: 38 balance: 60 balance: 100 balance: 99. accountNum++) { printf("account:%d ". day++) { accountNum = RandomNum(NUMACCOUNTS). int accountNum. // select an account at random if (RandomNum(2)) // deposit or withdraw accounts[accountNum]->Deposit(RandomNum(100)). } } /* Output account:0 transactions: 3 account:1 transactions: 4 account:2 transactions: 2 account:3 transactions: 4 account:4 transactions: 4 account:5 transactions: 5 account:6 transactions: 4 account:7 transactions: 3 account:8 transactions: 2 account:9 transactions: 1 account:10 transactions: 2 account:11 transactions: 3 account:12 transactions: 1 account:13 transactions: 3 account:14 transactions: 6 account:15 transactions: 3 account:16 transactions: 5 account:17 transactions: 1 account:18 transactions: 2 account:19 transactions: 2 */ balance: 124 balance: -33. accounts[accountNum]->EndMonth(). /* Create a bunch of accounts of various types */ for (accountNum=0.5 balance: 25 balance: 100 balance: -86 balance: 234 balance: 42 balance: -24 balance: 100 balance: 100 balance: -14 . */ void Account::TestOneMonth(void) { Account* accounts[NUMACCOUNTS]. accountNum<NUMACCOUNTS. accountNum<NUMACCOUNTS.5 balance: 253. accounts[accountNum]->Deposit(100). // yeah! polymorphism!! else accounts[accountNum]->Withdraw(RandomNum(100)). so you call it like regular function. day<=31. except with Account:: before the function name.

An instructor should respond to an init message which sets their age and sets them to have no unread mail and no eccentricities. The goal of the example is to demonstrate arranging classes in a hierarchy for maximum code-sharing. Stress is never more than 1000. this example demonstrates a sophisticated use of a key OOP axiom: an object never forgets its class.generally an instructor's level of respect in the community is their age minus the number of eccentricities. There are two measures of an instructor's current mood: Stress and Respect. you will design C++ classes suitable for storing information about university instructors. However. So for faculty respect is age plus number of eccentricities. Their stress is double the number of unread messages and their maximum stress is 2000. For this problem. Respect can never be negative. Lecturer. an instructor can best be described by three quantities: number of unread e-mail messages. by tracing the flow of execution. At any time. There are three types of instructor: Faculty. The Instructor::GetMail() method is a great example of how clever factoring can be used to split behavior between the super and sub classes. Faculty are the exception— Faculty eccentricities are regarded as "signs of a troubled genius" thereby increasing respect. Grad students are the exception. age. Respect. Also. and Grad student.6 Instructor Example (one of my favorite old CS107 final exam problems) This is a great example of behavior factoring between a superclass and its subclasses. . and number of eccentricities. Stress— an instructor's stress level is the number of unread messages. Think about how the reciever retains its class identity even as the flow of control jumps back and forth between Instructor and its subclasses.

Several things happen when an instructor gets new. the amount of unread-mail is increased. 90% of the time. instructors have different coping mechanisms. This is the sort of design drawing you might make to help think about your solution: Instance variables: •unreadMail •age •eccentricities Instructor Methods: -Stress() -Respect() -GetMail(int) Grad Methods: -Stress() -Cope() Lecturer Methods: -Cope() Faculty Methods: -Respect() -Cope() .7 Everything in an instructor's life is driven by receiving e-mail. The coping mechanism is activated at most one time for each batch of incoming mail. Faculty react by gaining 10 eccentricities. The resulting mental anguish causes the Grad student's eccentricities to go up or down by one randomly. Third. Second. there will be no change in the number of eccentricities. 10% of the time the number of eccentricities will randomly go up or down by one. Grad students react by reading all of their unread mail. In this case. The coping mechanisms may or may not bring the Stress and Respect factors back in line. Lecturers react by accidentally deleting half their unread mail. the new unread mail may cause the instructor's Stress factor to become larger than their Respect factor which makes the instructor unhappy. un-read e-mail: First.

*/ const int kMaxStress = 1000. virtual void GetMail(int). }. virtual void Cope() = NULL. int Stress() const. }. }. void RandomEccentric(). It stores an age and the number of unread email messages and responds to GetMail() to send the instructor mail. int age. // C++: "inline" definition // puts the code right in the . /********** LECTURER **********/ class Lecturer : public Instructor { Lecturer(int anAge): Instructor(anAge) {}. int eccentricities. /********** PROFESSOR **********/ class Professor : public Instructor { Professor(int anAge):Instructor(anAge) {}. virtual int Stress() const. void Cope(). /**********GRAD STUDENT**********/ class GradStudent : public Instructor { GradStudent(int anAge):Instructor(anAge) {}. class Instructor { public: Instructor(int anAge). }. int Respect() const. protected: int unreadMail.h.8 // Instructor. // private helper which changes eccentricities by +-1 // "pure virtual" helper method // This indicates to the compiler that Instructor // is an abstract super class. virtual int Respect() const.h /* The Instructor class is the abstract superclass for college instructors. void Cope(). void Cope(). .

void Instructor::RandomEccentric() { int delta.cp #include "Instructor. if (eccentricities<0) eccentricities = 0. } // 10% chance of ecc. if ((rand() % 10) == 0) RandomEccentric(). } int Instructor::Stress() const { if (unreadMail <= kMaxStress) return(unreadMail).eccentricities. Instructor::Instructor(int anAge) { age = anAge. } . Does not allow negative number // of eccentricities.9 // Instructor. if (stress>=0) return(stress). else delta = -1. if ((rand() % 2) == 0) delta = 1. else return(0). eccentricities = 0. change // key polymorphic line // Helper which randomly change the eccentricities // up or down by 1. unreadMail = 0. if (Stress() > Respect()) Cope(). } int Instructor::Respect() const { int stress = age . } void Instructor::GetMail(int numMessages) { unreadMail += numMessages. else return(kMaxStress). eccentricities += delta.h" // Construct with an age and init other to 0.

} /********** LECTURER **********/ void Lecturer::Cope() { unreadMail = unreadMail / 2. } void GradStudent::Cope() { unreadMail = 0. } /********** PROFESSOR **********/ int Professor::Respect() const { return(age + eccentricities). } void Professor::Cope() { eccentricities += 10.10 /********** GRAD **********/ int GradStudent::Stress() const { return(2 * Instructor::Stress()). } . RandomEccentric().