Appendix C. Selected C++ Examples

For those who desire some concrete examples illustrating the abstractions discussed in this text, I have provided 21 C++ programs. Those examples of general interest are published in this appendix. The remainder is available from Addison-Wesley at World Wide Web URL http://www.aw.com/cp/riel.html. and via anonymous FTP from ftp.aw.com in the directory cp/riel. The examples and a brief description of each are provided in the following list.

Example Chapter Description
#1 2 The implementation of the AlarmClock class described in Section 2.1.
#2[*] 3 The implementation of the course-scheduling system described in Section 3.3. The solution without controller classes is illustrated here.
#3 3 The implementation of the home heating system described in Section 3.4 (poor design with accessor methods).
#4[*] 3 The implementation of the home heating system described in Section 3.4 (good design without accessor methods).
#5[*] 4 The implementation of the “car uses a gas station” example described in Section 4.3. The five methods for implementing the uses relationship are demonstrated.
#6 4 The implementation of the meal containment hierarchy as described in Section 4.6.
#7 5 The implementation of the fruit basket example described in Section 5.9.
#8 5 The implementation of the meal containment hierarchy with inheritance as described in Section 4.6. This is a more general solution of Example #6.
#9[*] 5 The implementation of the sorted linked list inheriting from the linked list example described in Section 5.10.
#10[*] 5 The implementation of the LinkedRing class added to the linked list hierarchy of Example #9.
#11 5 The first solution to the “core the apple in the fruit basket” problem discussed in Section 5.19. This solution uses a NOP method in the base class.
#12 5 The second solution to the “core the apple in the fruit basket” problem discussed in Section 5.19. This solution uses a bookkeeping mechanism in the containing class.
#13 5 The third solution to the “core the apple in the fruit basket” problem discussed in Section 5.19. This solution uses a generic message handler, which is overridden in each derived class.
#14 6 The implementation of the wooden door example described in Section 6.3.
#15[*] 6 The implementation of the graduate student example described in Section 6.7. It illustrates the use of virtual, multiple inheritance in C++.
#16 8 The implementation of the Invoice class described in Section 8.3.
#17[*] 8 The implementation of the weak type-checking solution for constructing linked lists of dogs, linked lists of meals, and linked lists of airplanes; as described in Section 8.4.
#18[*] 8 The implementation of the template solution for constructing linked lists of dogs, linked lists of meals, and linked lists of airplanes; as described in Section 8.4.
#19[*] 9 The implementation of a minimal public interface in C++ on a reference-counting String class. This minimal interface is described in Section 9.5.
#20[*] 9 An example implementation of garbage collection in C++. The example examines a node class that hoards memory, and the memory handler that frees the space on demand.
#21[*] 11 The implementation of the automatic teller machine/bank example as designed in Chapter 11.

[*] This example is presented in this text. All others can be downloaded from the publisher via ftp.

Selected C++ Example #2

// Example #2 

// This C++ example illustrates the checking of a student's
// course prerequisites without using controller classes.
// This puts policy information inside one of the classes
// involved in the policy, rendering it less reusable.
// I feel that controller classes add complexity to design
// with little or no benefits. While controller classes do allow
// their host classes to be more reusable outside of the current
// domain, they are reusable only because they don't do anything,
// that is, they have data and a bunch of get and set methods.
// In this example, in order to check prerequisites, the
// course offering will ask the student for his/her list of
// courses that he/she has taken. The course offering will
// ask the Course to verify that the student has the necessary
// prerequisites by passing the course list as an explicit
// argument. The Course object then makes the determination.

// This example has several examples of containment in its
// implementation. For those readers who do not yet understand
// containment, I recommend reading Chapter 4 before working
// through this example. Experienced readers will notice areas
// where inheritance would have been very useful. Most noticeably
// in the area of reference counting. I felt that the addition
// of inheritance would have caused too many forward references
// into the text. It was left out at the expense of some
// redundant abstraction.

// The efficiency-minded should be aware that this code
// was written for understanding, not speed. All methods are not
// inline. The one-liners could easily be made inline. Also,
// some of the conditional tests could be improved for speed
// at the cost of readability.

#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

// Forward references to the list classes.
class CourseList;
class StudentList;
class OfferingList;


// Constants used within this program.

const int name_len = 30;
const int desc_len = 128;
const int course_len = 30;
const int student_len = 50;
const int small_strlen = 15;

// Courses have a name, description, duration, and a list
// of prerequisites. Since we expect Course objects to
// appear on many lists (e.g., Calculus I is a prerequisite of
// Calculus II, Calculus III, Physics I, etc.), we would like
// to implement shared shallow copies, i.e., we copy only
// pointers, not the entire course object. We accomplish
// this through a technique called reference counting. The
// class gets an integer counter, which maintains how many
// objects are sharing the particular course object. Anyone
// who points at the object must call the Course::attach_object()
// method, which increments the counter. When destroyed, the
// sharing object calls Course::detach_object() to decrement
// the counter. If detach_object() returns zero, then the
// caller knows that it is the last user of that Course
// object and it calls its destructor.

class Course {
private:
     char name[name_len];
     char description[desc_len];
     int duration;
     CourseList* prereq;
     int reference_count;
public:
     Course(char*, char*, int, int, ...);
     Course(const Course&);
     ~Course();
     int attach_object();
     int detach_object();
     void add_prereq(Course&);
     int check_prereq(CourseList&);
     void print();
     void short_print();
     int are_you(char*);
};


// Each key abstraction also has a corresponding list class
// to maintain the list operations. Readers with more
// experience would certainly argue for the use of C++ templates
// to handle the list classes. I felt that this was too much
// forward referencing, which would have rendered the example less
// readable.

class CourseList {
private:
     Course **courses;
     int size;
     int course_num;
public:
     CourseList(int);
     CourseList(CourseList&);
     ~CourseList();
     int add_item(Course&);
     Course* find_item(char*);
     int find_all(CourseList&);
     void print();
};


// This constructor for course takes a name, description,
// duration, and a variable list of prerequisites. Each
// prerequisite is added to the list using the CourseList::
// add_item() method.
Course::Course(char* n, char* d, int len, int pnum, ...)
{
     int i;
     va_list ap;

     strncpy(name, n, name_len);
     strncpy(description, d, desc_len);
     duration = len;
     prereq = new CourseList(course_len);
     reference_count = 1;
     if (pnum) {
          va_start(ap, pnum);
          for (i=0; i < pnum; i++) {
                      prereq->add_prereq(*va_arg(ap, Course*));
          }
          va_end(ap);
     }
}

// The copy constructor for course makes a copy of all the
// strings and calls the copy constructor for a CourseList.
Course::Course(const Course& rhs)
{
     strcpy(name, rhs.name);
     strcpy(description, rhs.description);
     duration = rhs.duration;
     prereq = new CourseList(*rhs.prereq);
     reference_count = rhs.reference_count;
}


// The destructor for Course deletes its prerequisites and
// checks to be sure that it is the last user that called
// delete on the course object. If not, an error message
// is displayed.
Course::~Course()
{
     delete prereq;
     if (reference_count > 1) {
          cout << ''Error: A course object destroyed with '';
          cout << reference_count << '' other objects referencing
                        it.
'';
     }
}


// Each object that points at a Course object must call
// attach_object to register itself with the object.
int
Course::attach_object()
{
     return(++reference_count);
}

// Each object that called attach_object must call detach_object
// in its destructor (or other member function) to decrement
// the reference counter.
int
Course::detach_object()
{
     return(--reference_count);
}


// To add a prerequisite to the course, we call the add_item
// method of CourseList. This method returns zero on failure
// to add the Course (i.e., the list is full).
void
Course::add_prereq(Course&new_prereq)
{
     if (prereq->add_item(new_prereq) == 0) {
          cout << ''Error: Cannot add any new prerequisites.
'';
     }
}


void
Course::print()
{
     cout << ''

Course:'' << name << ''
'';
     cout << ''Description:'' << description << ''
'';
     cout << ''Duration:'' << duration << ''
'';
     cout << ''List of Prerequisites: '';
     prereq->print();
     cout << ''

'';
}


// The short_print method is used in places where we only
// want to see the name and not all of the associated info
// of the Course.
void
Course::short_print()
{
     cout << name;
}



// This method is very important in the design of this
// system. The Course object receives a course list and
// checks its prerequisites against it, using the CourseList::
// find_all method. This method checks to see if all of the
// courses in the argument list are in the list to which the
// message was sent.
int
Course::check_prereq(CourseList& courses_taken)
{
     return(courses_taken.find_all(*prereq));
}


// This method checks to see if its name is equal to the
// name passed in. It is used for searching for a particular
// course from a list of Course objects (by name).
int
Course::are_you(char* guess_name)
{
     return(!strcmp(name, guess_name));
}


CourseList::CourseList(int sz)
{
     course_num = 0;
     courses = new Course*[size=sz];
}


// Note the use of reference counting in the CourseList copy
// constructor. Each course is simply pointed to, not copied.
// The attach_object method allows the incrementing of the
// reference counter. This reference counter keeps track of
// how many objects (CourseList or otherwise) are pointing to
// the Course object.
CourseList::CourseList(CourseList& rhs)
{
     int i;

     courses = new Course*[size=rhs.size];
     for (i=0; i < size; i++) {
          courses[i] = rhs.courses[i];
          courses[i]->attach_object();
     }
     course_num = rhs.course_num;
}


// The CourseList destructor detaches each object in the
// prerequisite list. If any of the detach_object method
// calls evaluates to zero, this course list is the
// last object using the course and its destructor must be
// called.
CourseList::~CourseList()
{
     int i;
     for (i=0; i < course_num; i++) {
          if (courses[i]->detach_object() == 1) {
                     delete courses[i];
          }
     }
     delete courses;
}


// The add_item method checks to be sure the list still has
// room. A reasonable solution would be to increase the
// size of the list on demand. This adds a significant amount
// of code and complexity without enlightening the reader
// to the problem at hand. For that reason, lists are a fixed
// size at creation time. They report an error when full (by
// returning zero).
// If the list has room, then the course is added and it is
// attached to preserve the reference counting.
int
CourseList::add_item(Course&new_item)
{
     if (course_num == size) {
          return(0);
     }
     else {
          courses[course_num++] = &new_item;
          new_item.attach_object();
     }
     return(1);
}


// The course list searches its list for the course whose
// name matches that passed in by the user. If the course
// isn't found, this method returns the NULL pointer.
Course*
CourseList::find_item(char* guess_name)
{
     int i;

     for (i=0; i < course_num; i++) {
          if (courses[i]->are_you(guess_name)) {
                     return(courses[i]);
          }
     }
     return(NULL);
}


// This method checks to be sure that all courses of the
// findlist are in the list to which the message was sent.
// Since courses are shallow copied in these lists, we need
// only check the addresses of the course objects and not
// the course names.
int
CourseList::find_all(CourseList& findlist)
{
     int i, j, found;

     for (i=0; i < findlist.course_num; i++) {
          found = 0;
          for (j=0; j < course_num && ! found; j++) {
                      if (findlist.courses[i] == courses[j]) {
                                 found=1;
                      }
          }
          if (!found) {
                    return(0);
          }
     }
     return(1);
}


void
CourseList::print()
{
     int i;

     cout << ''

'';
     for (i=0; i < course_num; i++) {
          courses[i]->short_print();
          cout << '' '';
     }
     cout << ''

'';
}

// Students have a name, social security number, and age. Like
// the Course objects, they also have a list of courses (the
// courses that the student has completed). Students are
// a reference-counting class just like the Course class. The
// reference counting works in exactly the same way as that of
// the Course. This will lead the experts to criticize the
// duplicate abstraction caused by the reference-counting
// mechanism. We could use inheritance to solve the problem
// but have not discussed this topic yet. The use of inheritance
// to eliminate duplicate abstractions will be examined in
// Chapter 5.
class Student {
private:
     char name[name_len];
     char ssn[small_strlen];
     int age;
     CourseList *courses;
     int reference_count;
public:
     Student(char*, char*, int, int, ...);
     Student(const Student&);
     ~Student();
     int attach_object();
     int detach_object();
     void add_course(Course&);
     CourseList& get_courses();
     void print();
     void short_print();
     int are_you(char*);
};


// The StudentList mirrors the CourseList class except it is
// working on Student objects rather than Course objects.
class StudentList{
private:
     Student **students;
     int size;
     int student_num;
public:
     StudentList(int);
     StudentList(StudentList&);
     ~StudentList();
     int add_item(Students);
     Student* find_item(char*);
     void print();
};


Student::Student(char* n, char* s, int a, int num, ...)
{
     int i;
     va_list ap;

     strncpy(name, n, name_len);
     strncpy(ssn, s, small_strlen);
     age = a;
     courses = new CourseList(course_len);
     reference_count = 1;
     if (num) {
          va_start(ap, num);
          for (i=0;i < num; i++) {
                       courses->add_item(*va_arg(ap, Course*));
          }
          va_end(ap);
     }
}


Student::Student(const Student& rhs)
{
     strcpy(name, rhs.name);
     strcpy(ssn, rhs.ssn);
     age = rhs.age;
     courses = new CourseList(*rhs.courses);
     reference_count = rhs.reference_count;
}

Student::-Student()
{
     delete courses;
}


int
Student::attach_object()
{
     return(++reference_count);
}


int
Student::detach_object()
{
     return(--reference_count);
}


void
Student::add_course(Course& c)
{
     if (courses->add_item(c) == 0){
          cout << ''Cannot add any new courses to the Student.
'';
     }
}

// Note the need for an accessor method. This method will
// be used by the CourseOffering class when it needs to
// check the prerequisites of a Student object. The Student
// is asked for its course list, which is then given to the
// course for processing. In general, accessor methods are
// bad in that they imply that this piece of data is not
// strongly related to the other data of this class or its
// methods. In general, ask why you are removing this data
// from its encapsulation, what you are doing with it, and
// why doesn't the class that owns the data do it for you.
// In this example, the class cannot perform the behavior itself
// because it needs data from both the Course and Student objects
// to make the decision.
CourseList&
Student::get_courses()
{
     return(*courses);
}


void
Student::print()
{
     cout << ''

Name: '' << name << ''
'';
     cout << ''SSN: '' << ssn << ''
'';
     cout << ''Age: '' << age << ''
'';
     cout << ''Prerequisites: '';
     courses->print();
     cout << ''

'';
}


void
Student::short_print()
{
     cout << name;
}


int
Student::are_you(char* guess_name)
{
     return(!strcmp(name, guess_name));
}


StudentList::StudentList(int sz)
{
     student_num = 0;
     students = new Student*[size=sz];
}


StudentList::StudentList(StudentList& rhs)
{
     int i;

     students = new Student*[size=rhs.size];
     for (i=0; i < size; i++) {
          students[i] = rhs.students[i];
          students[i]->attach_object();
     }
     student_num = rhs.student_num;
}


StudentList::~StudentList()
{
     int i;
     for (i=0; i < student_num; i++) {
          if (students[i]->detach_object() == 1) {
                     delete students[i];
          }
     }
     delete students;
}


int
StudentList::add_item(Student& new_item)
{
     if (student_num == size) {
          return(0);
     }
     else {
          students[student_num++] = &new_item;
          new_item.attach_object();
     }
     return(1);
}


Student*
StudentList::find_item(char* guess_name)
{
     int i;

     for (i=0; i < student_num; i++) {
          if (students[i]->are_you(guess_name)) {
                     return(students[i]);
          }
     }
     return(NULL);
}


void
StudentList::print()
{
     int i;

     for (i=0; i < student_num; i++) {
          students[i]->short_print();
          cout << '' '';
     }
}


// The CourseOffering class captures the relationship of a
// course, in a room, on a particular date, with a particular
// group of students. It is not a reference-counting class,
// because we never share CourseOffering objects on multiple
// lists.
class CourseOffering {
private:
     Course* course;
     char room[small_strlen];
     char date[small_strlen];
     StudentList *attendees;
public:
     CourseOffering(Course&, char*, char*);
     CourseOffering(const CourseOffering&);
     -CourseOffering();
     void add_student(Student&);
     void print();
     void short_print();
     int are_you(char*, char*);
};


// The CourseOffering list class is similar to the Student
// and Course list classes.
class OfferingList {
private:
     CourseOffering **offerings;
     int size;
     int offering_num;
public:
     OfferingList(int);
     OfferingList(OfferingList&);
     ~OfferingList();
     int add_item(CourseOffering&);
     CourseOffering* find_item(char*, char*);
     void print();
};


CourseOffering::CourseOffering(Course& c, char* r, char* d)
{
     course = &c;
     course->attach_object();
     strncpy(room, r, small_strlen);
     strncpy(date, d, small_strlen);
     attendees = new StudentList(student_len);
}


CourseOffering::CourseOffering(const CourseOffering& rhs)
{
     course = rhs.course;
     course->attach_object();
     strcpy(room, rhs.room);
     strcpy(date, rhs.date);
     attendees = new StudentList(*rhs.attendees);
}


CourseOffering::~CourseOffering()
{
     if (course->detach_object() == 1) {
          delete course;
     }
     delete attendees;
}


// The course offering must ensure that a new student has
// the necessary prerequisites. It does this by getting
// the list of courses the student has taken from the Student
// and gives it to the check_prereq method of the course.
// The course can determine if the prerequisites are met,
// since the course has the list of prerequisites and the
// Student has given, via the CourseOffering object's call
// to get_courses, the list of courses.
void
CourseOffering::add_student(Student& new_student)
{
     if (course->check_prereq(new_student.get_courses())) {
          attendees->add_item(new_student);
          cout << ''Student added to course.
'';
     }
     else {
          cout << ''Admission refused: Student does not have the '';
          cout << ''necessary prerequisites.
'';
     }
}


void
CourseOffering::print()
{
     cout << ''

The course offering for '';
     course->short_print();
     cout << '' will be held in room '' << room << '' starting on '';
     cout << date << ''
'';
     cout << ''Current attendees include: '';
     attendees->print();
     cout << ''

'';
}


void
CourseOffering::short_print()
{
     course->short_print();
     cout << ''('' << date << '') '';
}


// The name of the course is not enough when comparing course
// offerings. We must also test the dates.
int
CourseOffering::are_you(char* guess_name, char* guess_date)
{
     return(!strcmp(guess_date, date) &&
                course->are_you(guess_name));
}


OfferingList::OfferingList(int sz)
{
     offering_num = 0;
     offerings = new CourseOffering*[size=sz];
}


OfferingList::OfferingList(OfferingList& rhs)
{
     int i;

     offerings = new CourseOffering*[size=rhs.size];
     for (i=0; i < size; i++) {
     offerings[i] = rhs.offerings[i];
     }
     offering_num = rhs.offering_num;
}


OfferingList::~OfferingList()
{
     int i;
     for (i=0; i < offering_num; i++) {
          delete offerings[i];
     }
     delete offerings;
}


int
OfferingList::add_item(CourseOffering& new_item)
{
     if (offering_num == size) {
          return(0);
     }
     else {
          offerings[offering_num++] = &new_item;
     }
     return(1);
}


CourseOffering*
OfferingList::find_item(char* guess_name, char* date)
{
     int i;

     for (i=0; i < offering_num; i++) {
          if (offerings[i]->are_you(guess_name, date)) {
                     return(offerings[i]);
          }
     }
     return(NULL);
}


void
OfferingList::print()
{
     int i;

     for (i=0; i < offering_num; i++) {
          offerings[i]->short_print();
          cout << '' '';
     }
}


