Inheritance Issues


I. Multiple Inheritance

    A. What it is: Inheriting from multiple classes

    B. Why use it

        1. You want to provide more than one view of an object

	    Example: A software package for serializing objects to
              byte streams so they can be stored as binary objects
              requires a view that converts an object to a byte stream.
              In contrast, a sorting algorithm requires a view that
              allows it to compare two objects. 

              In C++ this would be written as:

              class Employee : public Serializable, public Comparable {
	         ...
	      };

	      Employee worker = new Employee();
	      Serializable *binaryObj = worker;
	      Comparable *comparisonObj = worker;

	2. You want to inherit implementation from more than one class

	    Example: We want our administrative computing system to keep
	      all students of the same year (freshmen, sophomores, juniors,
	      seniors, etc) on some list. We may then want to inherit
	      both from a base class for people at the university, such
	      as person, and from a base class for linked lists.

    C. Problems with multiple inheritance

        1. What to do with duplicated names

	    a. duplicated instance variables can be referenced by prefixing
	       the variable name with the class name

	       Example:

	       class fruit {
	       public:
	         int calories;
		 void debug_print() { printf("fruit cals = %d\n", calories); }
	       };

	       class veggie {
	       public:
	         int calories;
		 void debug_print() { printf("veggie cals = %d\n", calories); }
	       };

	       class food : public fruit, public veggie {
	       public:
	         food(int fcals, int vcals) {
		     fruit::calories = fcals;
		     veggie::calories = vcals;
	         }
		 void print_fields() {
		     printf("fruit calories = %d\n", fruit::calories);
		     printf("veggie calories = %d\n", veggie::calories);
		 }
		 void debug_print() {
		     fruit::debug_print();
		     veggie::debug_print();
		 }
	       };

	    b. duplicated method names can also be referenced by prefixing
	       the method name with the class name

	       i. What happens when an outside function tries to call
	          a duplicated method name? -- The derived class must
		  provide its own definition of the method

		  Example: see the above definition of debug_print
			in food

	      ii. The derived class can provide access to the duplicated
	          methods individually by providing new methods

		  Example:

		     void fruit_debug () { fruit::debug_print(); }
		     void veggie_debug () { veggie::debug_print(); }

        2. Problems with multiple inheritance from the same base class: Should
	   an instance of the derived class contain one or two copies of
	   the base class?

	   Example:  class student_prof : public student, public professor {
	              ...
		     };

           Desired Inheritance: probably want a student who is also a
	      professor to have only one instance of the person object
	      (one university id, name, etc) but two instances of the
	      list object, since you may want to link the professor to
	      a non-matriculated student list and to some faculty list

	   Inheritance Hierarchy:     list    person      list
                                        \      /  \        /
					 \    /    \      /
					  \  /      \    /
					student   professor
					   \          /
					    \        /
					     \      /
					   student_prof

           a. Two types of inheritance

	       i. replicated inheritance: inherit separate copies from each
	          chain of the inheritance hierarchy (e.g., the inheritance
		  of the list object)

	      ii. shared inheritance: inherit a single copy from both branches
	          of the tree (e.g., the inheritance of the person object)

	   b. Replicated inheritance is the default in C++

	   c. Shared inheritance can be obtained by specifying that a
	       base class is virtual

	       class student : public virtual person, public list { ... }
	       class professor : public virtual person, public list { ... }

               i. the place to declare the base class virtual is
                  the direct descendents of the superclass that forms the
                  apex of the diamond (in this case Person)
              ii. in this example person will be shared while the list
                  class will be replicated

	   d. Here is the complete example. Note that the constructor
		for StudentProfessor must directly call the constructor
		for the shared object, Person, as well as calling constructors
		for the immediate superclasses.

		class Person {
		   protected:
		      string name;
		      int age;
		   public:
		      Person(string n, int a) : name(n), age(a) {}
		};

		class Student : public virtual Person {
		   protected:
		      double gpa;
		   public:
		      Student(string n, int a, double g) 
			 : Person(n, a), gpa(g) {}
		};

		class Professor : public virtual Person {
		   protected:
		      string rank;
		   public:
		      Professor(string n, int a, string r) 
			 : Person(n, a), rank(r) {}
		};

		class StudentProfessor : public Student, public Professor {
		   public:
		      StudentProfessor(string n, int a, double g, string r) 
			 : Person(n, a), Professor(n, a, r), Student(n, a, g) {}
		      void printAttributes() {
			 cout << "name = " << name << endl;
			 cout << "age = " << age << endl;
			 cout << "gpa = " << gpa << endl;
			 cout << "rank = " << rank << endl;
		      }
		};

		int main() {
		   StudentProfessor s("Brad", 53, 4.0, "Professor");
		   s.printAttributes();
		}

        3. Multiple inheritance also introduces implementation problems
	   for the compiler which are not discussed in this class

II. Mix-In Inheritance (Composition Versus Inheritance): Inheritance from
    one "real" base class and an arbitrary number of interfaces

    A. Ways to do it

        1. C++: Make the interfaces be abstract base classes with every
	    method in the base class declared as pure virtual.

        2. Java: Use interfaces 

    B. But suppose I want to

        1) inherit behavior from multiple classes, or
	2) inherit an object's behavior but not its interface

        1. Use composition: Make your class contain an object of the type
	    whose behavior you want to inherit

	    a. Have your methods call the corresponding methods in the
	       composed object

	       Example: class Rectangle {
	                    public void setWidth(int w) { ... }
			    public void setHeight(int h) { ... }
			    public int getArea() { ... }
			    public int getWidth() { ... }
			    public int getHeight() { ... }
			    public void draw() { ... }
			    public boolean pointInObject(int x, int y) { ... }

			    int width;
			    int height;
			}

			class Square {
			  Rectangle mySquare;

			  public int getArea() { return mySquare.getArea(); }
			  public void draw() { mySquare.draw(); }
			  public boolean pointInObject(int x, int y) {
			      return mySquare.pointInObject(x, y); }
			  
			  pubic void setSize(int s) { 
			      mySquare.setWidth(s);
			      mySquare.setHeight(s);
			  }
			  int getSize() { return mySquare.getWidth(); }
			}

   	2. Composition works if you can get by with using the services that
	   an object provides, but sometimes you actually need access to its
	   internal implementation. 

	   a. Example: There is no way to store a List object in a student
	      class and achieve a threading of students. The only way to
	      do it is to inherit a List's implementation so that your program
	      can access the next fields.

	      Note: A better way to thread the students is to have a list
	         object completely outside the student class that holds the
		 students. That way a student can be on more than one list.
		 This example illustrates that often a small change to the
		 design can eliminate the need to inherit.

           b. In C++ one can use access modifiers before the base class to
	      control whether interface or implementation is inherited, or
	      both

	    a. class derived : public base { ... } -- inherits both the
	        interface and the implementation of the base class

	    b. class derived : protected base { ... } -- inherits the
	        implementation but not the interface of the base class

		i. all public methods in the base class become protected
		   methods in the derived class

	       ii. the using keyword can re-declare a method to be
	           public

	       Example: class Rectangle {
	                  public:
			    void setWidth(int w);
			    void setHeight(int h);
			    int getArea();
			    int getWidth();
			    int getHeight();
			    void draw();
			    bool pointInObject(int x, int y);
			  protected:
			    int width;
			    int height;
			};

			class Square : protected Rectangle {
			  public:
			    using Rectangle::getArea;
			    using Rectangle::draw;
			    using Rectangle::pointInObject;
			    
			    void setSize(int s) { width = s; height = s; }
			    int getSize() { return width; }
			};