Dynamic Binding Implementation Object-Oriented Programming Spring
Implementation of Virtual Functions Ellipse draw + hide + rotate + Circle rotate ++ centre + class Ellipse { //... public: virtual void draw() const; virtual void hide() const; virtual void rotate(int); } E1, E2, *P; class Circle: public Ellipse { //... public: virtual void rotate(int); virtual Point centre(); } C1, C2, C3; E1E1 E2E2 P C1C1 C2C2 C3C3
Circle :: rotate Ellipse :: rotate Circle :: centre Ellipse :: draw Ellipse :: hide draw hide rotate Ellipse VMT draw hide rotate centre Circle VMT C1C3C2E1E2 P The Virtual Methods Table C++ Jargon: vptr and vtbl
P->rotate() Circle :: rotate Ellipse :: rotate Circle :: centre Ellipse :: draw Ellipse :: hide draw hide rotate Ellipse VMT draw hide rotate centre Circle VMT C1C3C2E1E2 P
Location of VPTR #1/2 Borland Style: at the beginning of an object. – Intuitive and simple (usually) – Problematic, if the base class does not have any virtual functions: – When converting a pointer to the derived class into a pointer to the base, the compiler must add an offset to skip over the vptr. This offset must be subtracted in downcasting. – The compiler must also do a null check, because the offset should not be added in case of a null pointer.
Location of VPTR #2/2 Gnu Style: when first virtual function is encountered Not so simple or intuitive. Virtual function call is a bit more complicated. Compiler must have a deterministic algorithm for locating the vptr: – If the function called is not virtual in the static type of the pointer - use static binding – If the function is virtual - add to the pointer the offset corresponding to the size of the most derived “virtual-free” super-class of the static type of the pointer Casting is so much simpler. No need to add or subtract any offset in up or down casting.
Dynamic Binding and Dynamic Typing Dynamic Typing: no constraints on the values stored in a variable. – Usually implies reference semantics Run-time type information: dynamic type is associated with the value. – There is no notion of static type to be associated with a variable. No type safety: run-time error if an object doesn't recognize a message.
Dispatch Tables Used in dynamic type systems Support: – Runtime introduction of new types – Runtime changes to type hierarchy – “Method not found” error messages uSpace Efficiency: optimal! uTime Efficiency: lousy; mitigated by a cache of triples: lClass where search started lSelector searched lAddress of method found
Binding within Constructors How is an object of class B derived from class A initialized? In C++ and Java, the constructor of A is invoked before the constructor of B – Why? So the B constructor never sees uninitialized attributes What happens if A’s constructor invokes a virtual function?
Binding within Constructors – C++ The binding of function calls within constructors is static – B’s memory has not been initialized yet – The output of new B(); is: x=1 struct A { int x; virtual void f() {cout << “x=“ << x ;} A() : x(1) {f();} }; struct B: A { public: int y; virtual void f() {cout << “y=” << y;} B() : y(2){}; }; struct A { int x; virtual void f() {cout << “x=“ << x ;} A() : x(1) {f();} }; struct B: A { public: int y; virtual void f() {cout << “y=” << y;} B() : y(2){}; };
Problem with Static Binding within Constructors What happens in new B(); ? Some compilers do not allow calling a pure virtual function directly from constructors However, nesting such a call in a chain of function calls it will usually compile struct A { virtual void f() = 0; A() {f();} }; struct B: A { public: virtual void f() {cout << “B’s f”;} }; struct A { virtual void f() = 0; A() {f();} }; struct B: A { public: virtual void f() {cout << “B’s f”;} };
Binding within Constructors – Java The binding of function calls within constructors is dynamic – An initialization phase precedes the constructor invocation – The output of new B(); is: y=0 class A { private int x=1; public void f() {System.out.print(“x=“+x);} public A() {f();} } class B extends A { private int y=2; public void f() {System.out.print(“y=”+y);} public B() {} } class A { private int x=1; public void f() {System.out.print(“x=“+x);} public A() {f();} } class B extends A { private int y=2; public void f() {System.out.print(“y=”+y);} public B() {} }
Problem with Dynamic Binding within Constructors What happens in new B(); ? – s is initialized to null when A ’s constructor is invoked – B ’s toString() is invoked from A ’s constructor – The result: NullPointerException class A { public A() {System.out.print(toString());} } class B extends A { private String s = “Class B” public String toString() {return s.toLowerCase();} } class A { public A() {System.out.print(toString());} } class B extends A { private String s = “Class B” public String toString() {return s.toLowerCase();} }