CSCI 431 Programming Languages Fall 2003 Dynamic Binding (Section 10.4) A compilation of material developed by Felix Hernandez-Campos and Mircea Nicolescu 1 Fundamental Concepts in OOP • Encapsulation – Data Abstraction – Information hiding – The notion of class and object • Inheritance – Code reusability – Is-a vs. has-a relationships • Polymorphism – Dynamic method binding 2 Inheritance • Encapsulation improves code reusability – Abstract Data Types – Modules – Classes • However, it is generally the case that the code a programmer wants to reuse is close but not exactly what the programmer needs • Inheritance provides a mechanism to extend or refine units of encapsulation – By adding or overriding methods – By adding attributes 3 Inheritance Notation Java.awt.Dialog Base Class (or Parent Class or Superclass) Is-a relationship Java.awt.FileDialog Derived Class (or Child Class or Subclass) 4 Dynamic Method Binding • Consequence of inheritance – derived class D has all members of its base class B – can use an object of class D everywhere an object of class B is expected » a form of polymorphism • Example (C++): class person { ... }; class student : public person { ... }; class professor : public person { ... }; student s; professor p; ... person * x = &s; person * y = &p; // Both student and professor objects have all properties of // a person object // Both can be used in a person context 5 Dynamic Method Binding • Example (C++): class person { ... }; class student : public person { ... }; class professor : public person { ... }; void person::print_mailing_label () { ... } student s; professor p; ... person * x = &s; person * y = &p; s.print_mailing_label (); p.print_mailing_label (); // person::print_mailing_label () // person::print_mailing_label () 6 Dynamic Method Binding • Example (C++): – Suppose that we redefine print_mailing_label in both derived classes void student::print_mailing_label () { ... } void professor::print_mailing_label () { ... } student s; professor p; ... person * x = &s; person * y = &p; s.print_mailing_label (); p.print_mailing_label (); // student ::print_mailing_label () // professor ::print_mailing_label () – But what about: x->print_mailing_label (); y->print_mailing_label (); // ?? // ?? 7 Dynamic Method Binding • Example (C++): – Two alternatives for choosing the method to call: student s; professor p; ... person * x = &s; person * y = &p; x->print_mailing_label (); y->print_mailing_label (); // ?? // ?? » according to the types of variables (references) x and y – static method binding (will call the method of person in both cases) » according to the types of objects s and p to which x and y refer – dynamic method binding (will call the methods of student / professor) – Dynamic method binding – central issue to object-oriented programming – Example – list of persons that have overdue library books » list may contain both students and professors » traverse the list and print a mailing label - call the appropriate subroutine 8 Dynamic Method Binding • Disadvantage of dynamic method binding – run-time overhead • Smalltalk, Modula-3 – dynamic method binding • Java, Eiffel – dynamic method binding by default – individual methods can be labeled final (Java) or frozen (Eiffel) » cannot be overriden by derived classes » use static method binding • Simula, C++, Ada 95 – static method binding by default – how do we specify dynamic binding in C++? » label individual methods as virtual 9 Virtual and Non-Virtual Methods • Terminology in C++: – redefine a method that uses static binding – override a method that uses dynamic binding • C++ class person { public: virtual void print_mailing_label (); ... } – if print_mailing_label is overridden in classes student and professor » at run time, the appropriate one is chosen – dynamic binding 10 Virtual and Non-Virtual Methods • Ada – methods take as parameter the object they are applied to – dynamic binding is associated not with the method but with the parameter – here, r needs to be declared as person'Class → dynamic binding 11 Virtual and Non-Virtual Methods • Static method binding is more efficient • When could it be undesirable? – May not preserve consistency in the derived class – Example: class text_file { char * name; long crt_pos; public: void seek (long pos); ... }; // non-virtual class read_ahead_text_file : public text_file { char * upcoming_chars; public: void seek (long pos); // redefinition // also updates the upcoming_chars ... }; – with static binding: » text_file::seek may be called for a read_ahead_text_file object, if used where text_file was expected » state of the read_ahead_text_file object would become inconsistent 12 Abstract Classes • C++ class person { ... public: virtual void print_mailing_label () = 0; ... }; – Abstract method – virtual method with no body » also called pure virtual method in C++ – Abstract class – it has at least one abstract method » cannot declare objects of an abstract class, just pointers – Purpose of an abstract class: » serve as base to derive concrete classes » a concrete class must provide a definition for every abstract method it inherits – Interface (Java) – class with no other members than abstract methods 13 Member Lookup • Static method binding: – easy to find the method to call, based on the type of the variable – performed at compile time • Dynamic method binding – appropriate method is identified at run-time – objects must contain information to allow for finding the appropriate method – each object contains a pointer to a virtual method table (vtable) – all objects of a given class have the same vtable 14 Member Lookup • Implementation of a vtable: – The compiler will generate: – Consider the code: foo * f; f->m(); – Runtime overhead (compared to static binding) – two instructions 15 Member Lookup • When defining a derived class: – Ordering is essential: » all new members introduced by bar must appear at the end: » additional data members (w) at the end of the record » additional virtual methods (s and t) at the end of the vtable » virtual methods overridden in bar (such as m) – appear in bar's vtable at the same place as in foo's vtable 16 Member Lookup • Allowed operations (same example): class foo { ... }; class bar : public foo { ... }; ... foo F; bar B; foo * q; bar * s; q = &B; // ok; references through q will use prefixes of B's data space and vtable s = &F; // static semantic error; F lacks the additional data and vtable entries of a bar 17 Type Checking • C++ foo F; bar B; foo * q; bar * s; ... q = &B; s = &F; class foo { ... }; class bar : public foo { ... }; – Type correctness of the code above – checked statically s = q; // still an error – But it can be done: s = (bar*) q; // ok, but risky – programmer must ensure that q refers to a bar object – Better solution: s = dynamic_cast<bar*> (q); // C++ specific – involves dynamic type check 18 Type Checking • Java – allows only the "classic" cast, but with dynamic check • Eiffel class foo ... class bar inherit foo ... ... f : foo; b : bar; ... f := b; -- always ok b ?= f; -- reverse assignment, with dynamic check – Reverse assignment ?= assigns an object reference into a variable only if the type at run-time is acceptable » b gets f if f refers to a bar object at run-time » otherwise b gets nil 19 Type Checking – Implementation • Eiffel, Java, C++ – what is needed to perform dynamic checking? » run-time type descriptor included in the object representation • Smalltalk – – – – variables are untyped references data members are never public any assignment is legal only method invocation ("send a message") is dynamically checked » implementation – dictionary that maps method names to their code » run-time – lookup in dictionary to check if method is supported » if not – dynamic semantic error – comparison to C++ approach: » more flexible in Smalltalk » less efficient – increased run-time cost » delayed reporting of errors 20 Member Lookup • Disadvantages of virtual methods: – run-time cost – preclude in-line expansion of methods at compile time » performance decrease for small and frequently called methods • Potential problem with static lookup: – fragile base class problem (Java): » large standard library, evolving over time (adding new features) » user may have old library and run code designed for new library » code may use the new features » static lookup – invalid memory access » dynamic lookup – better, produce error message ("member not found") 21 Classes as "Closures" • Classes – can implement a mechanism similar to first-class subroutines 22 Classes as "Closures" • Advantage of latter version (with classes): – can also add data members to class bar (and object my_obj) – function f can use them – similarity between: » data members of an object with a virtual method, AND » referencing environment of a closure in a language with nested scopes • Application: – discrete event simulation – need to have a subroutine schedule_at that: » takes as parameters a time value and a function f (to be called at that time) » function f should have an arbitrary set of parameters – how do we pass such a function f (with ANY number of parameters)? » pass a "closure" instead – implemented with classes 23 Classes as "Closures" • Example - discrete event simulation: 24