// The main program is a simple menu-driven system for creating
// courses, course offerings, students; listing courses,
// students, and offerings; adding courses to students,
// students to courses, and prerequisites to courses. It can
// be used to test the public interfaces of the classes in
// this application.
void
main()
{
     CourseList courses(50);
     StudentList students(50);
     OfferingList offerings(50);
     Course *course1, *course2;
     Student *student;
     CourseOffering *offer1;
     int duration, age, choice;
     char answer[128], name[40], description[128],
                course_name[50];
     char ssn[20], date[20], room[20];
     char c;

     do {
          cout << ''What would you like to do?
'';
          cout << '' 1) Build a new course
'';
          cout << '' 2) Build a new student
'';
          cout << '' 3) Build a new course offering
'';
          cout << '' 4) List courses
'';
          cout << '' 5) List students
'';
          cout << '' 6) List offerings
'';
          cout << '' 7) Add a prerequisite to a course
'';
          cout << '' 8) Add a course to a student
'';
          cout << '' 9) Add a student to a course offering
'';
          cout << ''10) Detailed info on a course
'';
          cout << ''11) Detailed info on a student
'';
          cout << ''12) Detailed info on an of fering
'';
          cout << '' q) Quit
'';
          cout << ''
Your Choice: '';
          cin.getline(answer, 128);
          choice = atoi(answer);
          switch (choice) {
                     case 1:
                               cout << ''Enter Name: '';
                               cin.getline(name, 40);
                               cout << ''Enter Description: '';
                               cin.getline(description, 128);
                               cout << ''Enter Length of Course: '';
                               cin >> duration;
                               courses.add_item(*new Course(name,
                                description, duration, 0));
                               cin.get(c);
                               break;
                     case 2:
                               cout << ''Enter name: '';
                               cin.getline(name, 40);
                               cout << ''Enter ssn: '';
                               cin.getline(ssn, 20);
                               cout << ''Enter age: '';
                               cin >> age;
                               students.add_item(*new Student
                                (name, ssn, age, 0));
                               cin.get(c);
                               break;
                     case 3:
                               cout << ''Enter course: '';
                               cin.getline(course_name, 50);
                               course1 = courses.find_item(course_name);
                               if (course1 == NULL) {
                                         cout << ''Sorry, Cannot
                               find that course.
'';
                                         break;
                               }
                               cout << ''Enter room: '';
                               cin.getline(room, 20);
                               cout << ''Enter date: '';
                               cin.getline(date, 20);
                               offerings.add_item(*new
                                CourseOffering(*course1,
                                room, date));
                               break;
                     case 4:
                               cout << ''
List of courses: 
'';
                               courses.print();
                               cout << ''

'';
                               break;
                     case 5:
                               cout << ''
List of students: 
'';
                               students.print();
                               cout << ''

'';
                               break;
                     case 6:
                               cout << ''
List of Offerings: 
'';
                               offerings.print();
                               cout << ''

'';
                               break;
                     case 7:
                               cout << ''To which course? '';
                               cin.getline(course_name, 50);
                               course1 = courses.find_item
                                (course_name);
                               if (course1 == NULL) {
                                         cout << ''Sorry, Cannot
                               find that course.
'';
                                         break;
                               }
                               cout << ''Which prerequisite? '';
                               cin.getline(course_name, 50);
                               course2 = courses.find_item
                                (course_name);
                               if (course2 == NULL) {
                                         cout << ''Sorry, Cannot
                                find that course.
'';
                                          break;
                               }
                               course1->add_prereq(*course2);
                               break;
                     case 8:
                               cout << ''To Which Student? '';
                               cin.getline(name, 40);
                               student = students.find_item(name);
                               if (student == NULL) {
                                         cout << ''Sorry, Cannot
                               find that student.
'';
                                         break;
                               }
                               cout << ''Which Course? '';
                               cin.getline(course_name, 50);
                               course1 = courses.find_item
                                (course_name);
                               if (course1 == NULL) {
                                         cout << ''Sorry, Cannot
                                find that course.
'';
                                          break;
                               }
                               student->add_course(*course1);
                               break;
                     case 9:
                               cout << ''To which course? '';
                               cin.getline(course_name, 50);
                               cout << ''On which date? '';
                               cin.getline(date, 20);
                               offer1 = offerings.find_item
                                (course_name, date);
                               if (offer1 == NULL) {
                                         cout<<''Sorry, Cannot
                                find that course offering.
'';
                                          break;
                               }
                               cout << ''Which Student? '';
                               cin.getline(name, 40);
                               student = students. find_item(name);
                               if (student == NULL) {
                                     cout << ''Sorry, Cannot
                               find that student.
'';
                                          break;
                               }
                               offer1->add_student(*student);
                               break;
                     case 10:
                               cout << ''On Which Course? '';
                               cin.getline(course_name, 50);
                               course1 = courses.find_item
                                (course_name);
                               if (course1 == NULL) {
                                         cout << ''Sorry, Cannot
                                find that course.
'';
                                           break;
                               }
                               course1->print();
                               break;
                     case 11:
                               cout << ''On Which Student?'';
                               cin.getline(name, 40);
                               student = students.find_item(name);
                               if (student == NULL) {
                                         cout << ''Sorry, Cannot
                                find that student.
'';
                                           break;
                               }
                               student->print();
                               break;
                     case 12:
                               cout << ''On Which Course? '';
                               cin.getline(course_name, 50);
                               cout << ''Which date? '';
                               cin.getline(date, 20);
                               offer1 = offerings.find_item
                                (course_name, date);
                               if (offer1 == NULL) {
                                         cout << ''Sorry, Cannot
                                find that course offering.
'';
                                          break;
                               }
                               offer1->print();
                               break;
                               }
                               } while (answer[0] >= '1' &&
                                 answer[0] <= '9'),
                               }

Selected C++ Example #4

// Example #4 

// This C++ example illustrates the better method for implementing
// a fragment of the home heating system example described in
// Booch's OOA/D book. This better solution pushes higher-level
// functionality onto the Room class, making the heat flow
// regulator less god-like.

// This example illustrates several examples of containment. If
// you are unfamiliar with the concept of containment, please read
// Chapter 4 before working through this example.

#include <iostream.h>
#include <stdlib.h>
#include <string.h>

// Constants used in this example.

const int name_len = 50;
const int large_strlen = 128;
const int room_len = 20;


// These sensor classes are identical to those in the previous
// example. See the comments on the classes in that example for
// further information.
class DesiredTempActuator {
public:
     int get_temp();
};

// Returns a random number from 50 to 90
int
DesiredTempActuator::get_temp()
{
     return(random(41) + 50);
}


class ActualTempSensor {
public:
     int get_temp();
};

int
ActualTempSensor::get_temp()
{
     return(random(41) + 50);
}


class OccupancySensor {
public:
     int anyone_in_room();
};


// Returns a random value (0 or 1)
int
OccupancySensor::anyone_in_room()
{
     return(random(2) % 2);
}


// The Room class contains a desired temperature input device, a
// temperature sensor, and an occupancy sensor (all by value). It
// has a name attribute for descriptive information.
// Note that the accessor methods have been eliminated in favor of
// a ''do_you_need_heat()'' method.
class Room {
     char name[name_len];
     DesiredTempActuator dtemp;
     ActualTempSensor atemp;
     OccupancySensor occ;
public:
     Room(char*);
     int do_you_need_heat();
};


Room::Room(char* n)
{
     strncpy(name, n, name_len);
}


// The Room object determines if it needs heat by calculating its
// working temperature (desired - actual) and checking if anyone
// is in the room. The room needs heat if the actual temperature
// is less than the desired temperature and someone is in the room,
// OR if no one is in the room and the actual temperature is more than
// five degrees less than the desired temperature.
// The method returns one if heat is needed and zero if not.
int
Room::do_you_need_heat()
{
     int working_temp, occupied;

     working_temp = dtemp.get_temp() - atemp.get_temp();
     occupied = occ.anyone_in_room();
     cout << ''The'' << name << '' has a working temp of '' <<
working_temp;
     cout << '' and '' << (occupied ? ''someone in the room.
'' :
''no one in the room.
''),
     if (working_temp > 5 || working_temp > 0 && occupied) {
          return(1);
     }
     return(0);
}


// Our furnace does little in this implementation of a design
// fragment because it is relatively uninteresting to illustrate
// the problem of poorly related data and behavior.
class Furnace {
public:
     void provide_heat();
     void turnoff();
};

void
Furnace::provide_heat()
{
     cout << ''Furnace Running
'';
}

void
Furnace::turnoff()
{
     cout << ''Furnace Turned Off
'';
}



// The heat flow regulator does not contain a list of rooms and a
// furnace. It is associated with them. There is a very large
// difference between stating that a class contains an object by
// reference, or a class is associated with an object. See
// Chapter 7 for an indepth discussion on this subject.
class HeatFlowRegulator {
     Room *house[room_len];
     Furnace *heater;
     int room_num;
public:
     HeatFlowRegulator(Furnace*, int, Room**);
     int loop();
};


// This constructor is identical to the one of the previous
// example.
HeatFlowRegulator::HeatFlowRegulator(Furnace* f, int num, Room
**rooms)
{
     int i;

     heater = f;
     room_num = num;
     for (i=0; i < room_num; i++) {
          house[i] = rooms[i];
     }
}


// The looping of the heat flow regulator is to check each room to see
// if it needs heat. In order to do this, the regulator simply asks
// the room if it needs heat. The work of figuring out if heat is
// needed has been pushed onto the Room class, which contains the
// necessary information.
int
HeatFlowRegulator::loop()
{
     int anyone_need_heat=0, i;

     for (i=0; i < room_num; i++) {
          anyone_need_heat += house[i]->do_you_need_heat();
     }
     if (anyone_need_heat)
          heater->provide_heat();
     else
          heater->turnoff();
     return(anyone_need_heat);
}


