Selected C Example 2

 <  Free Open Study  >  

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.\n'';      } } // 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.\n'';      } } void Course::print() {      cout << ''\n\nCourse:'' << name << ''\n'';      cout << ''Description:'' << description << ''\n'';      cout << ''Duration:'' << duration << ''\n'';      cout << ''List of Prerequisites: '';      prereq->print();      cout << ''\n\n''; } // 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 << ''\n\n'';      for (i=0; i < course_num; i++) {           courses[i]->short_print();           cout << '' '';      }      cout << ''\n\n''; } // 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.\n'';      } } // 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 << ''\n\nName: '' << name << ''\n'';      cout << ''SSN: '' << ssn << ''\n'';      cout << ''Age: '' << age << ''\n'';      cout << ''Prerequisites: '';      courses->print();      cout << ''\n\n''; } 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.\n'';      }      else {           cout << ''Admission refused: Student does not have the '';           cout << ''necessary prerequisites.\n'';      } } void CourseOffering::print() {      cout << ''\n\nThe course offering for '';      course->short_print();      cout << '' will be held in room '' << room << '' starting on '';      cout << date << ''\n'';      cout << ''Current attendees include: '';      attendees->print();      cout << ''\n\n''; } 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?\n'';           cout << '' 1) Build a new course\n'';           cout << '' 2) Build a new student\n'';           cout << '' 3) Build a new course offering\n'';           cout << '' 4) List courses\n'';           cout << '' 5) List students\n'';           cout << '' 6) List offerings\n'';           cout << '' 7) Add a prerequisite to a course\n'';           cout << '' 8) Add a course to a student\n'';           cout << '' 9) Add a student to a course offering\n'';           cout << ''10) Detailed info on a course\n'';           cout << ''11) Detailed info on a student\n'';           cout << ''12) Detailed info on an of fering\n'';           cout << '' q) Quit\n'';           cout << ''\nYour 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.\n'';                                          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 << ''\nList of courses: \n'';                                courses.print();                                cout << ''\n\n'';                                break;                      case 5:                                cout << ''\nList of students: \n'';                                students.print();                                cout << ''\n\n'';                                break;                      case 6:                                cout << ''\nList of Offerings: \n'';                                offerings.print();                                cout << ''\n\n'';                                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.\n'';                                          break;                                }                                cout << ''Which prerequisite? '';                                cin.getline(course_name, 50);                                course2 = courses.find_item                                 (course_name);                                if (course2 == NULL) {                                          cout << ''Sorry, Cannot                                 find that course.\n'';                                           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.\n'';                                          break;                                }                                cout << ''Which Course? '';                                cin.getline(course_name, 50);                                course1 = courses.find_item                                 (course_name);                                if (course1 == NULL) {                                          cout << ''Sorry, Cannot                                 find that course.\n'';                                           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.\n'';                                           break;                                }                                cout << ''Which Student? '';                                cin.getline(name, 40);                                student = students. find_item(name);                                if (student == NULL) {                                      cout << ''Sorry, Cannot                                find that student.\n'';                                           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.\n'';                                            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.\n'';                                            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.\n'';                                           break;                                }                                offer1->print();                                break;                                }                                } while (answer[0] >= '1' &&                                  answer[0] <= '9');                                } 
 <  Free Open Study  >  


Object-Oriented Design Heuristics
Object-Oriented Design Heuristics (paperback)
ISBN: 0321774965
EAN: 2147483647
Year: 1996
Pages: 180

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net