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.