Chapter 8 Inheritance and Polymorphism; Chapter 19 Class Hierarchies. Base class Class with items and methods all explicitly defined. Derived class Class having access to methods and items of a base class plus items and methods of its own. Derived class inherits stuff from the base class. See simple syntax on p. 342. Any classes can be derived from a single base class, reducing the amount of extra coding. Inheritance should model isa relationships. This is NOT the same as saying isa relationships should always be modeled using inheritance. See hierarchy diagram on page 728. Design software that manages bank accounts: Checking, Savings, CD are three possible types. Each is different => 3 different classes. This is inefficient because they all share much. Define one base class that contains common items: Account #, name, address, SS#, interest rate; etc. Each of the checking, savings, and CD extends a base account class via the isa relationship. i.e. a checking account isa bank account. Design three separate classes that extend the base class, each of which has what is unique to the class. Some examples: Checking: Number with which next check order begins; List of checks cashed. CD: expiration date, penalty for early withdrawal. Savings: Various rates depending on balance in account. See demo10. Note how derived class objects have access to methods in BOTH the derived and base classes. NOTE also the use of an overloaded << operator for every class and which methods are called from the main. What happens if you specify a base class and a method of one of the derived classes? i.e. p.getCredits() in demo10. Suppose d is a derived object and f a method. If f is defined in d then d.f() calls it. If f is defined in the base class then d.f() calls the base class method. NOTE that f can be defined in BOTH. In this case, the derived class method is called. See bullets on p. 352. Demo10 illustrates some examples. NOTE the word public prior to the base class in the declaration of a derived class. Need it or member functions of a base class can NOT be specified with an object declared as a derived class type. This would constitute private inheritance and is rarely useful (common error on p. 348). Calling a base class constructor. See syntax on p. 349 and in demo10. Derived class can NOT access private members of its base class. In the student class constructor put name_ = “joe” and see the compile error. In the person class change private to protected and try again. Protected means access from with the class and all derived classes. Designers recommend against using protected access. Designer of the base class loses control over what objects can modify a protected variable and any constraints that may be needed. See common error p. 355. Note what happens if Person::testFunc(outstream); is changed to testFunc(outstream); in the student class testFunc method. Infinite recursion and stack overflow. Discuss this. Polymorphism. Consider the vector of objects from demo10. Need to have a default Person constructor with no parameters to do this. Also need #include <vector>. When the test harness is run, note which testFunc is called throughout the test harness (Each class has one). Note that different objects can be added to the vector. Compiler treats a Student object like a Person object. Makes sense since a Student isa Person, so the compiler accepts it. Similar for a Faculty object. Note which instances of testFunc are called in the vector loop. Why? Insert the code directory [1].setCredits(123). Since the record in position 1 is a student, this would seem logical. Compiler will not accept. Why? Possible solution? Try a vector of student objects (vector <Student> directory (10);). Won’t even compile if the test harness attempts to pushback a person or faculty object. A Person isnota Student necessarily. Another issue! Re-examine the vector of student objects through the debug window. The extra fields for Student and Faculty are not even stored in the vector since vectors are fixed sized things. This is called object slicing (p. 363). So how do we get a list of all different types? SOLUTION: Vector of pointers to objects (Polymorphic array). vector <Person*> directory (0); Demo11. Step through demo11 and examine the vector through the debug window. Everything is there. Examine the results generated by the loop to display each object’s data. Everything is NOT there. Only Person object information appears. Only Person Object methods are being called. Why? Code to generate method calls determined at compile time. Compile time binding or static binding. The code tells the compiler that the vector holds pointers to Person Objects. The compiler obliges by binding the method calls to those in the Person class Solution: Put virtual prior to all of the write declarations. Only in the header file. This makes them virtual. That is, code to generate method calls determined at run time. Run time binding or dynamic binding. Another form of Polymorphism – referring to a collection of different things. See syntax, p. 363. Virtual must be used in base class. Supplying a virtual qualifier to all methods that are named the same as a virtual method in the base class is not required but is considered good form. Step through demo11. Problem: Still cannot code directory[1]setCredits(123) for example. Compiler does not recognize the setCredits() method. Solution: Create a dummy virtual setCredits() method in the person class and make it virtual. It need do nothing. Note what happens if you write directory[3]setCredits(123) (since item 3 is NOT a student – defaults to the Person setCredits() method. A bit of a Kludge not really a good design but it worked anyway. Alternative: Set up a temporary student pointer to a record and use it to call setCredits. Example: Student* temp = (Student*) directory[1]; temp setCredits(45); However, this requires advance knowledge (at code writing time) that dept[1] is a student. How can you determine what type of record you have in a polymorphic list? A virtual isa() function can return an object type (for example - a string representing the type). Then use conditional statements. C++ provides other options. dynamic casting and the typeid operator. In Visual C++ .NET must make sure that “Enable run-time type info” is set to “Yes” under Solution properties Configuration properties C/C++ Language. You will get compiler errors otherwise. dynamic casting (p. 731): Assume: B is a base class, D is a derived class, pb points to a base class and pd points to a derived class. can write pd = dynamic_cast<D*> pb. If pb happens to point to a object of type D, this works. If not, pd is NULL. Can be used only to determine if a variable has a specific type. typeid operator (p. 733): Somewhat more general. Assume B is a base class, D is a derived class, pb points to a base class and pd points to a derived class. typeid(pb) and typeid(*pb) returns the type (See demo11) NOTE: dynamic casting allowed ONLY with a polymorphic class (a class with at least one virtual method. Remove all virtual qualifiers and the program will not build. [http://msdn.microsoft.com/enus/library/4k5yex0s(VS.71).aspx] [http://groups.google.com/group/microsoft. public.vc.language/browse_thread/thread/a f3eceb4da348452] Note the common errors on p. 734. NOTE second common error, author suggests using virtual functions in the base class and overriding them. This makes sense in his example since give_raise makes sense in both classes. There are other cases where it would NOT makes sense. i.e. putting in a virtual setCredits method in a Person class. Choose wisely. NOTE: on virtual destructors, p. 6178. Step through the end of demo11 with and without destructors declared as virtual. Show the difference. Pure Virtual Functions: Used when a behavior attached to a base class but implementations only possible in derived classes. e.g. area() in a shape class. See p. 731. Useful for graphics programs. Advance topic (virtual function tables) on p. 735 outlines how polymorphism is implemented. Note the concept of pointers to functions. This might be covered in 370. Multiple Inheritance: Quality Tip on page 742: Avoid it! It’s confusing and there are other approaches to take (nested classes and Java’s Interface. Languages such as Java and C# were specifically designed w/o multiple inheritance. Inclass activity question (Graphics application) Design (interface only) a square and rectangle classes. Include methods to Calculate area Set the dimensions and x or y coordinate of the center Declare these methods and the constructors. If a square is derived from a rectangle it, in fact, still contains BOTH length and width. The square also inherits the rectangle’s set methods for EACH of the length and width. Will allow the length and width to be set independent of each other. That’s a problem. OK – I suppose we can override the rectangle’s setWidth and setLength methods with a Square’s setWidth and setLength methods. Each of these will set BOTH length and width, making sure the length and width are always the same. This works, but now the square has two methods that do the same thing. Troubling – not a clean design. How do you explain that to a user of the class? It’s also misleading as exhibited in the following segment. Square s; s.SetWidth(1); s.SetHeight(2); //negates the previous // line Suppose you accept the previous solutions. Consider void f(Rectangle r) { r.SetWidth(32); // calls Rectangle::SetWidth } Square s; f(s); What happens? Even this can be fixed if we change the function signature to void f(Rectangle& r) and make sure the set methods are virtual Point is that these solutions are the result of trying to fix issues as opposed to solid design. Even still, there are problems. Consider void f(Rectangle& r) { r.SetWidth(5); r.SetHeight(4); assert(r.GetWidth() * r.GetHeight()) == 20); } This works if f is called with a rectangle but NOT if a square was passed. Should the programmer assume that changing the width of a rectangle leaves the length unchanged? Seems a reasonable assumption but varies with the object passed. Though a square isa rectangle, some consider it bad design to have a square class inherit from a rectangle class. Inheritance is designed to extend a class (inheritance by extension). Inheritance is not necessarily designed to restrict a class (inheritance by restriction) Liskov Substitution Principle It states that FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT. [http://www.objectmentor.com/resources/ar ticles/lsp.pdf] The previous function f is a violation of the Liskov substitution principle. Could fix this with some dynamic typing in the base class method. But that requires the base class to have knowledge of the derived class. May also require changes in the base class if new derived classes are created. This is all contrary to what inheritance should be – a clean and simple extension to a base class. What to do? A square isa rectangle. Geometrically, this is true Behaviorally, this is NOT true. Behavior of a Square object is NOT consistent with the behavior of a Rectangle object for which length and width are independent of each other. Interpret the isa relationship carefully and appropriately!!