Selected C Example 15

 <  Free Open Study  >  

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 << ''.\n''; } // 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 << ''\n''; } // 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() << ''\n'';      cout << ''Social Security Number: '' << get_ssn() << ''\n'';      cout << ''Courses: \n'';      for (i=0; i < course_num; i++) {           cout << ''\t'';           course_list[i]->print(get_name());      }      if (course_num) {           cout << ''Grade Point Average: '' << GPA << ''\n'';      }      cout << ''\n\n''; } // 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() << ''\n'';      cout << ''Salary: '' << salary << ''\n'';      if (student_num) {           cout << ''Cost per Student: '' << salary/student_num << ''\n'';      }      cout << ''Students: \n'';      for (i=0; i < student_num; i++) {           students[i]->print();      }      cout << ''\n\n''; } // 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() << ''\n''; // 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 << ''\n\nPrinting Grad Student g1 as a student\n'';    ((Student*) &g1)->print();    cout << ''Printing Grad Student g1 as a Instructor\n'';    ((Instructor*) &g1)->print();    cout << ''Printing Grad Student g1 as a grad student\n'';    g1.print();    cout << ''\n\nPrinting Grad Student g2 as a student\n'';    ((Student*) &g2)->print();    cout << ''Printing Grad Student g2 as a Instructor\n'';    ((Instructor*) &g2)->print();    cout << ''Printing Grad Student g2 as a grad student\n'';    g2.print();    cout << ''\n\nPrinting Grad Student g3 as a student\n'';    ((Student*) &g3)->print();    cout << ''Printing Grad Student g3 as a Instructor\n'';    ((Instructor*) &g3)->print();    cout << ''Printing Grad Student g3 as a grad student\n'';    g3.print(); } 
 <  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