NOTE: This file has been broken into two files named generic-types.html and inheritance.html.
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 part may store both accounting and inventory
information. The accounting system will want the part
to provide a different set of services (i.e., methods)
than the inventory system.
class part : public accounting, public inventory {
...
};
part widget = new part();
accounting *accountingWidget = part;
inventory *inventoryWidget = part;
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 food::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 above
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(); }
c. duplicated names that are virtual methods--suppose we want
to override a virtual method from one of the base classes
(e.g., the part class wants to provide a different
implementation for the accounting class's cost method).
i. we need to interpose an "interface" class between the
base class and the derived class
class part_interface : public accounting {
virtual void accounting_part_cost () = 0;
void cost () { accounting_part_cost(); }
}
class part : public accounting, public inventory {
public:
void accounting_part_cost {
... code to implement accounting's virtual
cost method ...
}
};
accounting *partWidget = new part();
partWidget->cost(); // calls the cost method in
// part_interface, which calls the
// accounting_part_cost method in
// part that overrides the cost
// method
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 { ... }
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; }
};
III. Generic Types
A. Polymorphism: refers to the ability of the same code to perform the
same action on different types of data
1. Inheritance supports one type of polymorphism: different methods
with the same name can perform the same type of action
(e.g., draw an object, indicate whether a point lies in an object)
2. True polymorphism means that the same code can be re-used
with different types of data.
Example: The code for a linked list is typically the same,
regardless of what type of object it is storing
B. Traditional typed languages, such as C, C++, and Java, do not easily
support true polymorphism, because functions must take parameters
of a particular type and return values of a particular type.
However, all three languages provide ways to approximate it.
1. Java provides the implicit master class Object.
a. Generic code uses the type Object whenever it needs to refer
to a specific piece of data
b. Programs downcast the Object type to a specific type when they
retrieve an object from a generic data structure
c. Disadvantages
i. downcasting can be dangerous
ii. Object supports generic data structures but not generic
algorithms, such as quicksort, which require a common
comparator method
d. Advantages: One copy of the code works with multiple types
2. C++ provides templates
a. Disadvantages
i. the code is very complicated to write, to debug, and to
understand
ii. a copy of the code must be created for each different type
b. Advantages: Supports both generic data structures and generic
functions
3. C provides void *'s
a. The programmer can create generic data types by declaring values
to be of type "void *"
b. The programmer can pass in type-specific functions (e.g.,
comparator functions) that take void *'s as parameters and
that downcast the void *'s to the appropriate type before
manipulating the data
c. Advantages
i. One copy of the code works with multiple objects
ii. The approach supports both generic data structures and
generic algorithms--Note that this approach cannot be used
to support generic algorithms in Java since functions cannot
be passed as pointers
d. Disadvantages
i. Downcasting can be dangerous, even more so than in Java,
since run-time type checks are not performed in C
ii. The code often has a cluttered appearance (note how your
jval code looks)
3. Both Java and C++ provide a set of generic data structures.
a. C++ provides the standard template library (STL), which includes
both templates for common data structures, such as lists and
hash tables, as well as common algorithms, such as quicksort.
b. Java provides a set of standard data structures, such as lists
and hash tables, in its java.util package.