Session 11: Polymorphism Coding type-dependent logic Virtual functions Pure virtual functions and abstract classes Coding type-dependent logic Suppose we need to do something different depending on what type of object we're processing And suppose those types are all subtypes (derived classes) of a common parent (base) class Here are 4 techniques programmers use: Motor Vehicle 1 IF-THEN-ELSE logic 2 CASE logic Engine Body Electrical System Transmission 3 Table-driven logic COMP 170 Fall, 2013 Mr Weisert Chassis Doors Battery Alternator 4 Polymorphism Comparing the techniques 1 IF-THEN-ELSE logic 2 CASE logic 3 Table-driven logic 4 Polymorphism Let's examine how we'd use each of them in a system to administer a public library Assume that we have to write a separate check_in (or return) routine for each type of item the library circulates 1 IF-THEN-ELSE logic (COBOL code) IF ITEM-TYPE = BOOK THEN PERFORM CHECK-IN-BOOK; ELSE IF ITEM-TYPE = MAGAZINE THEN PERFORM CHECK-IN-MAGAZINE; ELSE IF ITEM-TYPE = AUDIO_RECORDING THEN PERFORM CHECK-IN-RECORDING; ELSE IF ITEM-TYPE = FILM THEN PERFORM CHECK-IN-FILM; ELSE CALL ERROR USING "ILLEGAL ITEM TYPE"; Horribly repetitive and error prone Does anyone actually do this? COMP 170 1-4 Fall, 2013
2 Case logic (C code) switch (itemtype) {case book: check_in_book (item); break; case magazine: check_in_magazine (item); break; case recording:check_in_recording(item); break; case film: check_in_film (item); break; default: cerr << "Illegal item type " << itemtype; } A slight improvement, but still requires custom code for each type, not only here but probably also in dozens of other places in the program 3 Table-driven logic (C code) // Initialization void (check_in [9]) (LibraryItem); check_in[book] = check_in_book; check_in[magazine] = check_in_magazine; check_in[recording`] = check_in_recording; array of pointers to functions check_in[film] = check_in_film; // Invoke the appropriate function check_in[itemtype] (item); Big improvement in executable code, but we still have to set up the array of function pointers Explicit type code The 3 techniques examined so far all depend on reifying the type The type code is an actual data item in the LibraryItem record (object) enum LibraryType {book, recording, magazine, film, video}; struct LibraryItem { LibraryType type_code; char* title; Date datepurchased; }; Hiding the type code (C++) With polymorphism, on the other hand, we don't need a type_code data item The run-time environment will keep track of it for us The detailed syntax is somewhat different in C++, Java, and C#, but the principle is exactly the same a The Microsoft Programming Guide article in today's reading list provides C# details b The handout demonstration shows a similar example in Java We shall examine it in detail COMP 170 5-8 Fall, 2013
C++ special operator for invoking polymorphic functions The arrow operator itemptr->check_in(); is just shorthand for (*itemptr)check_in(); Why do we need it? 4 Polymorphic invocation itemptr->check_in(); We still have to (a) Define Book, etc as derived classes from LibraryItem (b) write a separate check_in routine for each type (at least for any type where we want a non-default action), (c) declare check_in in LibraryItem to be a virtual function but now the code to invoke such routines doesn't have to be aware of the type hierarchy What's happening here? What benefits can we expect? Virtual functions Similar to overloading: If a derived class provides no custom version, the base class's function is called The differences are: 1 that the selection is made not by the compiler, but during execution when the the function is invoked 2 that a base-class's member function can invoke a derived-class's member function, even if the base class doesn't know about the derived class What do we need to change when we add another derived class to the hierarchy? Hiding the type code in Java Java supports the same capability, but does so by default Every function is virtual (potentially polymorphic) unless it's declared final Example: itemcheckin() will invoke the version of checkin() defined in whichever subclass of LibraryItem the object reference item belongs to We don't need a pointer or explicit reference to invoke it because all Java objects are stored as references COMP 170 9-12 Fall, 2013
Run-time method selection In c++ requires a pointer (or a reference): Book item1; LibraryItem* item_ptr; item_ptr = &item1; item_ptr -> check_in(); Invokes the Book version of check_in() What about this? (*item_ptr)check_in(); How would we do this in Java? Reference assignment A reference data item declared as an object of the base class may take on a value of a reference to an object of a derived class But the opposite is prohibited (unless you explicitly cast the reference): Why is this a reasonable rule? LibraryItem item1; Book item2; (code to initialize items) item1 = item2; // OK item2 = item1; // Illegal How does it know? Obviously, some behind-the-scenes control information is present in memory for both virtual member functions, and pointers to objects in the class hierarchy Specifically: for each virtual function C++ or Java may either: construct a table of pointers to the different class-dependent versions, or construct for a class a table of pointers to its virtual functions (the vtbl) an efficient integer type code accompanies the pointer Thus, there's some overhead cost, but not much Arrays of objects It's common to declare an array of references to the base class: LibraryItem catalog = new LibraryItem[size]; and then set elements to references to objects of vaqrious derived classes: catalog[k] = new Book( ); And finally process those elements in a high-level loop that doesn't know the individual types Let's look at the handout example COMP 170 13-16 Fall, 2013
Polymorphism and I-O The absence of the run-time control information in files or databases is the main obstacle to handling persistent objects in both C++, C#, and Java You can write objects to a file, and you can read them back, but you can't then invoke virtual methods on them with run-time selection Why not? For some, this is a serious obstacle to adopting those languages for a major application Can we exploit OOP and databases together? Partial interim solutions include: Object-oriented data-base products (eg Object Store, Poet, Objectivity) Language independent standards (eg CORBA) Re-reifying the type code, and putting class selection logic into your input functions Popular quasi-object-oriented products don't solve (or even address) this problem Object-oriented paradigm: We've now seen all of it Data objects as instances of classes Encapsulation functions (methods, processes, behavior, services) are bound to the class component data items (internal representation) are hidden from client programs Inheritance of properties by specific classes from more general classes Run-time selection among versions of a function depending on the class to which an object belongs (polymorphism) COMP 170 17-20 Fall, 2013