void
main()
{
     int room_num, i, retval;
     Furnace our_furnace;
     Room *rooms[room_len];
     char buffer[large_strlen];

     cout << ''How many rooms in your house? '';
     cin >> room_num;
     cin.get();
     if (room_num > room_len)
          room_num = room_len;
     for (i=0; i < room_num; i++){
          cout << ''What is the name of room['' << (i+1) << '']? '';
          cin.getline(buffer, large_strlen, '
'),
          rooms[i] = new Room(buffer);
     }

     HeatFlowRegulator h(&our_furnace, room_num, rooms);

     do{
          retval = h.loop();
          cout << retval << '' rooms required heat!
'';
          cout << ''Continue? '';
          cin.getline(buffer, large_strlen, '
'),
     } while (buffer[0] == 'y'),
}

Selected C++ Example #5

//Example #5
// This example with its five parts will illustrate how a
// uses relationship may be implemented. The most common
// method of implementing the uses relationship is via
// containment. Containment relationships always imply
// uses, but uses do not always imply containment. The second
// example illustrates uses via containment. How does a Meal
// know the name of its Melon object when it wants to use
// it? Answer: It knows its name because it contains it, i.e.,
// Meals contain Melons.
// This brings up an interesting point. In a design
// discussion, if a member of the design team states that
// some class X uses class Y, always ask, ''How does the X
// object know the name of the Y object?''. While the answer is
// often, ''Because X contains the Y object,'' there are five
// other possibilities. We will discuss these in the context
// of a car using a gas station.

// How does the Car object know the name of the GasStation
// object? It clearly doesn't contain it; Cars do not contain
// GasStations.

// The first, and most popular, method is that the Car object
// is given the GasStation object as an explicit parameter to
// its method. This is illustrated by the Car1 class.

// The second method is for all Car objects to go to the
// same global GasStation. They know its name by hardcoding
// it into the Car's method(s). Some consider this to be
// a special case of the first method since global variables
// are really implicit parameters. Because it has a different
// form, we implement it in the class Car2.

// The third method is for the wealthy. When their Car runs
// out of gasoline, they build a GasStation, pump gas, and
// tear down the gas station. While this is ridiculous in
// the domain of cars and gas stations, it is appropriate in
// many domains. The Car3 class captures this method of using
// local objects.

// The fourth method is that when a car is born, god gives
// it its gas station for later use. Each car has a one-to-one
// association with its gas station. We will talk more about
// associations in Chapter 7, but this serves as a good example
// of the implementation of associations. Do not confuse
// an association with containment by reference. The
// relationships are very different at design time, however,
// C++ does not distinguish them. The class Car4 implements
// this feature.

// The fifth method is for the Car class to ask a third-party
// object for a gas station. Of course, this only postpones
// the problem. How did we know the name of the third-party
// object? It must be one of the other five methods: i.e.,
// containment, passed as an argument, a global object,
// a local object, an association, or ... ask a fourth-
// party object ... (ad nauseum).
// The Car5 class illustrates this implementation of uses by
// asking a contained map object for a gas station.

#include <iostream.h>

// The GasStation is the same class in all five methods of
// implementing uses relationships. The only interest is how
// we gain knowledge of the name of a particular gas station
// we want to use. Our gas station has a constructor; it
// can take gas deliveries, it can change its price (no grades
// of gasoline here, for simplification), and most
// importantly, it can dispense gasoline to anyone willing
// to pay. In our case, Car objects.
class GasStation{
private:
     double quantityOfGas;
     double price;
public:
     GasStation(double, double quantity=0.0);
     void take_gas_delivery(double);
     void change_price(double);
     double give_gasoline(double);
};

GasStation::GasStation(double start_price, double quantity)
{
     quantityOfGas = quantity;
     price = start_price;
}

void
GasStation::take_gas_delivery(double quantity)
{
     quantityOfGas += quantity;
}

void
GasStation::change_price(double new_price)
{
     price = new_price;
}

double
GasStation::give_gasoline(double amount)
{
     double gas_purchased;

     gas_purchased = amount / price;
     quantityOfGas -= gas_purchased;
     if (quantityOfGas < 0) {
          gas_purchased += quantityOfGas;
          quantityOfGas = 0.0;
}
     return(gas_purchased);
}



// The Car1 class implements the first method of implementing
// a uses relationship which is not containment. The Car's
// get_gasoline method takes not only money, but also a
// GasStation object, as an argument.
class Car1 {
private:
     double milesPerGallon;
     double fuelCapacity;
     double gasInTank;
     double mileage;
public:
     Car1(double, double, double=0.0, double=0.0);
     void get_gasoline(GasStation&, double);
     void drive(double);
     void print();
};


Car1::Car1(double mpg, double capacity, double starting_gas,
                       double miles)
{
     milesPerGallon = mpg;
     fuelCapacity = capacity;
     gasInTank = starting_gas;
     mileage = miles;
}


// The drive method computes the gasoline needed to travel the
// desired distance. It then checks to see if there is enough
// gasoline in the car. If not, a message is printed to the user and
// the car moves as far as it can. If the car has less than 10% of its
// remaining capacity, a warning is printed to the user.
void
Car1::drive(double miles)
{
     double gas_needed;

     gas_needed = miles / milesPerGallon;
     if (gas_needed > gasInTank) {
          mileage += gasInTank * milesPerGallon;
          cerr << ''You ran out of gasoline after travelling '';
          cerr << gasInTank * milesPerGallon << '' miles.
'';
          gasInTank = 0.0;
     }
     else {
          mileage += miles;
          gasInTank -= gas_needed;
          if (gasInTank < 0.1 * fuelCapacity) {
                                cerr << ''Warning: You have only enough gas
to go '';
                                cerr << GasInTank * miles_per_gallon << ''
miles.
'';
         }
     }
}



// The get_gasoline method is the most interesting. In this
// implementation the Car class knows the name of its gas station
// because it is passed into the method. The gas station is used to
// get the gasoline, and a number of checks are made to be sure the
// gas station wasn't out of gas or that the user didn't spill some
// gas. Of course, this implementation could be made more
// elaborate but this will suffice to demonstrate the
// implementation of ''Car uses GasStation. ''
void
Car1::get_gasoline(GasStation& myGasStation, double money)
{
      double gas_received;

      gas_received = myGasStation.give_gasoline(money);
      if (gas_received == 0) {
           cerr << ''Sorry the gas station was out of gas.
'';
      }
      else {
           gasInTank += gas_received;
           if (gasInTank > fuelCapacity) {
                      cerr << ''You spilled '' << (gasInTank -
fuelCapacity);
                      cerr << '' gallons of gas on the ground.
'';
                      gasInTank = fuelCapacity;
         }
     }
}


void
Car1::print()
{
     cout << ''The car gets '' << miles_per_gallon;
     cout << '' miles per gallon.
It currently has '';
     cout << gasInTank << '' gallons of gasoline on board.
'';
     cout << ''Its maximum fuel capacity is '' << fuelCapacity;
     cout << '' gallons
and it has '' << mileage << '' miles.
''
}


// The Car2 class demonstrates the implementation of a uses
// relationship through a global GasStation object. In this case,
// all Car2 objects go to the same GasStation, in this case
// ''Global_GasStation.'' Some designers consider this a special
// case of implementing the uses relationship through a parameter
// (e.g., Car1), but since it looks different in implementation, I
// felt it should be included.

GasStation Global_GasStation(1.25, 10000);
class Car2 {
private:
     double milesPerGallon;
     double fuelCapacity;
     double gasInTank;
     double mileage;
public:
     Car2(double, double, double=0.0, double=0.0);
     void get_gasoline(double);
     void drive(double);
     void print();
};


Car2::Car2(double mpg, double capacity, double starting_gas,
                        double miles)
{
     milesPerGallon = mpg;
     fuelCapacity = capacity;
     gasInTank = starting_gas;
     mileage = miles;
}


void
Car2::drive(double miles)
{
     double gas_needed;

     gas_needed = miles / milesPerGallon;
     if (gas_needed > gasInTank) {
          mileage += gasInTank * milesPerGallon;
          cerr << ''You ran out of gasoline after travelling '';
          cerr << gasInTank * milesPerGallon << '' miles.
'';
          gasInTank = 0.0;
     }
     else {
          mileage += miles;
          gasInTank -= gas_needed;
          if (gasInTank < 0.1 * fuelCapacity) {
                     cerr << ''Warning: You have only enough gas
to go '';
                     cerr << GasInTank * milesPerGallon << ''
miles.
'';
         }
     }
}
// Note the use of the Global_GasStation object to get_gasoline.
void
Car2::get_gasoline(double money)
{
     double gas_received;

     gas_received = Global_GasStation.give_gasoline(money);
     if (gas_received == 0) {
          cerr << ''Sorry the gas station was out of gas.
'';
     }
     else {
          gasInTank += gas_received;
          if (gasInTank > fuelCapacity) {
                      cerr << ''You spilled '' << (gasInTank -
fuelCapacity);
                     cerr << '' gallons of gas on the ground.
'';
                     gasInTank = fuelCapacity;
          }
     }
}


void
Car2::print()
{
     cout << ''The car gets '' << milesPerGallon;
     cout << ''miles per gallon.
It currently has '';
     cout << gasInTank << '' gallons of gasoline on board.
'';
     cout << ''Its maximum fuel capacity is '' << fuelCapacity;
     cout << ''gallons
and it has'' << mileage << '' miles.
'';
}



// The Car3 class implements its uses relationship through a local
// object that is built ''on-the-fly'' at runtime. Whenever the
// get_gasoline() method is called on Car3 objects, the method
// builds itself a gas station, uses the gas station, and then
// destroys it.This is inefficient but is used in some
// implementations,
class Car3 {
private:
     double milesPerGallon;
     double fuelCapacity;
     double gasInTank;
     double mileage;
public:
     Car3(double, double, double=0.0, double=0.0);
     void get_gasoline(double);
     void drive(double);
     void print();
};
Car3::Car3(doublempg, double capacity, double starting_gas,
                      double miles)
{
     milesPerGallon = mpg;
     fuelCapacity = capacity;
     gasInTank = starting_gas;
     mileage = miles;
}


void
Car3::drive(double miles)
{
     double gas_needed;

     gas_needed = miles / milesPerGallon;
     if (gas_needed > gasInTank) {
          mileage += gasInTank * milesPerGallon;
          cerr << ''You ran out of gasoline after travelling '';
          cerr << gasInTank * milesPerGallon << '' miles.
'';
          gasInTank = 0.0;
     }
     else {
          mileage += miles;
          gasInTank -= gas_needed;
          if (gasInTank < 0.1 * fuelCapacity) {
                     cerr << ''Warning: You have only enough gas
to go '';
                     cerr << gasInTank * milesPerGallon <<
'' miles.
'';
          }
      }
}

// Note the creation and use of local_station in order to get
// gasoline for the car. While a bit silly in the domain of cars and
// gas stations, there are domains where the use of a local object
// as part of the implementation of a method is perfectly
// appropriate.
void
Car3::get_gasoline(double money)
{
      double gas_received;
      GasStationlocal_station(1.25, 1000);

      gas_received = local_station.give_gasoline(money);
      if (gas_received == 0) {
           cerr << ''Sorry the gas station was out of gas.
'';
      }
       else {
            gasInTank += gas_received;
            if (gasInTank > fuelCapacity) {
                 cerr << ''You spilled '' << (gasInTank -
fuelCapacity);
                 cerr << '' gallons of gas on the ground.
'';
                 gasInTank = fuelCapacity;
            }
     }
}

void
Car3::print()
{
      cout << ''The car gets '' << milesPerGallon;
      cout << '' miles per gallon.
It currently has '';
      cout << gasInTank << '' gallons of gasoline on board.
'';
      cout << ''Its maximum fuel capacity is '' << fuelCapacity;
      cout << ''gallons
and it has'' << mileage << ''miles.
'';
}


// The Car4 class uses a different twist in implementing its uses
// relationship. When each Car4 object is built, it is told who its
// gas station is. This gas station is stored in the Car4 object for
// later use. In this case, the class will make a copy of the gas
// station, which is safer because it avoids problems of the gas
// station given to the Car4 object being destroyed before the Car4
// object. If data sharing is desired, then only the pointer,
// and not the stuff to which it points, should be copied. For an
// example of this form of shallow copying which also provides a
// safety mechanism, see the Air Traffic Controller example in
// Chapter 9 (Example #3). Do not confuse association with
// containment by reference. While the Car4 class does have a
// pointer to a GasStation as a data member, this is not containment
// by reference, it is association through a referential attribute.
// While C++ does not let us distinguish these two relationships,
// the distinction is available AND important to designers. If this
// were containment, we could ignore GasStations at some high
// level of design. The fact that it is assocation means we cannot.
class Car4 {
private:
     double milesPerGallon;
     double fuelCapacity;
     double gasInTank;
     double mileage;
     GasStation* myStation;
public:
     Car4(GasStation*, double, double, double=0.0, double=0.0);
     ~Car4();
     void get_gasoline(double);
     void drive(double);
     void print();
};
// Note the constructor copying the GasStation passed to it using
// the default copy constructor for GasStation. (Note: GasStation
// is a fixed-sized class so the default copy constructor does not
// cause any memory leakage/heap corruption problems. See Appendix B
// for a more thorough explanation of memory leakage/heap corruption
// problems of copy constructors/assignment operators.
Car4::Car4(GasStation* station, double mpg, double capacity,
                        double starting_gas, double miles)
{
     myStation = new GasStation(*station);
     milesPerGallon = mpg;
     fuelCapacity = capacity;
     gasInTank = starting_gas;
     mileage = miles;
}


Car4::~Car4()
{
     delete myStation;
}


void
Car4::drive(double miles)
{
     double gas_needed;

     gas_needed = miles / milesPerGallon;
     if (gas_needed > gasInTank) {
          mileage += gasInTank * milesPerGallon;
          cerr << ''You ran out of gasoline after travelling '';
          cerr << gasInTank * milesPerGallon << '' miles.
'';
          gasInTank = 0.0;
     }
     else {
          mileage += miles;
          gasInTank -= gas_needed;
          if (gasInTank< 0.1* fuelCapacity) {
                     cerr << ''Warning: You have only enough gas
to go '';
                     cerr << gasInTank * milesPerGallon <<
 '' miles.
'';
           }
       }
}


// Note the use of the referential attribute in the get_gasoline method.
void
Car4::get_gasoline(double money)
{
     double gas_received;
   gas_received = myStation->give_gasoline(money);
   if (gas_received == 0) {
        cerr << ''Sorry the gas station was out of gas.
'';
   }
   else {
        gasInTank += gas_received;
        if (gasInTank > fuelCapacity) {
                   cerr << ''You spilled '' << (gasInTank -
fuelCapacity);
                   cerr << '' gallons of gas on the ground.
'';
                   gasInTank = fuelCapacity;
        }
   }
}


void
Car4::print()
{
     cout << ''The car gets '' << milesPerGallon;
     cout << '' miles per gallon.
It currently has '';
     cout << gasInTank << '' gallons of gasoline on board.
'';
     cout << ''Its maximum fuel capacity is '' << fuelCapacity;
     cout << '' gallons
and it has '' << mileage << '' miles.
'';
}


// The Car5 class implements its uses relationship by asking a
// third-party class, in this case a Map object. Of course, asking
// a third-party only postpones the answer to the question, ''How
// does a class know the name of the object it wishes to use? ''We
// will have to use one of the other five methods of implementing
// uses. In this case, I chose containment, i.e., Car 5 contains a
// Map object.

// We first implement our Map class. I have chosen a naive algorithm
// for finding a GasStation on a Map. Each Map object has four
// quadrants and a car has an x, y location. The map takes the x-
// and y-coordinates of a car and returns one of four stations.
// While it is true that this is naive (the attentive will notice
// that a Map has no way of getting gasoline deliveries to its
// GasStations), it is sufficient to demonstrate uses.
class Map {
private:
     GasStation* quadrant[4];
public:
     Map();
     ~Map();
     GasStation* get_station(int, int);
};

// The constructor for Map simply builds the four GasStations , one
// for each quadrant.
Map::Map()
{
      quadrant[0] = newGasStation(1.45, 1000);
      quadrant[1] = newGasStation(1.30, 200);
      quadrant[2] = newGasStation(2.10, 10000);
      quadrant[3] = newGasStation(1.10, 678);
}

Map::~Map()
{
     int i;

     for (i=0; i < 4; i++) {
          delete quadrant[i];
     }
}


// When a Car5 object asks the Map for a station, it gives the Map its
// x- and y-coordinates. The Map returns the appropriate
// GasStation.
GasStation*
Map::get_station(int x, inty)
{
     if (x > 0) {
          if (y > 0)
                     return(quadrant[0]);
          else
                     return(quadrant[3]);
     }
     else {
          if (y > 0)
                     return(quadrant[1]);
          else
                     return(quadrant[2]);
     }
}

// The Car5 class contains the Map by value. Even if it contained it
// by reference, this would still be a containment relationship.
// It would not be association through a referential attribute
// like the Car4 class. The difference is significant at design time.
// I can state that Maps are not as important a class in this domain
// as Car5 and GasStation objects because the former is contained in
// a top-level class and does not have to be discussed at high-level
// design time.
class Car5 {
private:
     int loc_x, loc_y;
     double milesPerGallon;
     double fuelCapacity;
     double gasInTank;
     double mileage;
     Map myMap;
public:
      Car5(int, int, double, double, double=0.0, double=0.0);
      void get_gasoline(double);
      void drive(double);
      void print();
};

// The constructor for Map is called automatically before the
// constructor for each Car5 object. The destructor for Map will
// likewise be called whenever a Car5 object is destroyed.
Car5::Car5(int x, int y, double mpg, double capacity,
                            double starting_gas, double miles)
{
       loc_x = x;
       loc_y = y;
       milesPerGallon = mpg;
       fuelCapacity = capacity;
       gasInTank = starting_gas;
       mileage = miles;
}

void
Car5::drive(double miles)
{
      double gas_needed;

      gas_needed = miles / milesPerGallon;
      if (gas_needed > gasInTank) {
           mileage += gasInTank * milesPerGallon;
           cerr << ''You ran out of gasoline after travelling '';
           cerr << gasInTank * milesPerGallon << '' miles.
'';
           gasInTank = 0.0;
      }
      else {
           mileage += miles;
           gasInTank -= gas_needed;
           if (gasInTank < 0.1 * fuelCapacity) {
                      cerr << ''Warning: You have only enough gas
to go '';
                      cerr << gasInTank * milesPerGallon <<
''miles.
'';
         }
     }
}


// Note the use of the contained, third-party object called MyMap to
// get a GasStation for use by the get_gasoline method.
void
Car5::get_gasoline(double money)
{
     double gas_received;
    GasStation *myStation = myMap.get_station(loc_x, loc_y);

    gas_received = myStation->give_gasoline(money);
    if (gas_received == 0) {
         cerr << ''Sorry the gas station was out of gas.
'';
    }
    else {
         gasInTank += gas_received;
         if (gasInTank>fuelCapacity) {
                     cerr << ''You spilled '' << (gasInTank -
fuelCapacity);
                     cerr << '' gallons of gas on the ground.
'';
                     gasInTank = fuelCapacity;
         }
     }
}

void
Car5::print()
{
     cout << ''The car at location ('' << loc_x << '', '' << loc_y <<
'') gets '';
     cout << milesPerGallon << '' miles per gallon.
It currently
has '';
     cout << gasInTank << '' gallons of gasoline on board.
'';
     cout << ''Its maximum fuel capacity is '' << fuelCapacity;
     cout << '' gallons
and it has '' << mileage << '' miles.
''
}

void
main()
{
// The following code tests the Car1 class :
      GasStation g1(1.18, 300), g2(1.45, 2300);
      Car1 mycar1(22, 18, 10);

      mycar1.print();
      mycar1.drive(200);
      mycar1.print();
      mycar1.drive(100);
      mycar1.print();
      mycar1.get_gasoline(g1, 15.00);
      mycar1.print();

// The following code tests the Car2 class :

      Car2 mycar2(30, 20, 3);

      mycar2.print();
      mycar2.drive(200);
      mycar2.print();
      mycar2.get_gasoline(20.00);
      mycar2.print));
      mycar2.get_gasoline(10.00);
      mycar2.print();


// The following code tests the Car3 class:

      Car3 mycar3(9, 13, 5);

      mycar3.print();
      mycar3.drive(38);
      mycar3.print();
      mycar3.get_gasoline(10.00);
      mycar3.print();
      mycar3.drive(150);
      mycar3.print();

// The following code tests the Car4 class:

      Car4 mycar4(&g2, 18, 25, 12);

      mycar4.print();
      mycar4.get_gasoline(15.00);
      mycar4.print();
      mycar4.drive(150);
      mycar4.print();
      mycar4.get_gasoline(3.69);
      mycar4.print();

// The following code tests the Car5 class:

      Car5mycar5(10, -34, 35, 18, 15);

      myCar5.print();
      myCar5.drive(250);
      mycar5.print();
      myCar5.drive(250);
      myCar5.print();
      myCar5.get_gasoline(10.00);
      myCar5.print();
      myCar5.get_gasoline(20.00);
      mycar5.print();
}

Selected C++ Example #9

//Example #9 

// This example illustrates the reuse of a linked list
// abstraction in the development of a sorted linked list.
// The linked list has insert and remove methods, which are
// monomorphic. They make use of two polymorphic, protected
// find functions to implemement the differences between the
// two abstractions. Example 4b will illustrate the problem
// with software reuse through inheritance by introducing a
// linked ring class to the design. None of this linked list's
// code will be reusable when we introduce the linked ring
// (the bad news). But with the introduction of a new
// protected abstraction for testing the end of a list, it will
// be able to reuse the abstraction (the good news).

# include <iostream.h>
# include <new.h>

// We show that this class is a good candidate for
// parameterization. We will turn it into a template in
// the selected C++ examples of Chapter 8.
typedef int DATUM;

// The linked list class contains a local, protected data
// type for the nodes. It is protected instead of private
// because the sorted linked list needs access to the type.
// The linked list class also provides two protected polymorphic
// methods for finding (insert and remove, respectively), which
// are used by the insert and remove methods. These will be
// overridden in the derived class ''sorted linked list.''
// The protected get_head method allows the hiding of the
// head pointer' s implementation within the base class ''linked
// list'' while still allowing the derived class to access the
// abstraction.
class LinkedList {
protected:
     struct Node {
         DATUM data;
         Node* next;
     public:
         Node(const DATUM&);
     };
     virtual Node* ifind(const DATUM&) const;
     virtual Node* rfind(const DATUM&) const;
     Node* get_head() const;
private:
     Node* head;
     int length;
public:
     LinkedList();
     ~LinkedList();
     int insert(const DATUM&);
     int remove(const DATUM&);
     void traverse() const;
     virtual char* type();
     void test();
};


LinkedList::Node*
LinkedList::get_head() const
{
      return(head);
}


LinkedList::Node*
LinkedList::ifind(const DATUM&) const
{
     Node* temp = head;

     if (temp == NULL) {
          return(NULL);
     }
     while(temp->next != NULL) {
         temp = temp->next;
     }
     return(temp);
}

// The remove find method returns three possible values. A
// return of -1 implies that the object to be removed is at
// the head of the list, NULL implies the item is not in
// the list, any other value implies the pointer returned
// is a pointer to the preceding node in the list.
LinkedList::Node*
LinkedList::rfind(const DATUM& bad_item) const
{
     Node* found = head, *pre_found = (Node*) -1;


     while (found != NULL) {
         if (found->data == bad_item) {
                    return(pre_found);
         }
         pre_found = found;
         found = found->next;
     }
     return(NULL);
}


LinkedList::Node::Node(const DATUM& new_data)
{
     data = new_data;
     next = NULL;
}


LinkedList::LinkedList()
{
     head = NULL;
     length = 0;
}


LinkedList::~LinkedList()
{
     Node* temp;

     while (head != NULL) {
         temp = head;
         head = head->next;
         delete temp;
     }
}


int
LinkedList::insert(const DATUM& new_item)
{
     Node* temp = ifind(new_item);
     Node* new_node = new Node(new_item);

     if (temp == NULL) {
          new_node->next = head;
          head = new_node;
     }
     else {
          new_node->next = temp->next;
          temp->next = new_node;
     }
     return(++length);
}


int
LinkedList::remove(const DATUM& bad_item)
{
     Node* found = rfind(bad_item);
     Node* temp;

     if (found == NULL) {
          return(0);
     }
     else if (found == (Node*) -1) {
          length--;
          temp = head;
          head = head->next;
          delete temp;
          return(1);
}
else {
     length--;
     temp = found->next;
     found->next = found->next->next;
     delete temp;
        return(1);
    }
}


void
LinkedList::traverse()const
{
     Node* temp = head;

     cout << ''('';
     while (temp != NULL) {
         cout << temp->data << '' '';
         temp = temp->next;
     }
     cout << '')
'';
}

char*
LinkedList::type()
{
     return(''LinkedList''),
}


// Note the major reuse of the LinkedList abstraction in this
// class. The SortedLinkedList need only redefine three
// protected polymorphic methods from the LinkedList.
class SortedLinkedList : public LinkedList {
protected:
     Node* ifind(const DATUM&) const;
     Node* rfind(const DATUM&) const;
public:
     char* type();
};


LinkedList::Node*
SortedLinkedList::ifind(const DATUM& new_item) const
{
     Node * found = get_head(), *pre_found = NULL;

     while (found != NULL && found->data < new_item) {
         pre_found = found;
         found = found->next;
     }
     return(pre_found);
}


LinkedList::Node*
SortedLinkedList::rfind(const DATUM& bad_item) const
{
     Node* found = get_head(), *pre_found = (Node*) -1;
     while (found != NULL && found->data <= bad_item) {
         if (found->data == bad_item) {
                    return(pre_found);
         }
         pre_found = found;
         found = found->next;
     }
     return(NULL);
}

char*
SortedLinkedList::type()
{
     return(''SortedLinkedList''),
}


void
main()
{
          LinkedList x;
          SortedLinkedList y;

          cout << ''Testing the LinkedList
'';
          x.test();
          cout << ''

Testing the SortedLinkedList
'';
          y.test();
}



void
LinkedList::test()
{
     char c = 'a';
     int size;
     DATUM num;

     while (c != 'q') {
         cout << ''Your '' << type() << '' looks like: '';
         traverse();
         cout << ''What's your pleasure i)nsert, d)elete, q)uit? '';
         cin >> c;
         if (c == 'i') {
                    cout << ''Number to insert: '';
                    cin >> num;
                    size = insert(num);
                    cout << ''Number of elements on the '' << type();
                    cout << '' is '' << size << ''
.'';
         }
         else if (c == 'd') {
                  cout << ''Number to delete: '';
                  cin >> num;
                  size = remove(num);
                  if (size== 0) {
                          cout << ''Couldn't find the number ''
<< num;

                           cout << '''' in the '' << type() <<
'' .
'';
                  }
                  else {
                           cout << ''Found and deleted the num
ber '' << num;
                           cout << '''' from the '' << type() <<
''.
'';
                  }
         }
    }
}

Selected C++ Example #10

// Example #10 

// This example illustrates the necessary changes to Example
// #4a when adding a LinkedRing as a new derived class of
// LinkedList.

#include <iostream.h>
#include <new.h>

typedef int DATUM;

// The LinkedRing class requires that we abstract out the
// test for the end of a list into a separate, protected
// polymorphic ''at_end()''method. Since, in a LinkedRing,
// the beginning and end tests of a list are equivalent (i.e.,
// the pointer you have is equal to the head), the loops in
// the linked list methods had to be inverted from while to
// do-while. In addition to these changes, we had to add a
// protected polymorphic method to handle the different
// initialization of a node' s next pointer. LinkedList assigns
// it to NULL, while a LinkedRing assigns it to itself (to make
// a ring of one node).

// Requiring a change to the base class when a new derived
// class is added to an existing hierarchy occurs more often
// than not. This certainly has caused some reusability
// problems when inheritance is involved.
class LinkedList {
protected:
     struct Node {
         DATUM data;
         Node* next;
     public:
         Node(const DATUM&);
     };
     virtual Node* ifind(const DATUM&) const;
     virtual Node* rfind(const DATUM&) const;
     virtual int at_end(const Node*) const;
     virtual void set_next(Node*);
     void cleanup();
     Node* get_head() const;
private:
     Node* head;
     int length;
public:
     LinkedList();
     ~LinkedList();
     int insert(const DATUM&);
     int remove(const DATUM&);
     void traverse() const;
     virtual char* type();
     void test();
};

// The at_end method for the LinkedList simply checks the
// current_ptr against NULL.
int
LinkedList::at_end(const Node* current_ptr) const
{
     return(current_ptr == NULL);
}

// The set_next method is used to distinguish the LinkedList
// versus LinkedRing behavior of creating nodes. The
// LinkedList wants a NULL next pointer, while the LinkedRing
// wants the next point to reference the object that owns
// it.
void
LinkedList::set_next(Node* new_node)

{
     new_node->next = NULL;
}


LinkedList::Node*
LinkedList::get_head() const
{
     return(head);
}


LinkedList::Node*
LinkedList::ifind(const DATUM&) const
{
      Node* temp = head;

      if (temp == NULL) {
           return(NULL);
      }
      while (!at_end(temp->next)) {
          temp = temp->next;
      }
      return(temp);
}

