Last lass: Multiple Inheritance Multiple Inheritance Renaming Delegation Tricky cases in inheritance (stretchable circles) Implementing Polymorphism Not in the book... Problem How to efficiently find and call a method Meaning of polymorphic method not known at compile time Different implementations depending on language Smalltalk: untyped ++: efficient riteria Selection of function should be fast take little space be flexible (e.g., allow changes without recompilation) Smalltalk Smalltalk Jargon: to send a message to an object. Object responds to message. Smalltalk untyped ompiler has no idea which methods are provided by object Only single inheritance allowed How would you solve it? Smalltalk Message Passing Methods are identified by name (a string!) Object has associated class, which has hash table of strings with associated methods If method present in table, execute If string not found, if current class not root of inheritance hierarchy, then retry in (single) superclass If current object root, throw MessageNotUnderstood exception
Benefits and Drawbacks Benefits: No type information makes rapid prototyping very easy, menable to run-time modifications Do not need details of super classes at compile time Drawbacks Hash table has space overhead, Time overhead goes up as depth of inheritance increases ++: Virtual Function Table non- virtual functions virtual functions multiple base classes Based on B. Stroustrup. Multiple Inheritance in ++, The /++ Users Journal, 1999. void f(int); void f(int); struct { } void f(*, int){... * pa; * pa; f(pa,2); void f(int); void f(int); class B: { void f(int); void g(int); *pa; B *pb;... pb->f(2); class B: { void f(int); void g(int); *pa; B *pb;... pb->f(2); B f(pa,2) Bf(pb,2)
Virtual Functions Virtual Functions ::h struct { *; struct B{ *; class B: { virtual void g(int) class : B{ virtual void h(int) Note. The entries in the depend on what is overwritten. E.g., if overwrites g, then contains ::g. class B: { virtual void g(int) class : B{ virtual void h(int) struct { *; intc;} typedef as array of function pointers. Virtual Functions Virtual Functions Vtbls are constructed once, one for each class. (No overhead at object creation.) The for a and b has two entries (for f, g), the one for has three (f,g,h). * pc = new (); pc->g(2) * pc = new struct; pc-> = globalvtablefor; pc->[1](pc,2); The trick is that you can see a struct with many members as a struct with fewer, if you ignore the later members. ++ Virtual Functions: cast up ast up ::f ::h Important: the structure for a class contains the structure for its direct superclass at the beginning class B: { virtual void g(int) class : B{ virtual void f(int); virtual void h(int) * pa = new (); pc->g(2); return pc->a; (*(pc->[1]))(pc,2) return *(pc + 0)
all Overhead for ++ n lternative? (*(pc->[1]))(pc,2) ref = pc + 1 addr = *(ref) fnref = addr + 1 fnaddr = *(fnref) (*fnaddr)(pc,2) addition memory access addition memory access function call (not part of overhead) total: 2 memory accesses, 2 additions class B: { virtual void g(int) class : B{ virtual void f(int); virtual void h(int) : ::f ::h B Benefits: lternative only one addition, one memory access (save one of each) Drawback: Space: Memory for function pointers in every object not just every class Is not used. ++: Multiple Base lasses class B{ virtual void h(int) class :, B{ virtual void i(int); construct, construct B, construct B Multiple Base lasses, ssembly class B{ virtual void h(int) class :, B{ virtual void i(int); b ::i Multiple Base lasses, asts * pa; B *pb; *pc; pc->f(2); pc->g(2); pb = pc; pa = pc; pb = (pc *) 0; construct, construct B, construct
Multiple Base lasses, asts Multiple Base lasses: functions * pa; B *pb; *pc; *pc = new (); pa = pc; pb = pc; pb = (pc *) 0; pa = pc; pb = pc + 2; pb = 0 // special case! in general: if pointer 0 than zero, else add offset for space taken by. pc->f(2); pc->g(2); pc->h(2); pc->i(2); pc->a->f(pc,2) pc->b->g(pc+2, 2); pc->b->h(pc+2, 2); pc->a->i(pc,2) Note: g and h expect object of type B. We have to add the offset. Functions f and i do not need offset. (This does not quite work, hang on ) Multiple Base lasses: Problem! ++: Multiple Base lasses class B{ virtual void h(int) class :, B{ virtual void i(int); b ::i ::g pc->g(2) can not be translated to (pc->b->g)(pc+2,2)! We have to subtract the offset. b ::i ::g ++: Multiple Base lasses ++ Multiple Base lasses pc->g(2) becomes entry = pc->b->g; fptr = entry.fptr; offset = entry.offset (fptr)(pc+2+offset, 2) Hence, when g is called, we pass a pointer to a object, when h is called, we pass a pointer to a B object. b fptr 0 ::i 0 ::g -2 0 offset By default, we assume that pc has to be cast to a b-pointer first Reason: So that offsets still work when we cast pc to pb by hand. Offsets have to be taken into account at run time. Static solution impossible since we cannot statically determine what pc point to. It could point to an onject of derived class.
++ Multiple Base lasses cast is takes into account the offset, except if it is a zero pointer cast. That remains zero. Function calls use object with offset, extra addition at run time. ++ Multiple Base lasses Need part for each superclass including so that it can mimick superclass Need part for subclass No needed, use same technique as before Need offsets So that we can pass an object of correct type (type of superclass) to method Extra overhead: one memory reference (cached?) and one addition. Only in case of multiple inheritance. Optimization Optimization What is the problem with the ++ solution? (*(pc->[1]))(pc,2) auses pipeline stall as jump address is loaded. Better solution: implement vtable as a piece of code (a jump table) pc->g(2) becomes: if(pc-> == globalvtablefor){ ::g(pc,2) // often occurring special case } else { pc->[1](pc,2); //general case } Exploits branch prediction buffer in modern pipelined processors: result of if can be predicted with high accuracy, execution is started speculatively, penalty only incurred if prediction incorrect an speed up code significantly. Used in Smalltalk. Smalltalk fans claim this makes Smalltalk faster than ++. ::f ::h onclusions Overhead of virtual functions In Smalltalk potentially high, smart caching helps In ++ low. Somewhat complicated in conjunction with multiple inheritance. We now know how to translate ++ to