// The remove find method returns three possible values. A
// return of -1 implies that the object to be removed is at
// the head of the list, NULL implies the item is not in
// the list, any other value implies the pointer returned
// is a pointer to the preceding node in the list.
LinkedList::Node*
LinkedList::rfind(const DATUM& bad_item) const
{
     Node* found = head, *pre_found = (Node*) -1;

     if (head != NULL) {
          do {
                      if (found->data == bad_item) {
                                 return(pre_found);
                      }
                      pre_found = found;
                      found = found->next;
          } while (!at_end(found));
     }
     return(NULL);
}



LinkedList::Node::Node(const DATUM& new_data)
{
     data = new_data;
     next = NULL;
}


LinkedList::LinkedList()
{
     head = NULL;
     length = 0;
}


LinkedList::~LinkedList()
{
     if (head != NULL)
        cleanup();
}


void
LinkedList::cleanup()
{
     Node *temp, *current_ptr = head;
     do {
          temp = current_ptr;
          current_ptr = current_ptr->next;
          delete temp;
     } while (!at_end(current_ptr));
     head = NULL;
}

int
LinkedList::insert(const DATUM& new_item)
{
     Node* temp = ifind(new_item);
     Node* new_node = new Node(new_item);


     if (head == NULL) {          //Insertion into an empty list
          set_next(new_node);
          head = new_node;
     }
     else if (temp == NULL) {    // Insertion at the head
          new_node->next = head;
          head = new_node;
     }
     else {                     //All others
          new_node->next = temp->next;
          temp->next = new_node;
     }
     return(++length);
}

// Remove gets more complicated because LinkedRings are a
// pain when you need to remove their head node. This
// method does not remove the head node until it is the last
// thing in the list. It will copy the second node' s data
// into the head node and delete the second node instead.
int
LinkedList::remove(const DATUM& bad_item)
{
     Node* found = rfind(bad_item);
     Node* temp;

     if (found == NULL) {         // The item is not in the list.
          return(0);
     }
// The item is in the list, so decrement the count. Then check
// if it is the head, which we want to delete. If it is, then
// check if it is the last item in the list. If so, then
// simply get rid of it. If not, we need to copy the second
// node's data and throw away the second node (to preserve the
// last node in the list's next pointer to the head).
     length--;
     if (found == (Node*) -1) {
          if (!at_end(head->next)){
                     head->data = head->next->data;
                     temp = head->next;
                     head->next = head->next->next;
                     delete temp;
          }
          else {
                     delete head;
                     head = NULL;
          }
          return(1);
     }
     else {
          temp = found->next;
          found->next = found->next->next;
          delete temp;
          return(1);
     }
}

void
LinkedList::traverse()const
{
     Node* temp = head;

     cout << ''('';
     if (head != NULL) {
          do {
                       cout << temp->data << '' '';
                       temp = temp->next;
          } while(!at_end(temp));
     }
     cout << '')
'';
}


char*
LinkedList::type()
{
     return(''LinkedList''),
}


// Note the major reuse of the LinkedList abstraction in this
// class. The SortedLinkedList need only redefine three
// protected polymorphic methods from the LinkedList.
class SortedLinkedList:public LinkedList {
protected:
     Node* ifind(const DATUM&) const;
     Node* rfind(const DATUM&) const;
public:
     char* type();
};

LinkedList::Node*
SortedLinkedList::ifind(const DATUM& new_item) const
{
     Node *found = get_head(),*pre_found = NULL;

     while (found != NULL && found->data < new_item) {
         pre_found = found;
         found = found->next;
     }
     return(pre_found);
}


LinkedList::Node*
SortedLinkedList::rfind(const DATUM& bad_item) const
{
     Node* found = get_head(), *pre_found = (Node*)-1;


     while (found != NULL && found->data <= bad_item) {
         if (found->data == bad_item) {
                    return(pre_found);
         }
         pre_found = found;
         found = found->next;
     }
     return(NULL);
}

char*
SortedLinkedList::type()
{
     return(''SortedLinkedList''),
}


class LinkedRing:public LinkedList {
protected:
     int at_end(const Node*) const;
     void set_next(Node*);
public:
     ~LinkedRing();
     char* type();
};


LinkedRing::~LinkedRing()
{
     if (get_head() != NULL)
          cleanup();
}


// The at_end method of a LinkedRing checks if the current
// pointer is equal to the head pointer.
int
LinkedRing::at_end(const Node* current_ptr) const
{
     return(current_ptr == get_head());
}

void
LinkedRing::set_next(Node* new_node)
{
     new_node->next = new_node;
}

char*
LinkedRing::type()
{
     return(''LinkedRing''),
}



void
main()
{
     LinkedList x;
     SortedLinkedList y;
     LinkedRing z;

     cout << ''Testing the LinkedList.
'';
     x.test();
     cout << ''

Testing the SortedLinkedList.
'';
     y.test();
     cout << ''

Testing the LinkedRing.
'';
     z.test();
}

void
LinkedList::test()
{
     char c='a';
     DATUM num;
     int size;
      while (c != 'q') {
           cout << ''Your '' << type() << '' looks like: '';
           traverse();
           cout << ''What's your pleasure i)nsert, d)elete, q)uit? '';
           cin >> c;
           if (c == 'i') {
                      cout << ''Number to insert: '';
                      cin >> num;
                      size = insert(num);
                      cout << ''Number of elements on the '' << type(); 
                      cout << '' is '' << size << -.
"
           }
           else if (c == 'd') {
                      cout << ''Number to delete: '';
                      cin >> num;
                      size = remove(num);
                      if (size == 0) {
                                  cout << ''Couldn't find the number
'''' << num;
                                  cout << '''' in the '' << type() <<
''.
";
                    }
                    else {
                                  cout << ''Found and deleted the
number '''' << num;
                                  cout << '''' from the '' << type()
<< ".
";
                     }
        }
    }
}

Selected C++ Example #15

// Example #15 
// This C++ example illustrates the use of virtual multiple
// inheritance to force data and constructor sharing of a
// common base class. The example comes from the graduate
// student problem posed in Chapter 6.

#include <iostream.h>
#include <string.h>

// Constants used in this example.

const int name_len = 50;
const int ssn_len = 12;
const int course_len = 20;
const int student_len = 50;


// The Person class is the common base class, which both
// the student and instructor classes inherit. It would
// be inherited twice in the graduate student class if it
// were not for virtual inheritance at both the student and
// instructor levels.

// Note the protected accessor methods. The assumption is
// that one or more derived classes of Person need access
// to name and social security number. These methods are a
// bit dangerous in that they give pointers to the internals
// of Person. The fact that they are constant does not help
// much, since a user could cast the return value to a
// regular char pointer. The only safe way to protect the
// state of the Person class would be to force the user to
// pass the Person object a buffer. The Person object would
// then copy the name/ssn into the required buffer; e.g.,
//         void get_name(char* buf);
//         void get_ssn(char* buf);
// This copying is quite expensive. I would use the above
// forms if I were making these public accessors. Since they
// are protected, I'm willing to gamble that I do not have
// pathological implementors of derived classes. The choice
// depends on your level of paranoia.

// The copy constructor is not required here, since Person
// is a fixed-size class. I place it here for readability
// since it will be called further down the hierarchy.

class Person {
     char name[name_len];
     char ssn[ssn_len];
protected:
     const char* get_name() { return (name); }
     const char* get_ssn() { return(ssn); }
     Person();
public:
     Person(char*, char*);
     Person(Person&);
     void print();
};

// The first uncomfortable item we need to deal with is
// the requirement that the Person constructor possess this
// constructor, which, in fact, will never be called. We
// want to be able to initialize a graduate student given
// only a student object and a salary. This requires us to
// have a protected constructor for the Instructor class,
// which takes a salary (only) as an argument. Such a
// constructor will have an implied call to this constructor
// (see the protected instructor constructor below) , but
// since the Instructor virtually inherits from Person, this
// constructor will never be called. Without it, however,
// the example will not compile.
// We also want to build a graduate student from only an
// Instructor. This requires a protected student
// constructor that takes no arguments. The same problem that
// occurs with the instructor constructor occurs here.
Person::Person()
{
}

Person::Person(char* n, char* social_num)
{
     strncpy(name, n, name_len);
     strncpy(ssn, social_num, ssn_len);
}

Person::Person(Person& rhs)
{
     strcpy(name, rhs.name);
     strcpy(ssn, rhs.ssn);
}

void
Person::print()
{
     cout << ''Hi! My name and SSN is '' << name;
     cout << '' '' << ssn << ''.
'';
}

// The Student class contains a list of courses, each of
// which contains a name and a grade.
class Course {
     char name[name_len];
     int grade;
public:
     Course(char*, int);
     Course(Course&);
     void print(const char*);
};

Course::Course(char* n, int g)
{
     strncpy(name, n, name_len);
     grade = g;
}

Course::Course(Course& rhs)
{
     strcpy(name, rhs.name);
     grade = rhs.grade;
}


void
Course::print(const char* student)
{
     cout << student << '' received a '' << grade;
     cout << '' in the course '' << name << ''
'';
}


// The Student class virtually inherits from Person. The
// Student class is advertising that it is willing to share
// its Person base object with any other Person base object
// in a multiple inheriting derived class (the GradStudent, in
// this case). This virtual keyword has nothing to do with
// polymorphism. In fact, there is no polymorphism in this
// example. The behavior of Student is defined to be the same
// regardless of the virtual keyword; its implementation
// changes, however. Virtual inheritance will affect only the
// children of the virtually inheriting class.
class Student : virtual public Person {
      Course* course_list[course_len];
      double GPA;
      int grade_sum;
      int course_num;
protected:
      Student();
public:
      Student(char*, char*);
      Student(Student&);
      ~Student();
      int add_course(char*, int);
      void print();
};

// This protected constructor is called only indirectly from
// the graduate student constructor. The implied call to a
// Person constructor, which is callable with zero arguments,
// necessitates the protected Person constructor above. But,
// since this constructor is never called directly, that
// person constructor will never be executed. The result is
// a required constructor that can never be called.
Student::Student()
{
     int i;

     GPA = 0.0;
     grade_sum = course_num = 0;
     for (i=0; i < course_len; i++) {
          course_list[i]=NULL;
     }
}

// If this constructor is called directly, i.e., someone is
// building a Student, then it will call the Person
// constructor. If it is called indirectly from a GradStudent
// constructor, then the Person constructor will not be called.
// The GradStudent constructor will be responsible for the
// call to the Person constructor.
Student::Student(char* name, char* ssn) : Person(name, ssn)
{
     int i;

     GPA = 0.0;
     grade_sum = course_num = 0;
     for (i=0; i < course_len; i++) {
          course_list[i] = NULL;
     }
}


Student::Student(Student& rhs) : Person(rhs)
{
     int i;

     GPA = rhs.GPA;
     grade_sum = rhs.grade_sum;
     course_num = rhs.course_num;
     for (i=0; i < course_num; i++) {
          course_list[i] = new Course(*rhs.course_list[i]);
     }
}


Student::~Student()
{
     int i;

     for (i=0; i < course_num; i++) {
          delete course_list[i];
     }
}


int
Student::add_course(char* name, int grade)
{
     course_list[course_num++] = new Course(name, grade);
     grade_sum += grade;
     GPA = grade_sum / course_num;
     return(course_num);
}


void
Student::print()
{
     int i;

     cout << ''Student Name: '' << get_name() << ''
'';
     cout << ''Social Security Number: '' << get_ssn() << ''
'';
     cout << ''Courses: 
'';
     for (i=0; i < course_num; i++) {
          cout << ''	'';
          course_list[i]->print(get_name());
     }
     if (course_num) {
          cout << ''Grade Point Average: '' << GPA << ''
'';
     }
     cout << ''

'';
}


// The Instructor class must also virtually inherit if the
// Person object is to be shared at the GradStudent level.
// All base classes wishing to share a common base class
// in a multiple inheriting derived class must virtually
// inherit.
class Instructor : virtual public Person {
     double salary;
     Student* students[student_len];
     int student_num;
protected:
     Instructor(double);
public:
     Instructor(char*, char*, double);
     Instructor(Instructors);
     ~Instructor();
     int add_student(Student&);
     void print();
};

// This protected constructor has an implied call to a Person
// constructor callable with zero arguments. This required
// us to define such a constructor above. But since this
// constructor is protected, it will only be called by derived
// constructors. When called indirectly, this constructor will
// NOT call Person's constructor. The result is a needed
// constructor for compiling, which is never really called.
Instructor::Instructor(double sal)
{
     int i;

     salary = sal;
     student_num = 0;
     for (i=0; i < student_len; i++) {
          students[i] = NULL;
     }
}

Instructor::Instructor(char* name, char* ssn, double pay)

     : Person(name, ssn)
{
     int i;
     student_num = 0;
     salary = pay;
     for (i=0; i < student_len; i++) {
         students[i] = NULL;
     }
}


Instructor::Instructor(Instructor& rhs) : Person(rhs)
{
     int i;

     salary = rhs.salary;
     student_num = rhs.student_num;
     for (i=0; i < rhs.student_num; i++) {
          students[i] = new Student(*rhs.students[i]);
     }
}

Instructor::~Instructor()
{
     int i;

     for (i=0; i < student_num; i++) {
          delete students[i];
     }
}

int
Instructor::add_student(Student&new_student)
{
     students[student_num++] = new Student(new_student);
     return(student_num);
}

void
Instructor::print()
{
     int i;

     cout << ''Instructor Name: '' << get_name() << ''
'';
     cout << ''Salary: '' << salary << ''
'';
     if (student_num) {
          cout << ''Cost per Student: '' << salary/student_num <<
''
'';
     }
     cout << ''Students: 
'';
     for (i=0; i < student_num; i++) {
          students[i]->print();
     }
     cout << ''

'';
}

// The Grad_student class multiple inherits from Instructor
// and Student. Since they both virtually inherit from
// the Person class, they will share the same Person object.
// Also, their constructors will not call the Person
// constructor. The Grad_student is responsible for that
// initialization, as we will see below.
// The Grad_student class has three constructors: one that
// builds a graduate student from a name, social security
// number, and salary; one that builds a graduate student
// from a student object and a salary; and a third that
// builds a graduate student from an instructor.
class Grad_student : public Instructor, public Student {
public:
     Grad_student(char*, char*, double);
     Grad_student(Student&,double);
     Grad_student(Instructor&);
     void print();
};

// This constructor requires three additional constructor
// calls. The first constructor to be called will be
// the Person constructor, which takes a name and social
// security number. (Because all virtually inheriting base
// classes are called first.) The second constructor will
// be the Instructor constructor because it was the first
// class to be inherited in the class definition above. (Note:
// The order that constructor calls appear in the constructor
// definition is irrelevant. The importance is the order of
// the class definition.) Lastly, a call to the protected
// Student constructor callable with zero arguments is made.
// It is important to note that neither the Student or
// Instructor constructors will call their Person constructor.
// They would have made these calls if they were called
// directly.
Grad_student::Grad_student(char* name, char* ssn, double salary)
             : Instructor(salary), Person(name, ssn)
{
}

// This constructor calls Person' s copy constructor, followed
// by Instructor's constructor, which takes a salary, followed
// by Student's copy constructor.
Grad_student::Grad_student(Student& rhs, double salary)
             : Student(rhs),Instructor(salary),Person(rhs)
{
}

// This constructor calls Person's copy constructor, followed
// by Instructor's copy constructor, followed by Student's
// protected constructor callable with zero arguments.
Grad_student::Grad_student(Instructor&rhs)
                                : Instructor(rhs), Person(rhs)
{
}

// The graduate student must resolve ambiguity on the print
// method between Student and Instructor. In this case it
// chooses a boring solution. It could have been more
// elaborate by calling each base class method. It is useful
// to note that there is no ambiguity on the call to get_name()
// even though it can be inherited via student or instructor.
// The compiler recognizes that both paths give it the same
// function.
void
Grad_student::print()
{
     cout << ''I'm just a grad student named: '';
     cout << get_name() << ''
'';
// Could have printed both like:
//   Student::print();
//   Instructor::print();
}

void
main()
{
      Student x(''Arthur J. Riel'', ''038-48-9922''),
      Student y(''John Doe'', ''234-78-9988''),

      x.print();
      x.add_course(''Biology 101'', 94);
      x.add_course(''Physics 307'', 35);
      x.add_course(''Computer Science 101'', 98);
      x.add_course(''Advanced C++", 78);
      x.print();

      y.add_course(''Biology 207'', 87);
      y.add_course(''Organic Chemistry'', 67);
      y.add_course(''English 109'', 100);

      Student z = x;
      z.add_course(''Introduction to Latin'', 89) ;
      z.add_course(''Running for Fun'', 84);
      z.add_course(''Basket Weaving 101'', 100);

      Instructor loco(''Chris Roth'', ''934-76-4365'', 29400);

      loco.add_student(x);
      loco.add_student(y);
      loco.add_student(z);

      loco.print();

// Build a graduate student from a student.
     Grad_student g1(x, 14800);

// Build a graduate student from scratch.
     Grad_student g2(''Bob Miller'', ''888-44-7765'', 34900L);

// Build a graduate student from an instructor.
     Grad_student g3(loco);

     g3.add_course(''Post-Doc 101", 82);
     g3.add_student(x);

   cout << ''

Printing Grad Student g1 as a student
'';
   ((Student*) &g1)->print();
   cout << ''Printing Grad Student g1 as a Instructor
'';
   ((Instructor*) &g1)->print();
   cout << ''Printing Grad Student g1 as a grad student
'';
   g1.print();

   cout << ''

Printing Grad Student g2 as a student
'';
   ((Student*) &g2)->print();
   cout << ''Printing Grad Student g2 as a Instructor
'';
   ((Instructor*) &g2)->print();
   cout << ''Printing Grad Student g2 as a grad student
'';
   g2.print();

   cout << ''

Printing Grad Student g3 as a student
'';
   ((Student*) &g3)->print();
   cout << ''Printing Grad Student g3 as a Instructor
'';
   ((Instructor*) &g3)->print();
   cout << ''Printing Grad Student g3 as a grad student
'';
   g3.print();
}

Selected C++ Example #17

//Example #17 

// This example code shows the attempt to implement three
// different lists (meal lists, airplane lists, and dog lists)
// with one implementation of the list code. The general approach
// is to use inheritance to weaken the type-checking mechanism
// of C++ by making all data types that want to be in a list
// inherit from a common base class (in this case the class
// ListItem). The common base class captures any necessary
// operations that the LinkedList class might need (in this
// case, print() and type()). The problem with weak type
// checking is that a wrong derived object might end up on
// the wrong list. The benefits of mixed list and strong
// type checking are captured in the next example, which
// reimplements this code with C++ templates.
// This example could be extended to include type checking.
// The algorithm for doing this is to have each LinkedList
// object remember the data type of the first argument inserted.
// Once this is recorded all other insertions will check the
// type of the object being added to the list with the stored
// value. Of course, this algorithm makes it impossible to
// have mixed data type lists anywhere in the application.

#include <iostream.h>
#include <new.h>

// ListItem is the class responsible for weakening the type
// checking of C++. We make all of our list element classes
// inherit from this common base class. The LinkedList will
// be of pointers to this base class. Note that all ListItems
// must know how to give their type and print themselves. class ListItem {
public:
     virtual void print(ostream& o = cout) = 0;
     virtual const char* type() = 0;
};

// The derived classes Dog, Meal, and Airplane are only
// skeleton classes to simplify their implementation.
class Dog : public ListItem {
public:
     void bark();
     void bite();
     void print(ostream&o = cout);
     const char* type();
};

void
Dog::bark()
{
     cout << ''Bow Wow
'';
}

void
Dog::bite()
{
     cout << ''Ouch!!!
'';
}

void
Dog::print(ostream& o)
{
     o << ''I am a dog!
'';
}

const char*
Dog::type()
{
      return(''Dog''),
}

class Meal : public ListItem {
public:
     void eat();
     void print(ostream& o = cout);
     const char* type();
};

void
Meal::eat()
{
     cout << ''Crunch ... Munch ... Crunch ... 
'';
}

void
Meal::print(ostream& o)
{
     o << ''I'm a meal
'';
}

const char*
Meal::type()
{
     return(''Meal''),
}

class Airplane : public ListItem {
public:
      void fly();
      void print(ostream& o = cout);
      const char* type();
};

void
Airplane::fly()
{
     cout << ''Va-a-a--room!!!
'';
}

void
Airplane::print(ostream& o)
{
     o << ''I'm an airplane!
'';
}

const char*
Airplane::type()
{
     return(''Airplane''),
}



typedef ListItem* DATUM;


class LinkedList {
     struct Node {
         DATUM data;
         Node* next;
     public:
         Node(DATUM&);
     };

     Node* head;
     int len;
public:
     LinkedList();
     ~LinkedList();
     int insert(DATUM&);
     DATUM remove();
     void traverse()const;
     int length();
};

LinkedList::Node::Node(DATUM& new_data)
{
     data = new_data;
     next = NULL;
}


LinkedList::LinkedList()
{
     head = NULL;
     len = 0;
}


LinkedList::~LinkedList()
{
     Node* temp;

     while (head != NULL) {
         temp = head;
         head = head->next;
         delete temp;
     }
}
int
LinkedList::insert(DATUM& new_item)
{
     Node* temp = head;

     if (temp == NULL) {
          head = new Node(new_item);
     }
     else {
          while (temp->next != NULL) {
                     temp = temp->next;
          }
          temp->next = new Node(new_item);
     }
     return(++len);
}


// The remove method needs something to return if the list
// is empty. We deal with this problem by creating a dummy
// DATUM as an internal variable within the remove method.
DATUM
LinkedList::remove()
{
     Node* temp = head;
     static DATUM bad_item;
     DATUM retval;

     if (temp == NULL) {
          return(bad_item);
     }
     else {
          retval = head->data;
          head = head->next;
          len--;
          delete temp;
          return(retval);
     }
}


// Notice that the traverse method sends a message to the
// data in each node to print itself. This sets up a requirement
// that any data type that wants to be in a LinkedList needs
// a print method (this constraint is enforced in the base
// class ''ListItem''). An even more important consideration is
// the fact that the syntax is different if DATUM is a pointer
// or nonpointer. Nonpointers would use a dot, and not
// an arrow, operator. This problem carries over to templates
// as well. It is common to see templates advertise which types
// they can, and cannot, handle.
void
LinkedList::traverse() const
{
     Node* temp = head;

     cout << ''(
'';
     while (temp != NULL) {
         temp->data->print();
         cout << ''
'';
         temp = temp->next;
     }
     cout << '')

'';
}


int
LinkedList::length()
{
     return(len);
}


void
main()
{
     LinkedList MealList, AirplaneList, DogList;
     Meal *meal1 = new Meal, *meal2 = new Meal, *meal3 = new Meal;
     Dog *dog1 = new Dog, *dog2 = new Dog, *dog3 = new Dog;
     Airplane *air1 = new Airplane, *air2 = new Airplane, *air3 =
new Airplane;

// At first glance everything seems to work nicely.
      MealList.insert(meal1);
      MealList.insert(meal2);
      MealList.insert(meal3);
      Meal* aMeal = (Meal*) MealList.remove();
      aMeal->eat();

      AirplaneList.insert(air1);
      AirplaneList.insert(air2);
      AirplaneList.insert(air3);

      DogList.insert(dog1);
      DogList.insert(dog2);
      DogList.insert(dog3);

      MealList.traverse();
      AirplaneList.traverse();
      DogList.traverse();

// Until we see some of the nasty side effects of weak type
// checking. This code accidentally flies a dog off the
// runway at the airport.

      AirplaneList.insert(dog2);
      DATUM anItem;
      while (AirplaneList.length() != 0){
          anItem = AirplaneList.remove();
          cout << ''My real type is '' << anltem->type() << ''
'';
          cout << ''I can fly. . .watch. . . 
'';
          ((Airplane*) anItem)->fly();
      }
      delete meal1;
      delete meal2;
      delete meal3;
      delete dogl;
      delete dog2;
      delete dog3;
      delete air1;
      delete air2;
      delete air3;
}

Selected C++ Example #18

// Example #18 

// This example code shows the attempt to implement three
// different lists (meal lists, airplane lists, and dog lists)
// with one implementation of the list code. The general approach
// is to use C++ templates to capture the common structure
// that all LinkedLists share. The template is then used to
// generate the three different lists. The advantage of templates
// over weakened type checking via inheritance (Example #17) is
// that mistakes of flying dogs off runways cannot occur (due to
// the maintenance of strong type checking by templates). The
// disadvantage is that we pay for the class/method code for each
// new data type that wishes to have a list instantiation.

#include <iostream.h>
#include <new.h>

// The derived classes Dog, Meal, and Airplane are only
// skeleton classes, to simplify their implementation.
class Dog {
public:
     void bark();
     void bite();
     void print(ostream& o = cout);
};

void
Dog::bark()
{
     cout << ''Bow Wow
'';
}

void
Dog::bite()
{
     cout << ''Ouch!!!
'';
}

void
Dog::print(ostream& o)
{
     o << ''I am a dog!
'';
}

classMeal {
public:
     void eat();
     void print(ostream& o = cout);
};

void
Meal::eat()
{
  cout << ''Crunch ... Munch ... Crunch ...
'';
}

void
Meal::print(ostream& o)
{
     o << ''I'm a meal
'';
}

class Airplane {
public:
     void fly();
     void print(ostream&o = cout);
};

void
Airplane::fly()
{
     cout << ''Va-a-a--room!!!
'';
}


void
Airplane::print(ostream& o)
{
     o << ''I'm an airplane!
'';
}

// Borland wouldn' t accept nested templates, so the Node class is
// placed outside the scope of the LinkedList.
template <class DATUM>
struct Node {
     DATUM data;
     Node* next;
public:
     Node(DATUM&);
};

template <class DATUM>
class LinkedList{
      Node<DATUM>* head;
      int len;
public:
     LinkedList();
     ~LinkedList();
     int insert(DATUM&);
     DATUM remove();
     void traverse() const;
     int length();
};


template <class DATUM>
Node<DATUM>::Node(DATUM& new_item)
{
     data = new_item;
     next = NULL;
}


template <class DATUM>
LinkedList<DATUM>::LinkedList()
{
     head = NULL;
     len = 0;
}

template <class DATUM>
LinkedList<DATUM>::~LinkedList()
{
     Node<DATUM>* temp;

     while (head != NULL) {
         temp = head;
         head = head->next;
         delete temp;
     }
}


template <class DATUM>
int
LinkedList<DATUM>::insert(DATUM& new_item)
{
     Node<DATUM>* temp = head;

     if (temp == NULL) {
          head = new Node<DATUM>(new_item);
    }
     else {
          while (temp->next != NULL) {
                     temp = temp->next;
          }
          temp->next = new Node<DATUM>(new_item);
     }
     return(++len);
}


template <class DATUM>
DATUM
LinkedList<DATUM>::remove()
{
     Node<DATUM>* temp = head;
     static DATUM bad_item;
     DATUM retval;

     if (temp == NULL) {
          return(bad_item);
     }
     else {
          retval = head->data;
          head = head->next;
          len--;
          delete temp;
          return(retval);
     }
}


// Notice that the traverse method sends a message to the
// data in each node to print itself. This sets up a requirement
// that any data type that wants to be in a LinkedList needs
// a print method. An even more important consideration is
// the fact that the syntax is different if DATUM is a pointer
// or nonpointer. Nonpointers would use a dot, and not
// an arrow, operator. The template requires its DATUM to be
// a pointer (in this example), and that the DATUM possess a
// print method.
template <class DATUM>
void
LinkedList<DATUM>::traverse() const
{
     Node<DATUM>* temp = head;

     cout << ''(
'';
     while (temp != NULL) {
         temp->data->print();
         cout << ''
'';
         temp = temp->next;
     }
     cout << '')

'';
}


template <class DATUM>
int
LinkedList<DATUM>::length()
{
     return(len);
}


void
main()
{
     LinkedList<Meal*> MealList;
     LinkedList<Airplane*> AirplaneList;
     LinkedList<Dog*> DogList;

// The following template expansion would generate errors due to
// the traverse method requiring a pointer data type (or at least
// a data type that supports the ''->'' operator).
//
//     LinkedList<Dog> x;
//
      
      Meal *meal1 = new Meal, *meal2 = new Meal, *meal3 = new Meal;
      Dog *dog1 = new Dog, *dog2 = new Dog, *dog3 = new Dog;
      Airplane *air1 = new Airplane, *air2 = new Airplane, *air3 =
                       new Airplane;
                       
// At first glance everything seems to work nicely 
      MealList.insert(meall);
      MealList.insert(meal2);
      MealList.insert(meal3);
      Meal* aMeal = (Meal*) MealList. remove();
      aMeal->eat();
      char c;
      cin >> c;

      AirplaneList.insert(air1);
      AirplaneList.insert(air2);
      AirplaneList.insert(air3);

      DogList.insert(dog1);
      DogList.insert(dog2);
      DogList.insert(dog3);

      MealList.traverse();
      AirplaneList.traverse();
      DogList.traverse();

      cin >> c;

// The following line of code would generate compiler errors due to
// the preservation of strong type checking by the C++ template
// mechanism.
//
//         AirplaneList.insert(dog2);

      delete meal1;
      delete meal2;
      delete meal3;
      delete dog1;
      delete dog2;
      delete dog3;
      delete air1;
      delete air2;
      delete air3;
}

Selected C++ Example #19

// Chapter 9 Example #1 
// This C++ example illustrates a minimal public interface
// implementation for a reference-counted String class. The
// minimal interface used is the one described in Chapter 9
// of this text. The code is actually three different files,
// string.hxx (the header file) , string.in1 (the inline
// function file included into the header file), and string.cpp
// (the C++ methods file). The three files are provided to
// illustrate the code organization as well as the details of
// the interface. Extra methods/operators are shown in the
// class definition but are not provided in the source code
// (for brevity).

// String.hxx
// This file contains the class definition for the class String.
#ifndef _STRING_
#define _STRING_
#include <iostream.h>
#include <string.h>

// The external definition of the global string that contains the
// name of this class 's name, i.e. , String.
extern char* String_type;

class String {
  struct StringRep {
           char* s;
           int ref_cnt;
           StringRep(const char*);
     };
     StringRep *str;
     void disconnect_self();
public:
     // Constructors and Destructors
     String(const char*);
     String(const String&);
     ~String();
     
     // Required functions for each class
     const char* type() const;
     String& shallow_copy(const String&);
     String& deep_copy(const String&);
     String* shallow_copy()const;
     String* deep_copy()const;
     int equal(const String&) const;
     int same(const String&) const;
     
     // Additional member functions
     String& upper_case();
     String& lower_case();
     String& upper_case(const String&);
     String& lower_case(const String&);
     String& reverse();
     intlength()const; 

     //Required operators
     String&operator=(const String&);
     int operator==(const String&) const;
     int operator!=(const String&) const;
     friend ostream& operator<<(ostream&, const String&);
     friend istream& operator>>(istream&, String&);
     
     // Additional operators
     String& operator=(const char*);
     String operator+(const String&)const;
     String& operator+=(const String&);
     int operator<(const String&) const;
     int operator>(const String&) const;
     int operator<=(const String&) const;
     int operator>=(const String&) const;
     String operator~() const;
     char& operator[](intindex) const;
     
     // Required self-test function.
     static void test();
};

#include ''string.inl''
#endif

// String.inl
// This file contains all the inline function definitions used by
// the class String.

// The function that returns the type of the class String is
// implemented as an inline function, which returns the global
// type string of this class. This global variable contains the
// constant String, is defined in String.cxx, and is used to
// facilitate fast testing of the type of an object. For example,
//      if (o.type() == String_type) {
// as opposed to
//      if (!strcmp(o.type(), ''String'')) {

inline const char*
String::type() const
{
     return(String_type);
}


// The constructor for String, which takes a character pointer as
// an argument, creates a new StringRep structure on the heap
// and allocates space within it for the String' s characters.
// These characters are copied into the allocated space, and the
// reference counter is assigned to one.
inline
String::String(const char* s)
{
     str = new StringRep(s);
}


// The destructor for the String class must disconnect the String
// object from its StringRep implementation. The disconnect_self
// function is a private function that decrements the reference
// counter and, if it goes to zero, cleans up the object.
inline
String::~String()
{
     disconnect_self();
}


// The shallow copy function for the String class, which takes
// zero arguments, is implemented as an inline function call to
// the copy constructor for Strings. The object that calls this
// constructor is allocated space on the heap. The source of the
// copy is the current object, which called the shallow copy
// function in the first place.
inline String*
String::shallow_copy() const
{
     return(new String(*this));
}


// The deep copy function for the String class, which takes zero
// arguments, is implemented as an inline function call to the
// constructor for Strings that takes a character pointer as an
// argument. This heap object encapsulates the character array
// in the current object (i.e., *this).
inline String*
String::deep_copy() const
{
     return(new String(str->s));
}


// The equal function for the String class is implemented as an
// inline function call to the standard C library function
// ''strcmp,'' which tests the equality of two strings
inline int
String::equal(const String& rhs) const
{
     return(!strcmp(str->s, rhs.str->s));
}


// The ''same'' function for the String class is implemented as an
// inline function that tests the StringRep pointers to ensure
// that they are the same. If they are the same, then the two
// String objects are the exact same object or shallow copies of
// each other.
inline int
String::same(const String& rhs) const
{
     return(str == rhs.str);
}


// The overloaded equivalence operator is implemented as an
// inline function call to the equal function defined on the
// class String. This function returns one if the two strings are
// equivalent (i.e., contain the same characters) and zero
// otherwise. For testing exact equality, see the function
// ''same()'' above.
inline int
String::operator==(const String& rhs) const
{
     return(equal(rhs));
}


// The overloaded nonequivalence operator is implemented as
// an inline function call to the equal function defined on the
// class String. The return value of the equal function is
// inverted before being returned. This operator returns one if
// the strings are not equal and zero otherwise.
inline int
String::operator!=(const Strings rhs) const
{
     return(!equal(rhs));
}
// String.cxx
// This file contains the member functions for the class String.
#include ''string.hxx''
char* String_type= ''String'';

// The constructor for StringRep is used as a support function in
// several of the member functions for Strings. It takes a
// character pointer as an argument, allocates space for it,
// copies the characters into this allocated space, and assigns
// its reference count to one.

String::StringRep::StringRep(const char* old_s)
{
     if (old_s != NULL) {
          s = new char[strlen(old_s)+1];
          strcpy(s, old_s);
     }
     else {
          s = NULL;
     }
     ref_cnt = 1;
 }


// The disconnect_self function is a private function that will
// separate a String object from its internal StringRep node.
// This process involves decrementing the reference counter in
// the StringRep node and, if it is zero (indicating that the
// String disconnecting itself is the last object pointing to the
// StringRep), the String object cleans up the StringRep object.
void
String::disconnect_self()
{
     if (--str->ref_cnt == 0) {
          delete str->s;
          delete str;
     }
}


// The copy constructor for the String class will increment the
// ref_cnt field of the String on the right-hand side of the 
//initialization, i.e., the argument rhs. The StringRep pointer
// of the new object is then initialized to point at the existing
// StringRep (to which rhs is also pointing).
String::String(const String& rhs)
{
     rhs.str->ref_cnt++;
     str = rhs.str;
}


// The shallow copy function for Strings will simply assign the
// pointer to the StringRep object to point at the StringRep of
// the object on the right-hand side. The String object on the
// left-hand side of the function call (i.e., this) must first
// disconnect itself from its StringRep.

String&
String::shallow_copy(const String& rhs)
{
     disconnect_self();
     str = rhs.str;
     str->ref_cnt++;
     return(*this);
}


// The deep copy function for the String class (which takes an
// additional argument) first disconnects the node from its
// current StringRep and then creates a new StringRep,
// assigning that StringRep to the value of the character array
// stored in the StringRep of the rhs String object. This
// function returns a reference to the String object on the left-
// hand side (i.e., *this) to facilitate nested function calls.
String&
String::deep_copy(const String& rhs)
{
     if (!same(rhs)){
          disconnect_self();
          str = new StringRep(rhs.str->s);
     }
     return(*this);
}


// The overloaded operator= function for the String class must
// first disconnect the String object on the left-hand side from
// its associated StringRep. It then assigns the StringRep pointer
// in that object to point at the StringRep object in the String
// object on the right-hand side of the assignment operator.
// This function returns a reference to the String object on the
// right-hand side in order to facilitate nested function calls.

String&
String::operator=(const String& rhs)
{
     if (!same(rhs)) {
          disconnect_self();
          str = rhs.str;
          str->ref_cnt++;
     }
     return(*this);
}


// The overloaded assignment operator for the String class, which
// takes a character pointer as an argument, first disconnects
// the string from its StringRep data member. It then creates a
// new StringRep structure and encapsulates the argument array
// of characters (i.e., rhs) into it.
String&
String::operator=(const char* rhs)
{
     disconnect_self();
     str = new StringRep(rhs);
     return(*this);
}


// The overloaded input and output operators for Strings simply
// read in the character array using standard C++ functions.
// The overloaded input operator will first disconnect the existing
// String from its associated StringRep object.
ostream&
operator<<(ostream& o, const String& rhs)
{
     o << rhs.str->s;
     return(o);
}


istream&
operator>>(istream&i, String& rhs)
{
     char buf[512];
     rhs.disconnect_self();
     i >> buf;
     rhs.str = new String::StringRep(buf);
     return(i); 
}

Selected C++ Example #20

// Chapter 9 Example #2 
// This C++ example illustrates the implementation of garbage
// collection in C++. Garbage collection in C++ typically
// revolves around a memory handler function that ''knows''
// which classes in the system are hoarding memory. The memory
// handler knows which classes are hoarding memory because the
// author of the application overloaded the new and delete
// functions for the class in question.
// In this example, we have a LinkedList class, which contains
// Node objects. The Node class has overloaded its standard
// allocator and deallocator (i.e., operators new and delete)
// to hoard recyclable nodes on a freelist (a class variable)
// The Node class has a free_garbage class method, which users
// of the class can call. In this example, our memory handler,
// called free_garbage, detects that memory has run out and
// tells the Node class to put the memory back.

#include <iostream.h>
#include <new.h>
#include <stdlib.h>
#include <string.h>

typedef int DATUM;

// The memory handler for the application.
void garbage_collect();


// Notice that the Node class is granting friendship to the
// LinkedList class. Granting friendship to a class or method
// is typically considered bad style since it weakens data
// hiding. In this example, it is allowed since the Node class
// is really an internal implementation detail of the LinkedList.
// To force the LinkedList class to use accessor methods for
// each access would overly convolute the code of the LinkedList
// with items such as ''head->set_next(head->get_next())''
// instead of the much more readable ''head = head->next''.
class Node {
     DATUM data;
     Node* next;
     static Node* freelist;
     friend class LinkedList;
public:
     Node(DATUM&);
     void* operator new(size_t);
     void operator delete(void*);
     static int free_garbage();
     static void print_freelist();
};

// The definition of the class variable ''freelist'' attached
//to the Node class.
Node* Node::freelist = NULL;


// The overloaded new operator (the standard allocator) will
// first try to recycle a node before going to the heap and
// getting one the expensive way.

void*
Node::operator new(size_t sz)
{
     Node* p;

// First try to get a node off the freelist.
     if (freelist != NULL) {
          p = freelist;
          freelist = freelist->next;
     }
     else {
// If that doesn't work, get one the old-fashioned, expensive
// way by making a trip to the heap.
           p = (Node*) new char[sz];
      }
      return(p);
}


// Be sure that ''delete 0'' works properly (a NOP),
// then add the node to the freelist for later recycling.
void
Node::operator delete(void* p)
{
     if (p != NULL) {
           ((Node*) p) ->next = freelist;
           freelist = (Node*) p;
     }
}

// The constructor for Node simply initializes the two data
// members, regardless of whether or not it is a recycled node.
Node::Node(DATUM& new_data)
{
     data = new_data;
     next = NULL;
}


// This class method is used for diagnostics. It prints the
// current addresses out on the Node's freelist.
void
Node::print_freelist()
{
     Node* temp = freelist;

     cout << ''Freelist: '';
     while (temp != NULL) {
          cout << temp << '' '';
          temp = temp->next;
     }
     cout << ''
'';
}

// This class function will throw away the freelist of
// nodes if a garbage-collection handler function
// requests it. Since nodes have an overloaded delete
// operator, the global delete must be called. Note the use
// of the scope resolution operator. This function returns
// the number of bytes it put back on the heap.
int
Node::free_garbage()
{
     Node* temp;
      int counter = 0;
      while (freelist != NULL) {
           temp = freelist;
           freelist = freelist->next;

// Must use global delete to prevent infinite
// recursion between deleting a node off of the
// freelist and the overloaded delete operator putting
// the node back on the freelist.
           ::delete temp;
           counter++;
      }
      return(counter*sizeof(Node));
}


// The LinkedList class is a user of Nodes. Its constructor
// and destructor call the overloaded new and delete of the
// Node class. Use of LinkedList objects results in a Freelist
// of Node objects stashed away in the Node class.
class LinkedList {
     Node* head;
     int length;
public:
     LinkedList();
     LinkedList(DATUM);
     ~LinkedList();
     int insert(DATUM);
     int remove(DATUM);
     void traverse() const;
};

LinkedList::LinkedList()
{
     head = NULL;
     length = 0;
}

LinkedList::LinkedList(DATUM first_item)
{
     head = new Node(first_item);
     length = 1;
}

LinkedList::~LinkedList()
{
     Node* temp;

     while (head != NULL) {
         temp = head;
         head = head->next;
         delete temp;
     }
}
int
LinkedList::insert(DATUM new_item)
{
     Node* temp = head;

     if (temp == NULL) {
          head = new Node(new_item);
     }
     else {
          while(temp->next != NULL) {
                    temp = temp->next;
          }
          temp->next = new Node(new_item);
     }
     return(++length);
}


int
LinkedList::remove(DATUM bad_item)
{
     Node* temp = head;

     if (temp == NULL) {
          return(0);
     }
     if (head->data == bad_item) {
          head = head->next;
          length--;
          delete temp;
     }
     else {
          while (temp->next != NULL&&
                      temp->next->data != bad_item){
                     temp = temp->next;
          }
          if (temp->next == NULL) {
                     return(0);
          }
          else {
                     Node* p = temp->next;
                     temp->next = temp->next->next;
                     length--;
                     delete p;
          }
    } return(1);
}


void
LinkedList::traverse()const
{
      Node* temp = head;

      cout << ''('';
      while (temp != NULL) {
           cout << temp->data << '''';
           temp = temp->next;
      }
      cout << '')
'';
}


// The main function first installs our memory handler.
// It then works with a number of LinkedList objects, which
// results in a freelist of nodes. We attempt to allocate
// an unallocatable amount of memory. The new operator for
// the array of characters is called. It calls the equivalent
// of malloc, which fails. New detects the failure and checks if
// there is a memory handler. There is one installed
// (garbage_collect), so we execute it and try malloc again,
// hoping for success. The hope is that the garbage collector
// will free enough memory so that the second attempt by new
// to allocate the memory is successful. In this example, the
// second attempt fails. The memory handler detects that no
// additional space has been freed and thus exits from the
// application, preventing infinite attempts to free memory even
// when there is none to free.
void
main()
{
     set_new_handler(garbage_collect);

     LinkedList a;
     LinkedList b(100);

     a.traverse();  b.traverse();
     a.insert(1);   a.insert(2);
     a.insert(3);   a.insert(4);
     a.insert(5);   a.insert(6);
     b.insert(200); b.insert(300);
     b.insert(400); b.insert(500);
     b.insert(600); b.insert(700);

     a.traverse();b.traverse();
     Node::print_freelist();

     a.remove(1); b.remove(100);
     a.traverse();b.traverse();
     a.remove(6); b.remove(700);
     a.traverse();b.traverse();
     a.remove(4); b.remove(400);
     a.traverse();b.traverse();

     Node::print_freelist();
     a.insert(99); a.insert(199);
     b.insert(-45);b.insert(-44);
     a.traverse(); b.traverse();

     Node::print_freelist();

     char* big_buf = NULL;

// This block is impossible to retrieve. New will fail and will
// call our handler, which tells the Node class to free
// its horded nodes. The call to new still fails, so we exit
// the application.

     big_buf = new char[65530L];
     strcpy(big_buf, ''Arthur Riel''),
     cout << ''big_buf = '' << big_buf <<''.
'';
}


// The garbage-collection routine is a cornerstone in this
// example. It possesses knowledge of what element of the
// application is hoarding memory. When the heap is out of
// space, this routine tells the hoarders to put their memory
// back on the heap. If this routine cannot find any memory to
// free, it bails out.
void
garbage_collect()
{
     int space_freed;

// Get some space freed on the heap by telling the hoarding
// Node class to put its memory back.
      space_freed = Node::free_garbage();

// If there's nothing to free, simply fail. Otherwise,
// return and let new try again.
      if (!space_freed){
           cerr << ''Fatal Memory Error!!!
'';
           exit(-1);
      }
}

Selected C++ Example #21

Overview Notes. The following C++ example is an implementation of the ATM/ Bank distributed process discussed in Chapter 11. It is a simulation in that there is no physical network between the two sides of the application. Each side was tested independently by simulating the network action through strings with the appropriate fields. The reader may replace four message sends in the Network class with their favorite byte-transfer mechanism system calls and have a fully distributed system through remote proxies. The actual mechanism used is irrelevant, as this code will demonstrate. Several points of interest in this code in which the reader may be particularly interested are itemized below. This code should not be examined until the reader has become familiarized with the design discussion of Chapter 11.

  • This code is not particularly concerned with bells and whistles, although many are included. Undoubtedly, more could have been added. Items such as checking for corrupted input data files are fairly minimal. Any bells and whistles that were part of the function specification have been included, however. Physical network timeouts were not addressed in this code. They would normally be implemented via alarm signals on each of the processes. The Cancel key would also be implemented in this way. Leaving out these asynchronous events left the code in a cleaner state and therefore made it easier to understand. Their implementation is not conceptually difficult given this code, but it is a bit tedious.

  • The reader should be sure to examine the relationship between the CardReader and PhysicalCardReader classes in atm.hpp and atm.cpp. This is a good example of a wrapper class for software reuse. The comments surrounding these two classes discuss this reuse at length.

  • The reader should also be sure to examine the role of the SuperKeypad class in allowing the containing ATM class to distribute its intelligence without adding uses relationships between its pieces. This type of abstraction is extremely useful to object-oriented designers and is discussed fully in the text portion of Chapter 11. It also is an example of a flaw in the data-driven methodologies. These methodologies can not detect these classes without considering behavior. However, it is useful to discover these classes early in analysis and design for purposes of controlling the complexity of the design.

  • The reader should carefully examine the role that the BankProxy (atm.hpp/atm.cpp), the ATMProxy (bank.hpp/bank.cpp), and the Network (network.hpp/network.cpp) classes play in hiding the details of distributed programming during design time. The use of remote proxies can clearly encapsulate the complex interprocess communication issues within distributed applications. This subject is discussed in Chapters 9 and 11.

  • Lastly, efficiency issues were not a high priority. For those real-time programmers who wish to question why I send a four-byte ASCII field to report a Good/Bad status over the network instead of a single-byte (or bit), keep in mind this code is meant to teach design. Besides, four bytes make for a more extensible system!

The ATM Side of the Application

// ATMMain.cpp: The main driving routine for the ATM side of the 
// application. This main function initializes the Card Slot
// directory (to simulate the card slot of the card reader), and ATM
// Slot directory (to simulate the ATM slot where the ATM puts the
// cards it eats. These are initialized from two command line
// arguments, which are passed in to the main program. It then
// builds a network object, which would require some parameter to
// initialize a particular byte-transmission mechanism in the
// real world. For this simulation, the user will type in strings to
// simulate information transmitted over a network of some kind.
// The main method then builds BankProxy around the network and
// uses it to create an ATM object. It then activates the ATM
// object, which sits in an infinite loop waiting for bank cards.

#include <iostream.h>

#include ''network.hpp''
#include ''atm.hpp''
#include ''trans.hpp''

int
main(int argc, char** argv)
{
     if (argc != 3) {
          cout << ''Usage: '' << argv[0] << '' CardSlotsATMSlots
'';
          return(1);
     }

  Network *network = new Network;
     BankProxy* MyBank = new BankProxy(network);
     ATM *a1 = new ATM(MyBank, ''ATM1'', 8500.00);

     strcpy(CardSlots, argv[1]);
     strcpy(ATMSlots, argv[2]);

     a1->activate();

     delete MyBank;
     delete a1;
     return(0);
}

The Atm.hpp File

// Atm.hpp: This is the header file for the main classes, which 
// reside solely on the ATM side of the application.

#ifndef _ATM_
#define _ATM_

// Files that deal with transactions will often define an ATM_SIDE
// or BANK_SIDE macro to describe which application space they are
// in. The transaction code gets compiled into each application,
// but some methods are only appropriate for one address space or
// the other.



#include <iostream.h>
#include <stdlib.h>
#include ''consts.hpp''

// Forward references
class Transaction;
class TransactionList;
class Network;

// These two pointers provide the path for the Card
// Reader's directory, which simulates where a card is
// inserted, and the ATM's directory, where eaten cards
// are placed. Normally these would be hardware addresses,
// but for our simulation they are going to be directories
// in a file system. A person copies a BankCard file
// into the CardSlots directory to simulate inserting
// a card into the ATM and the ATM removes the file to
// simulate ejecting the card. The ATM moves the file to its
// internal directory to simulate eating a card. The Bank
// Card file is a one-line ASCII file that consists of seven
// numeric characters representing the account number and
// a four-digit PIN number. The bank card itself is the
// same name as the physical card reader's ''name'' data member.
// These two directory strings are assigned in the main driver
// function to the first two command line arguments passed
// in.

extern char CardSlots[];
extern char ATMSlots[];

// The following two constant strings are used for
// portability in simulating ejecting and eating cards.

extern const char* file_delete_format;
extern const char* file_move_format;

// The relationship between the PhysicalCardReader
// and the CardReader is that the CardReader is a
// wrapper class for the PhysicalCardReader. The
// latter is very reusable in that it reads only raw
// data off of a card. The CardReader is specific to
// the domain of ATM and therefore is not reusable. It is
// responsible for distributing the system intelligence
// from the ATM to its pieces (in this case, the CardReader).

class PhysicalCardReader {
     char name[small_string];
public:
   PhysicalCardReader(char* n);
     int readinfo(char* buf);
     void eject_card();
     void eat_card();
};


// The following classes model the physical pieces of the ATM
// class. They include CardReader, Display, Keypad, DepositSlot,
// CashDispenser, and ReceiptPrinter. Of particular interest is
// the SuperKeypad. This class exists solely due to an analysis of
// the behavior of the system. This analysis indicated much
// cohesion between the Keypad and DisplayScreen. Cohesion is one
// metric for determining when two or more classes need
// encapsulation inside of a containing class. It is useful to note
// that a pure data driven analysis methodology would be unable to
// detect such classes since they are motivated by behavior and do
// not exist in the real-world model.
class CardReader {
     PhysicalCardReader cardReader;
   int validcard;
     char account[small_string];
     char pin[small_string];
public:
     CardReader(char* n);
     int read_card();
     int get_account(char*);
     int get_pin(char*);
     void eject_card();
     void eat_card();
};

class Keypad {
      int enabled;
public:
     Keypad();
     void enable();
     void disable();
     char getkey();
};

class DisplayScreen {
public:
     void display_msg(const char*);
};

class SuperKeypad{
     Keypad *keypad;
     DisplayScreen *display;
public:
     SuperKeypad();
     ~SuperKeypad();
     void display_msg(const char*);
     int verify_pin(const char* pin_to_verify);
     Transaction* get_transaction(char* account, char* pin);
};


class CashDispenser{
      int cash_on_hand;
public:
     CashDispenser(int initial_cash);
     int enough_cash(int amount);
     int dispense(int amount);
};

class DepositSlot{
public:
     int retrieve_envelope();
};

class ReceiptPrinter{
public:
     void print(TransactionList* translist);
};


// The BankProxy class is the representative of the Bank class in
// the ATM's address space. It is a wrapper class for the Network,
// which is itself a wrapper for the exact byte-transfer mechanism
// (pipes, in this example).
class BankProxy {
   Network* network;
public:
   BankProxy(Network* n);
   int process(Transaction*);
};


class ATM {
      BankProxy* bank;
      CardReader* cardReader;
      SuperKeypad* superKeypad;
      CashDispenser* CashDispenser;
      DepositSlot* depositSlot;
      ReceiptPrinter* receiptPrinter;
      TransactionList* translist;
public:
     ATM(BankProxy* b, char* name, int cash);
     ~ATM();
     void activate();
     int retrieve_envelope();
     int enough_cash(double amount);
     int dispense_cash(double amount);
};
#endif

The Atm.cpp File

// Atm.cpp: The source file of the main classes composing the ATM 
// side of the application. It consists of all method and global
// data definitions required by these classes.

#include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <math.h>

#include ''network.hpp''
#include ''atm.hpp''
#include ''trans.hpp''

// Definition of the two card slots in the ATM system, the
// card reader's slot, where a user inserts his or her card,
// and the ATM' s slot into which eaten cards are taken.
// These are simulated in this system by directories in the
// file system. The two path names are given to the ATM application
// as its first two command-line arguments.

char CardSlots[large_string];
char ATMSlots[large_string];

// The definition of the two format strings for simulating
// the ejecting and eating of bank cards. Can be changed to
// the equivalent Unix commands for portability.

const char* file_delete_format = ''del %s\%s'';
const char* file_move_format = ''copy %s\%s %s\%s.%d'';


// The checking of an account name determines that it consists of
// numeric digits. (Note: The actual account on the Bank side of the
// application sees seven digits plus a terminating S or C for
// savings and checking, respectively).
int
bad_account(char* account)
{
     while (*account != '') {
         if (*account< '0' | | *account > '9') {
                    return(1);
         }
         account++;
     }
     return(0);
}
// For now PIN numbers and account numbers use the same algorithm.
// They may drift in the future.
int
bad_pin(char* pin)
{
     return(bad_account(pin));
}

// Each PhysicalCardReader has a name, which it uses as the name of
// the BankCardfile when it is inserted into CardSlot directory.
// This naming would not be necessary in a real system, since the
// hardware would take care of this naming problem. It appears in
// this application only for simplifying the simulation.
PhysicalCardReader::PhysicalCardReader(char* n)
{
     strcpy(name, n);
}

// The readinfo method tries to open a file in the CardSlots
// directory with the name of the card reader. This name
// would not be needed in a real system. The encoded data
// would be read off the card reader's hardware. The method
// returns a one if the card cannot be read, zero if it
// was read successfully. The buf argument is filled in
// with the first line of the file on success. It is assumed
// to contain a seven-digit numeric string (account number)
// followed by a four-digit numeric string (pin number).

int
PhysicalCardReader::readinfo(char* buf)
{
     FILE* fd;

     sprintf(buf, ''%s/%s'', CardSlots, name);
     if ((fd = fopen(buf, ''r'')) == NULL) {
          return(1);
     }
     fgets(buf, large_string, fd);
     fclose(fd);
     return(0);
}


// The simulation for eject cards is to remove the file from the card
// slot directory. In a real ATM system, this method would be a call
// to a hardware driver.
void PhysicalCardReader::eject_card()
{
      char buf[large_string];

      sprintf(buf, file_delete_format, CardSlots, name);
      system(buf);
}
// The simulation for eating cards is to move the BankCard file from
// the CardSlot directory to the ATM slot directory. In a real ATM
// system, this method would be a call to a hardware driver.
void
PhysicalCardReader::eat_card()
{
     char buf[large_string];
     static int count=1;

     sprintf(buf, file_move_format, CardSlots, name, ATMSlots,
name, count++);
     system(buf);
}

// The constructor for CardReader calls the constructor of its
//PhysicalCardReader.
CardReader::CardReader(char* name) : cardReader(name)
{
     validcard = 0;
     account[0] = pin[0] = '';
}


// The read_card method checks to see if there is a card in
// the slot. If there isn't, it returns 1. If there is and
// it isn't readable, then the card is rejected and a 1 is
// returned. If the data on the card is readable, then the account
// and PIN number are read from the card (account is assumed to be
// a seven-character numeric string, the PIN a four-digit numeric
// string). If the data cannot be parsed (the card is not a valid
// bank card), then a 1 is returned and the card is ejected.

int
CardReader::read_card()
{
     char buf[large_string];

     validcard = 0;
     switch (cardReader.readinfo(buf)) {
          case -1: // If the card couldn't be read, then eject it.

                      cardReader.eject_card();
                      return(1);
          case 1: // If there is no card, then report it to ATM.
                  return(1);
          case 0: // We have the information, parse it.
                  // If the account number is bad, return 1 and
                     eject.
                     sscanf(buf, ''%7s'', account);
                     account[7] = '';
                     if (bad_account(account)) {
                                cardReader.eject_card();
                                return(1);
                 }

                    //If the PIN number is bad, return 1 and
                      eject.
                    sscanf(&buf[7], ''%4s'', pin);
                    pin[4] = '';
                    if (bad_pin(pin)) {
                                cardReader.eject_card();
                                return(1);
                    }
          }
          validcard= 1;
          return(0);
}


// The accessor methods are required for verifying a user-
// supplied PIN number and building transactions. These are
// valid since there is a design situation facing policy between
// two separate key abstractions, i.e., the SuperKeypad and
// the CardReader. Both return 0 on success, 1 on failure.

int
CardReader::get_account(char* acc)
{
     if (validcard) {
          strcpy(acc, account);
     }
     return(!validcard);
}


int
CardReader::get_pin(char* p)
{
     if (validcard) {
          strcpy(p, pin);
     }
     return(!validcard);
}



// The following two methods simply delegate to their wrapped
// PhysicalCardReader class and execute its methods.
void
CardReader::eject_card()
{
     cardReader.eject_card();
}


void
CardReader::eat_card()
{
     cardReader.eat_card();
}



Keypad::Keypad()
{
     enabled = 0;
}

void
Keypad::enable()
{
     fflush(stdin);
     enabled =1;
}


void
Keypad::disable()
{
     enabled = 0;
}

// The getkey method reads a single character from the Keypad
// (in the simulation, the hardware is assumed to be the standard
// input). We assume the newline character to be the Enter key,
// implying that all input has been received. The method returns
// the character read on success, NULL terminator if the keypad
// is not enabled.
char
Keypad::getkey()
{
     return(enabled?getchar() : ''),
}


void
DisplayScreen::display_msg(const char* msg)
{
     cout << ''@ATM Display@ '' << msg;
}


SuperKeypad::SuperKeypad()
{
     keypad = new Keypad;
     display = new DisplayScreen;
}

SuperKeypad::~SuperKeypad()
{
      delete keypad;
      delete display;
}

// This method delegates to its contained display screen. Such
// noncommunicating behavior is an argument for splitting the
// SuperKeypad class. However, the verify_pin()and
// get_transaction()methodsprovide more than enough cohesion
// of data to justify the SuperKeypad's existence.
void
SuperKeypad::display_msg(const char* msg)
{
     display->display_msg(msg);
}


// The verify_pin method enables the keypad, prompts the user
// for a PIN number, and checks it against the user-supplied
// PIN number. The method returns zero on success, nonzero
// on failure.
int
SuperKeypad::verify_pin(const char* pin_to_verify)
{
     char pin[small_string];
     int i = 0;

     keypad->enable();
     display->display_msg(''Enter Pin Number: ''),
     while ((pin[i++] = keypad->getkey()) != EnterKey)
           ;
     pin[i]= '';
     keypad->disable();
     return(strncmp(pin,pin_to_verify,4));
}


// Note the case analysis on the type of transaction. This case
// analysis is necessary since our object-oriented design has
// bumped up against an action-oriented (text-menu driven) user
// interface as per our discussion in Chapter 9. At least this case
// analysis is restricted to one point in the design (one method)
// and hidden in the SuperKeypad class. Any classes higher in the
// system are oblivious to the case analysis.
Transaction*
SuperKeypad::get_transaction(char* account, char* pin)
{
     int i = 0;
     char amount_str[small_string], trans_type;
     char target_account[small_string];
     double amount;

     keypad->enable();
     do{
         display->display_msg(''Select a Transaction
''),
         display->display_msg(''	W)ithdrawal
''),
         display->display_msg(''	D)eposit
''),
         display->display_msg(''	B)alance
''),
         display->display_msg(''	T)ransfer
''),
         display->display_msg(''	Q)uit
''),
         trans_type = keypad->getkey();
         while (keypad->getkey() != EnterKey)
                    ;
} while (trans_type != 'W' && trans_type != 'D' &&
         trans_type != 'B' && trans_type != 'T' && trans_type
         != 'Q'),

if (trans_type == 'Q') {
     return(NULL);
}
display->display_msg(''Enter Account Type (S/C): ''),
account[7] = keypad->getkey();
account[8] = '';
while (keypad->getkey() != EnterKey)
      ;

if (trans_type != 'B') {
     display->display_msg(''Enter Amount: ''),
     while  ((amount_str[i++]  =  keypad->getkey())  !=
     EnterKey)
               ;
     amount_str[i-1] = '';
     amount = atof(amount_str);
}
if (trans_type == 'T') {
     display->display_msg(''Enter Target Account Number: ''),
     i=0;
     while ((target_account[i++] = keypad->getkey()) !=
     EnterKey)
                ;
     target_account[i-1] = '';
     display->display_msg(''Enter Target Account Type (S/
C) : ''),
     target_account[7] = keypad->getkey();
     target_account[8] = '';
     while (keypad->getkey() != EnterKey)
                ;
}
switch (trans_type) {
     case 'W' :
                 return(new Withdraw(account, pin, amount));
     case 'D':
                 return(new Deposit(account, pin, amount));
     case 'B':
                 return(new Balance(account, pin));
     case 'T':
                 return(new Transfer(account, pin,
                 target_account, amount));
         default:
                   cerr << ''Unknown type in get_transaction
                   switch statement
'';
                   return(NULL);
     }
}


CashDispenser::CashDispenser(int initial_cash)
{
     cash_on_hand = initial_cash;
}


int
CashDispenser::enough_cash(int amount)
{
     return(amount <= cash_on_hand);
}

// We can give out only multiples of $10. The reader may want to
// elaborate on this class by giving it fixed numbers of $20 bills,
// $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps,
// theater tickets, etc., as well. Many warn the user that they are
// out of $10 bills and will dispense only multiples of $20. All of
// these items can be added to this class without impact on the rest
// of the system.
int
CashDispenser::dispense(int amount)
{
     amount -= amount % 10;
     if (enough_cash(amount)) {
          cout << ''@CashDispenser@ Giving the user '' << amount
<< '' cash
'';
          return(0);
     }
     return(1);
}


int
DepositSlot::retrieve_envelope()
{
     cout << ''@DepositSlot@ Getting an envelope from the
user
'';
     return(0);
}


// The receipt printer simulates the printing of receipts by
// creating a Receipts file in the current working directory.
// Again, the reader can elaborate on this class, adding a number
// of error checks, paper availability, etc. Like the cash
// dispenser, this is left as an exercise // to the reader since it
// adds no pedagogical benefit to this example.
void
ReceiptPrinter::print(TransactionList* translist)
{
   FILE* fd;

   cout << ''@ReceiptPrinter@ Your receipt is as follows: 
'';
   if ((fd = fopen(''receipt'', ''w'')) == NULL) {
      fd = stdout;
   }
      translist->print(fd);
   if (fd != stdout) {
       fclose(fd);
   }
}



// The BankProxy is an extremely important class. It is the
// representative of the Bank class within the ATM application. It
// is merely a wrapper for the Network class, which is a wrapper
// itself for which transport mechanism a distributed process is
// going to use for communication. In this example, I chose to
// simulate the network, but readers are free to use any network
// or byte-transfer mechanism they wish. The application is
// completely independent of this mechanism. (Note: Changes in the
// byte-transfer mechanism affect only the Network class's
// implementation.)
BankProxy::BankProxy(Network* n)
{
   network = n;
}


// When a BankProxy needs to process a transaction, it asks its
// Network object to send it. Assuming the send works correctly,
// the method then asks the Network for a response, which takes the
// form of a status integer (0,1, indicating success or failure on
// part of the real Bank class living in the bank's application
// space). If other Transaction application-specific data is
// required, then it is sent to the appropriate transaction's
// update message. This is to allow the Bank to update the state of
// a transaction in the ATM's application space from changes
// generated from the Bank's application space. Currently, only
// the Balance derived transaction uses this method to update its
// balance from the account in the Bank's application space.
int
BankProxy::process(Transaction* t)
{
   int status, count;
   char other_info[small_string];
   if (network-> send(t)) {
      return(1);
   }
   count = network->receive(status, other_info);
   if (count) {
     t->update(other_info, count);
   }
     return(status);
}


// Anew ATM object is given its Bank Proxy, a name to be handed down
// to its PhysicalCardReader (only needed for simulation), and its
// initial cash.
ATM::ATM(BankProxy* b, char* name, int cash)
{
     bank = new BankProxy(*b);
     cardReader = new CardReader(name);
     superKeypad = new SuperKeypad;
     cashDispenser = new CashDispenser(cash);
     depositSlot = new DepositSlot;
     receiptPrinter = new ReceiptPrinter;
     translist = new TransactionList(max_transaction_atm);
}


ATM::~ATM()
{
     delete bank;
     delete cardReader;
     delete superKeypad;
     delete cashDispenser;
     delete depositSlot;
     delete receiptPrinter;
     delete translist;
}

// The activate method for the ATM class is the main driver for the
// ATM objects. This method puts up the welcome message and waits
// for a card to become available (in simulation, a card becomes
// available when a user copies a file with the PhysicalCard-
// Reader's name into the CardSlots directory). When a card is
// available, the ATM retrieves the account and PIN number from the
// CardReader. It then asks its SuperKeypad to verify the PIN.
// The SuperKeypad verifies the PIN by getting a PIN number from the
// user and ensuring that it equals the one from the card. The
// actual check will be done by the Bank, which ensures that the PIN
// is equal to the one stored in the Account object.
// Once the PIN is verified, this method asks the SuperKeypad to
// collect and build a transaction. It preprocesses the
// transaction (handling things like getting envelopes for
// deposits, checking the cash dispenser to ensure enough cash is
// available for a withdrawal, etc.). If preprocessing was
// successful, it then asks its BankProxy to process the
// transaction. This method packages up the transaction, ships it
// over the network to the Bank's application, and collects a
// response from the Bank's application via the network. Notice
// the transparent nature of the interprocess communication. At
// design time we were able to completely ignore this distributed
// processing. Assuming the processing went well, we then execute
// a postprocess, which performs tasks like giving the user his or
// her money, etc. This method repeats the processing of the
// transaction until the user selects Quit, which requires the
// SuperKeypad::get_transaction method to return NULL. At this
// time the receipt printer generates a receipt and ejects the
// card.
void
ATM::activate()
{
     char account[small_string], pin[small_string];
     int count = 1, verified;
     Transaction* trans;

     while (1) {
          superKeypad->display_msg(''Welcome to the Bank of
Heuristics!!!
''),
          superKeypad->display_msg(''Please Insert Your Card In
the Card Reader
''),
// Get a card.
           while (cardReader->read_card()!=0) {
                      ;
           }
           cardReader->get_account(account);
           cardReader->get_pin(pin);
// Try three times to verify the PIN number.
          do {
                      verified = superKeypad->verify_pin(pin);
          } while (verified != 0 && count++ < 3);
// If it couldn't be verified, then eat the card.
           if (verified != 0) {
                       superKeypad->display_msg(''Sorry,  three
strikes and you're out!!! 
''),
                       cardReader->eat_card();
           }
           else {
// Otherwise, keep getting Transactions until the user asks to
// quit.
// while ((trans = superKeypad->get_transaction(account, pin))
// != NULL) {
// Preprocess the transaction, if necessary. The default is to do
// nothing.
// if (trans->preprocess(this) == 0) {
// If preprocessing was successful, then process the Transaction.
// If the Bank says the Transaction is valid, then add it to the
// current list (for the receipt) and carry out any postprocessing.
// if (bank->process(trans) == 0)
// { translist->add_trans(trans);
// trans->postprocess(this);
              }
// If problems occur, display an appropriate message and continue.
              else {
                 superKeypad->display_msg(''The Bank Refuses Your
Transaction!
''),
                 superKeypad->display_msg(''Contact  your  Bank
Representative.
''),
                 delete trans;
              }
                              }
                              else {
              superKeypad->display_msg(''This ATM is unable to
              comply with your''),
              superKeypad->display_msg('' request at this
              time.
''),
delete trans;
                                }
                     }
//When we're done, print the receipt, clean up the Transaction
// list, and eject the card. We're now ready to loop for another
// user.
                     receiptPrinter->print(translist);
                     translist->cleanup();
                     cardReader->eject_card();
              }
      }
}


// These are methods used by derived types of Transaction,
// specifically, in their pre-/post-process methods.
int
ATM::retrieve_envelope()
{
     return(depositSlot->retrieve_envelope());
}

int
ATM::enough_cash(double amount)
{
     return(cashDispenser->enough_cash((int) amount));
}


int
ATM::dispense_cash(double amount)
{
     return(cashDispenser->dispense((int) amount));
}

The Bank Side of the Application

// Bankmain.cpp: The main driver function for the Bank side of the 
// application. This main method builds a network, creates a bank,
// initializing its accounts to the data in a user-provided file,
// and builds an ATM proxy, which is then activated. The ATM proxy
// will listen on the network for a data packet. When it arrives,
// this packet is parsed to build an appropriate transaction. The
// Bank is then asked to process the transaction, and the results
// are shipped back to the sender. In this simulation, the network
// is really a user typing in packets with the appropriate format.
// The reader can replace these calls to getline with any
// appropriate byte-transfer mechanism.

#include <iostream.h>
#include ''bank.hpp''
#include ''trans.hpp''

main(int argc, char** argv)
{
   if (argc != 2) {
      cout << ''Usage: '' << argv[0] << '' AccountsFile
'';
      return(1);
   }

   Network* network = new Network;
   Bank* mybank = new Bank(100, argv[1]);
   ATMProxy* atm = new ATMProxy(mybank, network);

   atm->activate();

   delete mybank;
   delete atm;
   return(0);
}

The Bank.hpp File

// Bank.hpp: This is the header file for the main classes that 
// exist solely on the Bank side of the application.

#ifndef _BANK_
#define _BANK_

#define BANK_SIDE

#include ''consts.hpp''
#include ''trans.hpp''
#include ''network.hpp''

// Forward class declarations.
class Bank;

// The Account class contains the account number, the PIN number,
// and the balance. While Accounts seem to have a type on the ATM
// side of the application, this type becomes a suffix letter (S or
// C) on the account number on the Bank side of the application.
// The get_balance method is used by Balance::process, which must
// be able to get its current balance from the account for return to
// the ATM user. 
class Account {
      char account_name[small_string];
      char pin[small_string];
      double balance;
public:
  Account(char* acc_name, char* p, double amount = 0.0);
  double get_balance();
// The verify_account method checks that the account name and PIN 
// number passed in are equal to that of the account.
   int verify_account(char* acc_name, char* p);
// The check_balance method ensures that the account balance is at
//least ''amount'' in value. The modify_balance will add the
// amount argument to the balance of the account. Deposits modify
// with a positive amount, Withdrawals modify with a negative
// amount, and transfers do both.
   int check_balance(double amount);
   void modify balance(double amount);
// The equal method checks if the account name passed in is equal to
// that of the account. This is used by the find_account method of
// the AccountList class.
   int equal(char* acc_name);
   void print(FILE*);
   };


class AccountList{
      Account** accountList;
      int accountnum;
      unsigned size;
public:
     AccountList(unsigned sz);
     AccountList(unsigned sz, const char* AccountFile);
     ~AccountList();
     Account* find_account(char* acc_num);
     int add_account(Account* a);
     void print(FILE* fd);
};


// The ATMProxy is a very important class in the design of this
// application. It is the representative of the real ATM within the
// Bank application's address space. While its counterpart, the
// BankProxy in the ATM application, is mostly reactive, the
// BankProxy is proactive. It must know who its Bank object is so it
// can send it messages whenever the network delivers a request to
// process a transaction. The Network class is a wrapper for an
// appropriate byte-delivery mechanism (files, pipes, sockets,
// TCPIP, a squirrel with the information on a piece of paper so it
// can type it in with its nose, etc.). The activate method is the
// main driver of the Bank application. The Bank acts as a general
// server for this class.
class ATMProxy{
      Bank* bank;
   Network* network;
public:
     ATMProxy(Bank* b, Network* n);
     void activate();
};


// The Bank is constructed from a data file of accounts. In a
// real-world bank application, the bank would undoubtedly have
// many more responsibilities. The big one for this application is
// the processing of transactions.
class Bank {
     AccountList* accountList;
     TransactionList* transList;
public:
     Bank(unsigned sz, const char* accounts_file);
     ~Bank();
     int add_account(char* account_num, char* pin, double
balance = 0.0);
     int process(Transaction* t);
};

#endif

The Bank.cpp File

// Bank.cpp: The source file for the main classes of the Bank side of 
// the application. It contains all of the method and global data
// definitions these classes need.

#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include ''bank.hpp''


Account::Account(char* acc_name, char* p, double amount)
{
     strcpy(account_name, acc_name);
     strcpy(pin, p);
     balance = amount;
}
// Currently used solely by the Balance::get_balance method for
// retrieving the account balance for the users of the ATM that
// request it.
double
Account::get_balance()
{
   return(balance);
}


// This method verifies that the account name and PIN number of a
// transaction match the account. This method would be more
// sophisticated in the real world. There is no doubt that, at a
// minimum, an encryption algorithm would be employed on the PIN
// number. The friendly Bank of Heuristics can easily leave it out
// for demonstration purposes.
int
Account::verify_account(char* acc_name, char* p)
{
     return(!strncmp(account_name, acc_name, 7) &&
!strncmp(pin, p, 4));
}


int
Account::check_balance(double amount)
{
     return(amount <= balance);
}


//Note: Withdraw objects modify with negative amounts, while
// Deposit objects use positive amounts. Tranfers use one of each.
void
Account::modify_balance(double amount)
{
     balance += amount;
}

// This method is used by the find_account method of the AccountList
//class.
int
Account::equal(char* acc_name)
{
     return(!strncmp(account_name, acc_name, 7));
}


void
Account::print(FILE* fd)
{
     fprintf(fd, ''%s %s %4.21f
'', account_name, pin, balance);
}
AccountList::AccountList(unsigned sz)
{
     accountList = new Account*[size = sz];
     accountnum = 0;
}


// The AccountFile is assumed to consist of an arbitrary
// number of lines, each of which is a seven-numeric-character
// string, a space, a four-digit PIN number, a space, and
// an initial balance.

AccountList::AccountList(unsigned sz, const char* AccountFile)
{
     FILE* fd;
     char buffer[large_string];
     char* account_num,*pin,*amount;

     accountList = new Account*[size = sz];
     accountnum =0;

     if ((fd = fopen(AccountFile, ''r'')) == NULL) {
          cerr << ''AccountList Error: Cannot read '' ''<<
AccountFile;
          cerr << '''' to initialize accounts.
'';
     }
     while(fgets(buffer, large_string, fd) != NULL) {
         account_num = &buffer[0];
         pin = &buffer[8];
         amount = &buffer[13];
         buffer[12] = buffer[7] = '';
         if  (add_account(new  Account(account_num,  pin,
atof(amount)))) {
                   cerr << ''AccountList Error: Ran out of
space for the AccountList.
'';
                   break;
         }
     }
     fclose(fd);
}


AccountList::~AccountList()
{
     int i;

     for (i=0; i < accountnum; i++) {
          delete accountList[i];
     }
     delete accountList;
}
Account*
AccountList::find_account(char* acc_name)
{
   int i;
     for (i=0; i < accountnum; i++) {
          if (accountList[i]->equal(acc_name)) {
                     return(accountList[i]);
          }
     }
     return(NULL);
}


int
AccountList::add_account(Account* a)
{
     if (accountnum < size) {
          accountList[accountnum++] = a;
          return(0);
     }
     else {
          return(1);
     }
}

void
AccountList::print(FILE* fd)
{
     int i;

     for (i=0; i < accountnum; i++) {
          accountList[i]->print(fd);
     }
}


ATMProxy::ATMProxy(Bank* b, Network* n)
{
     bank = b;
   network = n;
}


// This method drives the entire Bank side of the application. The
// ATMProxy waits for a transaction to be taken off the network.
// When such an object comes through the network, the method gives
// it to the Bank to process and then ships the return status back to
// the caller. If a NULL transaction is received, it is determined
// to be a Quit, and the Bank application terminates.
void
ATMProxy::activate()
{
   Transaction* t;
   int status;

   while (1) {
      if ((t = network->receive()) == NULL) {
         break;
      }
      status = bank->process(t);
      network->send(status, t);
   }
}



Bank::Bank(unsigned sz, const char* accounts_file)
{
     accountList = new AccountList(sz, accounts_file);
     transList = new TransactionList(max_transaction_bank);
};


Bank::~Bank()
{
   cout << ''Bank is being destroyed!!!
'';
   accountList->print(stdout);
   transList->print(stdout);
     delete accountList;
     delete transList;
}


int
Bank::add_account(char* account_num, char* pin, double balance)
{
     return(accountList->add_account(new Account(account_num,
pin, balance)));
}


// When a bank processes a transaction, it first adds the
// transaction to its daily list of transactions. It then asks the
// transaction (polymorphically) to process itself. The bank
// hands its whole AccountList to the transaction since the bank
// does not know which, or how many, accounts the transaction
// requires. In this simulation, all of the accounts live in memory.
// In a real-world implementation, the accounts would live in a
// database. This change would impact only the implementation
// of the AccountList class and is irrelevant to any other code in
// our system. This is very beneficial since it implies that, at design
// time, we can pretend all objects live in memory. If in reality
// they live on disk in some database, then one of our classes, a
// wrapper, will isolate that fact from the rest of our application.
int
Bank::process(Transaction* t)
{
      transList->add_trans(t);
      return(t->process(accountList));
}

The Common Classes

// Consts. hpp: This header file for the ATM system captures a few 
// constant definitions.
#ifndef _CONSTS_
#define _CONSTS_

const int max_transaction_atm = 20;
const int max_transaction_bank = 1000;
const int max_accounts = 100;
const int small_string = 16;
const int large_string = 128;
const char EnterKey = '
';

#endif

The Trans.hpp File

// Trans. hpp: This header file contains the definition of the 
// classes that make up the Transaction portion of this
// application. The Transaction classes are interesting in that
// they span both sides of the application, i.e., they are used by
// both Bank classes and ATM classes. In fact, the transactions
// used by the Bank classes do not need to look anything like the
// transactions used by the ATM classes. In this implementation, I
// have chosen to make them one in the same. The different public
// interfaces are supported via #ifdef's using ATM_SIDE and
// BANK_SIDE macros to differentiate them.

#ifndef _TRANS_
#define _TRANS_

#include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include ''consts.hpp''

// Forward references
class Account;
class AccountList;
class ATM;

// A TimeStamp object encapsulates the data and time of a
// transaction. Its current implementation is that of a string
// produced via the time and ctime library routines. The reader
// should feel free to come up with something more elaborate.
class TimeStamp{
     char date_time[small_string*2];
public:
     TimeStamp();
     void print(FILE*);
};


// All transactions have a TimeStamp, one account name, its PIN
// number, and an amount. (Note: While Balance transactions do not
// need an amount, they use it to carry back the balance value.)
// Recall that protected accessor methods are perfectly
// appropriate. The packetize method is used to turn the object
// into a string suitable for shipping across the network. It
// requires every transaction (derived classes included) to know
// the format of this string, but the alternative is to have the
// Transaction class grant friendship to the network or at least
// provide a collection of accessor methods. The latter tends to be
// problematic in that all classes gain access to the abstract
// implementation of the Transaction class. This format knowledge
// does not imply that a network needs to ship the format as is. It
// implies only that they must use it to produce the final packets
// they will ship.
class Transaction{
     TimeStamp timeStamp;
     char source_account[small_string];
     char pin[small_string];
     double amount;
protected:
     Transaction(char* account, char* p, double a);
   char* get_source_account();
     double get_amount();
   void set_amount(double new_amount);
public:
  virtual void print(FILE*);
  virtual char* type() = 0;
//Only ATM classes use the preprocess, postprocess, and update
// methods. The Transaction class for the Bank side of the
// application never compiles them into the object code.
   #ifdef ATM_SIDE
   virtual int preprocess(ATM*);
   virtual void postprocess(ATM*);
   virtual void update(char* info, int count);
   virtual void packetize(char* buf);
#endif
// The process and verify_account methods are used only by the
// classes on the Bank's side of the application. The Transaction
// class for the ATM side of the application never compiles them
// into the object code.
#ifdef BANK_SIDE
     virtual int process(AccountList* accounts) = 0;
   int verify_account(Account*);
   virtual void packetize(int status, char* buf);
#endif
};

// The following four classes are the derived classes of the
// abstract Transaction class : Deposit, Withdraw, Balance, and
// Transfer.

class Deposit : public Transaction {
public:
     Deposit(char* account, char* p, double a);
      void print(FILE*);
            char* type();
#ifdef ATM_SIDE
     int preprocess(ATM*);
#endif
#ifdef BANK_SIDE
     int process(AccountList* accounts);
#endif
};


class Withdraw : public Transaction {
public:
     Withdraw(char* account, char* p, double a);
            char* type();
#ifdef ATM_SIDE
     int preprocess(ATM*);
     void postprocess(ATM*);
#endif
#ifdef BANK_SIDE
     int process(AccountList* accounts);
#endif
     void print(FILE*);
};


class Balance : public Transaction {
      double balance;
public:
      Balance(char* account, char* p);
       void print(FILE*);
             char* type();
#ifdef ATM_SIDE
     void update(char* info, int count);
#endif
#ifdef BANK_SIDE
      int process(AccountList* accounts);
#endif
};

// The Transfer class adds the additional data member of
// target_account since the money needs to be transferred
// someplace.
class Transfer : public Transaction {
      char target_account[small_string];
public:
     Transfer(char* s_account, char* p, char* t_account, double
a);
     void print(FILE*);
     char* type();
#ifdef ATM_SIDE
     void packetize(char* buf);
#endif

#ifdef BANK_SIDE
     int process(AccountList* accounts);
#endif
};


class TransactionList{
     Transaction** TransList;
     int transnum;
     unsigned size;
public:
     TransactionList(unsigned sz);
     ~TransactionList();
     int add_trans(Transaction*);
     void print(FILE*);
     void cleanup();
};


#endif

The Trans.cpp File

// Trans.cpp: The source code for the Transaction classes. The 
// Transaction classes are interesting since they appear on both
// sides of the application. The Transaction objects on each side
// of the application could be completely different definitions.
// For convenience, they were made the same in this example. The
// parts of the public interface unique to one side of the
// application are restricted through the use of the BANK_SIDE or
// ATM_SIDE macros.

#define BANK_SIDE

#ifdef ATM_SIDE
#include ''atm.hpp''
#endif

#ifdef BANK_SIDE
#include ''bank.hpp''
#endif

#include ''trans.hpp''

// The TimeStamp constructor uses the time and ctime standard C
// library functions to create a simple string capturing data and
// time.
TimeStamp::TimeStamp()
{
     time_t timer;

     time(&timer);
     strcpy(date_time, ctime(&timer));
}

void
TimeStamp::print(FILE* fd)
{
     fprintf(fd, ''%s'', date_time);
}


Transaction::Transaction(char* account, char* p, double a)
{
     strcpy(source_account, account);
     strcpy(pin, p);
     amount = a;
}

// These are protected accessor methods for use by the four derived
// classes of the Transaction class.
char*
Transaction::get_source_account()
{
     return(source_account);
}


double
Transaction::get_amount()
{
     return(amount);
}


void
Transaction::set_amount(double new_amount)
{
   amount = new_amount;
}


void
Transaction::print(FILE* fd)
{
     timeStamp.print(fd);
     fprintf(fd, ''	Account:%s	Amount:%4.21f
'',
      source_account,
       amount);
}


#ifdef ATM_SIDE

// If the application is the ATM side, then define the preprocess,
// postprocess, and update methods to do nothing. The packetize
// method is responsible for building a string, which consists of a
// type field (four characters), the source account (seven
// characters), a space, a PIN number (four characters), a space,
// an amount (nine characters), a space, and a NULL terminator.
// This string is suitable for shipping across a network, or it can
// be transformed into something that can be shipped across a
// network.

int
Transaction::preprocess(ATM*)
{
     return(0);
}

void
Transaction::postprocess(ATM*)
{
}


void
Transaction::update(char* info, int count)
{
}

void
Transaction::packetize(char* buf)
{
   char type_buf[small_string];
   strcpy(type_buf, type());
   type_buf[4]='';

   sprintf(buf, ''%s%s %s %4.21f'', type_buf, source_account,
pin, amount);
}
#endif

// If this is the Bank side of the application, include a
// verify_account method, which checks if an account's name and
// PIN match that of the Transaction.

#ifdef BANK_SIDE
int
Transaction::verify_account(Account* a)
{
   return(a->verify_account(source_account, pin));
}


void
Transaction::packetize(int status, char* buf)
{
   sprintf(buf, ''%4d %4.21f'', status, amount);
   cout << ''Bank packetized network object: '' '' << buf <<
''''
'';
}

#endif


Deposit::Deposit(char* account, char* p, double a) :
   Transaction(account, p, a)
{
}


void
Deposit::print(FILE* fd)
{
     fprintf(fd, ''Deposit: ''),
     Transaction::print(fd);
}


char*
Deposit::type()
{
   return(''Deposit''),
}


// Preprocessing deposits requires grabbing an envelope. Nothing
// can go wrong with this in the simulation, but in the world of
// hardware we would have to consider timeouts, jammed envelopes,
// etc. In the real world, the DepositSlot::retrieve_envelope
// method would be implemented to handle these cases.
#ifdef ATM_SIDE

int
Deposit::preprocess(ATM* a)
{
     return(a->retrieve_envelope());
}

#endif
// Processing a Deposit transaction involves getting the source
// account, verifying it, and then modifying the balance to reflect
// the deposited amount. This is a bit of a simplification from the
// real world, where a ''shadow'' account might be updated but a
// human needs to retrieve the envelope and check to be sure that
// money was actually included. This method suits our purposes,
// which is to show the design and implementation of a distributed
// process.
#ifdef BANK_SIDE

int
Deposit::process(AccountList* accounts)
{
   Account* account = accounts->find_account(get_source_account());

   if (account == NULL) {
      return(1);
   }
   if (!Transaction::verify_account(account)) {
      return(1);
   }
   account->modify_balance(get_amount());
   return(0);
}

#endif


Withdraw::Withdraw(char* account, char* p, double a) :
   Transaction(account, p, a)
{
}


void
Withdraw::print(FILE* fd)
{
     fprintf(fd, ''Withdraw: ''),
     Transaction::print(fd);
}

char*
Withdraw::type()
{
     return(''Withdraw''),
}


// Preprocessing a withdrawal involves checking the cash
// dispenser to be sure the ATM has enough cash. Postprocessing
// gives the user the cash.
#ifdef ATM_SIDE
int
Withdraw::preprocess(ATM* a)
{
     return(!a->enough_cash(get_amount()));
}

void
Withdraw::postprocess(ATM* a)
{
     a->dispense_cash(get_amount());
}

#endif


// The processing of a Withdraw transaction requires finding the
// source account, verifying the account, ensuring there is enough
// cash in the account, and, only then, reducing the amount of money
// in the account.
#ifdef BANK_SIDE

int
Withdraw::process(AccountList* accounts)
{
     Account* account = accounts->find_account(get_source_account());

     if (account == NULL || || !account->check_balance
(get_amount()))
{
     return(1);
   }
   if (! Transaction::verify_account(account)) {
       return(1);
   }
   account->modify_balance(-get_amount());
   return(0);
}

#endif


Balance::Balance(char* account, char* p) : Transaction(account,
p, 0.0)
{
}

void
Balance::print(FILE* fd)
{
     fprintf(fd, ''Balance: ''),
     Transaction::print(fd);
}

char*
Balance::type()
{
     return(''Balance''),
}


// Processing a Balance involves finding the source account and
// copying its balance into the Amount field of the Transaction
// object. This assumes the account name and PIN were verified.
#ifdef BANK_SIDE

int
Balance::process(AccountList* accounts)
{
   Account* account = accounts->find_account(get_source_account());

   if (account == NULL) {
      return(1);
   }
   if (!Transaction::verify_account(account)) {
      return(1);
   }
   set_amount(account->get_balance());
   return(0);
}

#endif

// The update method for Balance (on the ATM side of the
// application) is used to copy the retrieved balance back into the
// corresponding Transaction object on the ATM side.
#ifdef ATM_SIDE
void
Balance::update(char* info, int count)
{
   set_amount(atof(info));
}

#endif


Transfer::Transfer(char* s_account, char* p, char* t_account,
double a)
     : Transaction(s_account, p, a)
{
     strcpy(target_account, t_account);
}


void
Transfer::print(FILE* fd)
{
     fprintf(fd, ''Transfer: ''),
     Transaction::print(fd);
     fprintf(fd, ''	Target Account: %s

'', target_
      account);
}


char*
Transfer::type()
{
     return(''Transfer''),
}

// Packetizing a transfer implies including the target account at
// the end of the packet.
#ifdef ATM_SIDE

void
Transfer::packetize(char* buf)
{
     Transaction::packetize(buf);
     strcat(buf, '' ''),
     strcat(buf, target_account);
}

#endif

// Processing a Transfer object requires that we retrieve the
// source and target accounts, verify the source account, ensure
// that the source account has enough money for the transfer, and
// only then update the balances of the source (by subtracting the
// amount) and the target (by adding the equivalent amount).

#ifdef BANK_SIDE

int
Transfer::process(AccountList* accounts)
{
   Account* src_account = accounts->find_account
    (get_source_account());
   Account* tar_account = accounts->find_account(target_account);

   if (src_account == NULL || tar_account == NULL) {
      return(1);
   }
   if (!Transaction::verify_account(src_account)) {
      return(1);
   }
   if (!src_account->check_balance(get_amount())) {
      return(1);
   }
   src_account->modify_balance(-get_amount());
   tar_account->modify_balance(get_amount());
   return(0);
}
#endif


TransactionList::TransactionList(unsigned sz)
{
     transnum = 0;
     TransList = new Transaction*[size=sz];
}

TransactionList::~TransactionList()
{
     cleanup();
     delete TransList;
}

int
TransactionList::add_trans(Transaction* t)
{
     if (transnum < size) {
          TransList[transnum++] = t;
          return(0);
     }
     return(1);
}

void
TransactionList::cleanup()
{
     int i;
     for (i=0; i < transnum; i++) {
          delete TransList[i];
     }
}


void
TransactionList:sprint(FILE* fd)
{
     int i;
     for (i=0; i < transnum; i++) {
          TransList[i]->print{fd);
     }
}

The Network.hpp File

// Network.hpp: The header file for the network implementation 
// class. While not a first-class citizen in this design (i.e., a key
// abstraction), it is a very important implementation class. This
// class is a basic wrapper for the physical network, whatever it
// happens to be (a pipe, sockets, a LAN, a WAN, a telephone, two
// soup cans). The data of the Network class can be filled in with the
// reader's favorite mechanism. The methods of the class clearly
// state where a send buffer or receive buffer needs to be placed.
// This code has a simulation so that both sides of the application
// can be tested.

#ifndef _NETWORK_
#define _NETWORK_

#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include ''consts.hpp''

#define BANK_SIDE

class Transaction;

class Network {
// The user's favorite byte-transmission method goes here. See the
// implementation of the four methods to determine where the send
// and receives for this method need to go.
public:
#ifdef ATM_SIDE
   int send(Transaction* t);
   int receive(int& status, char* buf);
#endif
#ifdef BANK_SIDE
   Transaction* receive();
   void send(int status, Transaction* t);
#endif
};


#endif

The Network.cpp File

// Network.cpp: The implementation of the Network class for this 
// application. The Network class, like the Transaction class, is
// used on both sides of the application. It uses ATM_SIDE and
// BANK_SIDE macros to distinguish which side is being compiled.
// Each side of the network class has both a send and receive pair,
// which match the formats of the corresponding application side.

#include ''network.hpp''
#include ''trans.hpp''

// The send method, which takes a Transaction, is used by the ATM
// side of the application to send a transaction to the Bank side of
// the application. It asks the Transaction to packetize itself.
// The Transaction will build a string in its argument address
// which has the format of a four-character type field (first four
// letters of the class's name, a seven-digit source account field,
// a space, a four-digit PIN number, a space, a nine-digit
// floating-point field (stored in ASCII), and a NULL terminator).
// Some transactions may add additional info to the end of the
// record, e.g., the Transfer transaction adds the target account
// to the end of the string for later retrieval by the Network::
// receive() method below (which takes zero arguments).
// In this simulation, the method simply prints the packet it would
// send through the reader's favorite mechanism.

#ifdef ATM_SIDE
int
Network::send(Transaction* t)
{
   char buffer[large_string];

   t->packetize(buffer);
   cout << ''@Network Simulation@ Sending from the ATM to the
Bank: '' '' << buffer;
   cout << ''''
'';
// The reader would now send this string through there favorite
// byte sending mechanism.
    return(0);
}

// The receive method for the Network class on the ATM side of the
// application waits for a buffer to be sent from the Bank. This
// buffer is expected to have the return status (0 or 1) in the first
// four bytes, followed by any transaction-specific information in
// the remaining characters. It is assumed that a space separates
// the status from the additional info. If the fifth character is a
// NULL terminator, then there is no additional info. The method
// returns the number of bytes existing, aside from the status
// field.
int
Network::receive(int& status, char* buf)
{
   char buffer[large_string];

// The reader would place his or her favorite byte-exchange
// mechanism here and ask it to receive a byte string from the Bank
// side of the application. This byte string is assumed to be a
// four-character status field followed by an indeterminant number
// of transaction-specific information. In this simulation, the
// balance of the account is passed as additional information.

   cout << ''@Network Simulation@ Enter Status (4 characters), a
space, '';
   cout << ''and the account balance: '';
   cin.getline(buffer, large_string, '
'),
   if (buffer[4] == '') {
      status = atoi(buffer);
      buf[0]= '';
      return(0);
   }
   else if (buffer[4] == '') {
      buffer[4] = '';
      status = atoi(buffer);
      strcpy(buf, &buffer[5]);
      return(strlen(&buffer[5]));
   }
   else {
      cout << ''@Network Simulation@ Bad packet received at the
ATM!!!
'';
      Status = 1;
   }
   return(0);
}

#endif

#ifdef BANK_SIDE
// The receive method on the Bank side of the application receives
// a string of the byte-transfer mechanism (in this case, a string
// typed by the user). The string is then parsed by the Network
// class, and an appropriate Transaction object is built. The
// explicit case analysis on the type of the transaction is
// necessary due to the absence of an object-oriented network. The
// Network class provides the object-oriented interface so that
// the rest of our model sees nothing but objects. The case analysis
// is hidden within this method.

Transaction*
Network::receive()
{
   char buffer[large_string];

   // The reader may replace this call to getline with any
   // appropriate byte-transfer mechanism.
   cout << ''@Network Simulation@ Enter type (4 characters), '';
   cout << ''an account (7 digits), 
'';
   cout << ''@Network Simulation@ a space, a pin (4 characters), a
space, '';
   cout << ''and an amount: 
'';
   cin.getline(buffer, large_string);

// We parse the string by turning the whitespace into NULL
// terminators. We then build the appropriate object denoted by
// the first four characters of the buffer. This is the inverse
// routine for the send method on the ATM side of the application.

   buffer[11] = buffer[16] = buffer[26] = '';
   if (!strncmp(buffer, ''With'', 4)) {
      return(newWithdraw(&buffer[4], &buffer[12], atof
(&buffer[17])));
   }
   else if (!strncmp(buffer, ''Depo'', 4)) {
     return(new Deposit(&buffer[4], &buffer[12], atof
(&buffer[17])));
   }
   else if (!strncmp{buffer, ''Bala'', 4)) {
     return (new Balance(&buffer[4], &buffer[12]));
   }
   else if (!strncmp(buffer, ''Tran'', 4)) {
     return(newTransfer(&buffer[4], &buffer[12], &buffer
[27], atof(&buffer[17])));
   }
   else {
     cout << ''@Bank Application@ Unknown packet type!!!
'';
   }
   return(NULL);
}

// The send method of the Bank side of the application uses the
// transaction to packetize the data. The buffer created will be a
// four-digit status field, followed by a space, and the amount of
// the transaction. This amount is then given to the Balance
// transaction on the other side of the application (the ATM side).
void
Network::send(int status, Transaction* t)
{
   char buffer[large_string];

   t->packetize(status, buffer);
// The reader can replace this output with the appropriate
// byte-transfer mechanism.
    cout << ''@Network Simulation@ Packet Sent to ATM: '''' << 
buffer << ''''
";
}

#endif
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset