 Polymorphism means “Many forms”
 OO Purists – “Only virtual methods”
 Liberal C++ guys – Either virtual methods
(Behavior), or templates (Types), or even
function overloading for that matter
(Behavior)
 Generally accepted – virtual method
based, and template based
 Runtime/Dynamic polymorphism (Based
on virtual methods)
 Compile time/Static polymorphism
(Based on templates)
 These 2 are largely orthogonal
techniques, but many ways where one
could be replaced with the other…and
that’s where the confusion
// Using virtual method based polymorphism
// lock is a virtual method. SingleThreading and Multi-
threading are 2 derived classes which define lock()
differently
void func(BaseThreadingPolicy * pThreadingPolicy)
{
pThreadingPolicy->lock();
};
// Using template based polymorphism
SingleThreading and Multi-threading both implement a lock()
method, but the needn’t have any virtual methods, nor do
they need to derive from a common base class
template <class T> func(T* pThreadingPolicy)
{
pThreadingPolicy->lock();
};
 In the previous slide we have shown that
templates aren’t restricted to
polymorphism of type alone, but they
also can be used for polymorphism of
behavior
 In fact the template version in the
previous example yields much faster
code than the virtual method equivalent
 Much prevailing confusion about which
technique to use in what situation
 Runtime binding between abstract type
and concrete type
 Enforces a common interface for all
derived types via the base class
 Enforces an ‘Is-A’ relationship
 Used extensively in OO frameworks
 Templates eating into parts of its territory
 Compile time binding between abstract
type and concrete type
 Concrete types need not belong to the
same inheritance hierarchy. They just need
to meet the ‘Constraints’ imposed by the
generic type. So, more generic than the
virtuals
 Foundation of ‘Generic programming’ or
programming based on ‘Concepts’ (Eg:
STL)
 A very ‘Happening area’ in C++ right now
(See TR1, Boost, C++0x...)
 Templates –
› Buy us generality in software, without the
‘Abstraction penalty’
› Are type safe
› Are non-intrusive
› Allow both reference and value semantics
unlike virtuals
IndexIndex Inheritance basedInheritance based
approachapproach
Template based approachTemplate based approach
1 Slower (virtuals) Faster
2 Non type-safe
(Crash!)
Type safe
3 Intrusive (put an
int/char inside a class
and derive from base)
Non-Intrusive
4 Reference semantics
only (leaks, crashes..)
Value or Reference
semantics, both allowed
 So, templates lead to ‘Faster, Safer, and
Easier to reuse’ data structures than
inheritance
 Using inheritance for designing generic
data structures, is a common mistake in
C++
 Dates back to days where compiler
support was bad for templates
 Code and structure reuse from base
classes
 OO Frameworks
› Framework implementation requires
‘Heterogeneous containers’
› Reactor example
› Often depend on polymorphic return types
› Factory example
› Can’t do that with templates
 Don’t overuse inheritance (Implementing
data structures using inheritance is an
example of misuse of inheritance)
 Evaluate if your design could be done
using templates, they are faster and
safer than virtuals.
 Read up on templates and be aware of
its pitfalls (Another class of mine will deal
with that)
 Templates and Inheritance can be
combined sometimes
› Eg 1: Singleton pattern implementation
› Eg 2: Reusable object counter
› Eg 3: Reducing template code bloat
 Weakness of inheritance – Base classes
don’t know the type of their derived classes
 Static attributes in base class are ‘Shared’
 Weakness of templates – They don’t lead to
an ‘Is-A’ relationship
 Best of both worlds
› Have base class know the derived class type
by using templates [So, getInstance in the
base class is able to create a derived class
instance]
› Enforce ‘Is-A’ relationship using inheritance
[Eg: Make ctor private in the base class, so
nobody can instantiate a derived class
instance as well]
 Synergy again
› Base classes need to share the static
attributes
› Use templates to get over that issue – One
base class generated for each derived class
› Use inheritance for the ‘Is-A’ relationship
[Whenever the class ctor is called, the base
class ctor is called, likewise for the dtor,
increment and decrement operations
happen automatically]
 Each class that has a virtual method has an
associated ‘vtable’
 A ‘vtable’ is just an array of function
pointers containing pointers to virtual
method implementations for that class
 Each object of any such class, has a pointer
to the associated vtable
 The compiler creates the required vtables
and initializes each object to point to the
associated vtable all ‘Under the hood’
Class Base
{
public:
Base();
virtual void func1();
virtual void func2();
virtual ~Base():
};
Class Derived
{
public:
Derived();
virtual void func1();
virtual ~Derived();
};
Pointer to Base::func1
Pointer to Base::func2
Pointer to Base::~Base
vtable for the Derived class
Pointer to Derived::func1
Pointer to Base::func2
Pointer to Derived::~Derived
vtable for the Base class
 The compiler would have converted all
your virtual method calls
› Your call: pDerived->func2();
› It’s become: (*pDerived->vptr[1])(pDerived)
 As we see above, the vptr stored by the
compiler in the derived object is looked
up, an offset (+1) added to get to the
second virtual method in the class, and
the function is called
 Direct cost – vtable lookup (Put at about
5-10% overhead as opposed to non-
virtual methods)
 Indirect cost –
› Cannot inline virtual methods (Makes a big
difference)
› Each objects needs to store extra pointer
(An issue for fine grained objects – say
1000000 link element objects, each
containing 4 bytes extra!)
 Well, this gets worse when MI (Multiple
Inheritance is used)
 Now, the deal is 15-20% overhead for MI for
method defined in the 2nd
and onwards
base class. There’s no penalty for methods
in the first base class we derive from
 Ensure your most frequently called methods
are from the FIRST base class you derive
from if you use MI, order your base classes
accordingly
 ZERO runtime cost (Abstraction without
abstraction penalty)
 However …
› Coders not aware of how templates work
‘Under the hood’ end up creating code
bloat
› Developer turn around time increased due
to longer compiles (This could be avoided
too)
 Code bloat happens because compilers
cannot do ‘Commonality and variability
analysis’
 If the code body is the same for a set of
template argument values, the compiler
fails to see that and generates multiple
classes for each argument value
 Eg: list<int *>, list<char *, list<MyClass*> all of
these may have the same underlying
implementation of a ‘Container of
POINTERS’, but the compiler generates 3
different class definitions for these
 A coder who knows this, does the
following
› Give a partial template specialization for
POINTER types
› Have that derive from a void * based
container which has most of the
implementation in it
› This specialized class will have simple inline
methods that ‘Delegate’ to the base class
and gets work done
 So, we get the following result –
› The thin inline wrappers in the specialized
class offer type safety
› Majority of the code body is in the non-
template base class and hence no
duplication of it
› The thin wrappers are all inline hence no
performance overhead as well
 This is called the ‘Hoisting idiom’
 Happens mainly because the template
method definitions have to be in the
header file
 They may in turn pull in other headers
and hence lead to large include sizes
and hence more compile time
 More work to the compiler in case one
uses template meta-programming
 To reduce compile times, we can use
the ‘Explicit instantiation’ technique in
cases where large includes are
warranted
 Here basically you give the method
body in a .cpp file and put only the
template class definition in the .h file, but
explicitly instantiate the template in
the .cpp file where the methods are
defined for all types expected

Static and dynamic polymorphism

  • 2.
     Polymorphism means“Many forms”  OO Purists – “Only virtual methods”  Liberal C++ guys – Either virtual methods (Behavior), or templates (Types), or even function overloading for that matter (Behavior)  Generally accepted – virtual method based, and template based
  • 3.
     Runtime/Dynamic polymorphism(Based on virtual methods)  Compile time/Static polymorphism (Based on templates)  These 2 are largely orthogonal techniques, but many ways where one could be replaced with the other…and that’s where the confusion
  • 4.
    // Using virtualmethod based polymorphism // lock is a virtual method. SingleThreading and Multi- threading are 2 derived classes which define lock() differently void func(BaseThreadingPolicy * pThreadingPolicy) { pThreadingPolicy->lock(); }; // Using template based polymorphism SingleThreading and Multi-threading both implement a lock() method, but the needn’t have any virtual methods, nor do they need to derive from a common base class template <class T> func(T* pThreadingPolicy) { pThreadingPolicy->lock(); };
  • 5.
     In theprevious slide we have shown that templates aren’t restricted to polymorphism of type alone, but they also can be used for polymorphism of behavior  In fact the template version in the previous example yields much faster code than the virtual method equivalent  Much prevailing confusion about which technique to use in what situation
  • 6.
     Runtime bindingbetween abstract type and concrete type  Enforces a common interface for all derived types via the base class  Enforces an ‘Is-A’ relationship  Used extensively in OO frameworks  Templates eating into parts of its territory
  • 7.
     Compile timebinding between abstract type and concrete type  Concrete types need not belong to the same inheritance hierarchy. They just need to meet the ‘Constraints’ imposed by the generic type. So, more generic than the virtuals  Foundation of ‘Generic programming’ or programming based on ‘Concepts’ (Eg: STL)  A very ‘Happening area’ in C++ right now (See TR1, Boost, C++0x...)
  • 8.
     Templates – ›Buy us generality in software, without the ‘Abstraction penalty’ › Are type safe › Are non-intrusive › Allow both reference and value semantics unlike virtuals
  • 9.
    IndexIndex Inheritance basedInheritancebased approachapproach Template based approachTemplate based approach 1 Slower (virtuals) Faster 2 Non type-safe (Crash!) Type safe 3 Intrusive (put an int/char inside a class and derive from base) Non-Intrusive 4 Reference semantics only (leaks, crashes..) Value or Reference semantics, both allowed
  • 10.
     So, templateslead to ‘Faster, Safer, and Easier to reuse’ data structures than inheritance  Using inheritance for designing generic data structures, is a common mistake in C++  Dates back to days where compiler support was bad for templates
  • 11.
     Code andstructure reuse from base classes  OO Frameworks › Framework implementation requires ‘Heterogeneous containers’ › Reactor example › Often depend on polymorphic return types › Factory example › Can’t do that with templates
  • 12.
     Don’t overuseinheritance (Implementing data structures using inheritance is an example of misuse of inheritance)  Evaluate if your design could be done using templates, they are faster and safer than virtuals.  Read up on templates and be aware of its pitfalls (Another class of mine will deal with that)
  • 13.
     Templates andInheritance can be combined sometimes › Eg 1: Singleton pattern implementation › Eg 2: Reusable object counter › Eg 3: Reducing template code bloat  Weakness of inheritance – Base classes don’t know the type of their derived classes  Static attributes in base class are ‘Shared’  Weakness of templates – They don’t lead to an ‘Is-A’ relationship
  • 14.
     Best ofboth worlds › Have base class know the derived class type by using templates [So, getInstance in the base class is able to create a derived class instance] › Enforce ‘Is-A’ relationship using inheritance [Eg: Make ctor private in the base class, so nobody can instantiate a derived class instance as well]
  • 15.
     Synergy again ›Base classes need to share the static attributes › Use templates to get over that issue – One base class generated for each derived class › Use inheritance for the ‘Is-A’ relationship [Whenever the class ctor is called, the base class ctor is called, likewise for the dtor, increment and decrement operations happen automatically]
  • 16.
     Each classthat has a virtual method has an associated ‘vtable’  A ‘vtable’ is just an array of function pointers containing pointers to virtual method implementations for that class  Each object of any such class, has a pointer to the associated vtable  The compiler creates the required vtables and initializes each object to point to the associated vtable all ‘Under the hood’
  • 17.
    Class Base { public: Base(); virtual voidfunc1(); virtual void func2(); virtual ~Base(): }; Class Derived { public: Derived(); virtual void func1(); virtual ~Derived(); };
  • 18.
    Pointer to Base::func1 Pointerto Base::func2 Pointer to Base::~Base vtable for the Derived class Pointer to Derived::func1 Pointer to Base::func2 Pointer to Derived::~Derived vtable for the Base class
  • 20.
     The compilerwould have converted all your virtual method calls › Your call: pDerived->func2(); › It’s become: (*pDerived->vptr[1])(pDerived)  As we see above, the vptr stored by the compiler in the derived object is looked up, an offset (+1) added to get to the second virtual method in the class, and the function is called
  • 21.
     Direct cost– vtable lookup (Put at about 5-10% overhead as opposed to non- virtual methods)  Indirect cost – › Cannot inline virtual methods (Makes a big difference) › Each objects needs to store extra pointer (An issue for fine grained objects – say 1000000 link element objects, each containing 4 bytes extra!)
  • 22.
     Well, thisgets worse when MI (Multiple Inheritance is used)  Now, the deal is 15-20% overhead for MI for method defined in the 2nd and onwards base class. There’s no penalty for methods in the first base class we derive from  Ensure your most frequently called methods are from the FIRST base class you derive from if you use MI, order your base classes accordingly
  • 23.
     ZERO runtimecost (Abstraction without abstraction penalty)  However … › Coders not aware of how templates work ‘Under the hood’ end up creating code bloat › Developer turn around time increased due to longer compiles (This could be avoided too)
  • 24.
     Code bloathappens because compilers cannot do ‘Commonality and variability analysis’  If the code body is the same for a set of template argument values, the compiler fails to see that and generates multiple classes for each argument value  Eg: list<int *>, list<char *, list<MyClass*> all of these may have the same underlying implementation of a ‘Container of POINTERS’, but the compiler generates 3 different class definitions for these
  • 25.
     A coderwho knows this, does the following › Give a partial template specialization for POINTER types › Have that derive from a void * based container which has most of the implementation in it › This specialized class will have simple inline methods that ‘Delegate’ to the base class and gets work done
  • 26.
     So, weget the following result – › The thin inline wrappers in the specialized class offer type safety › Majority of the code body is in the non- template base class and hence no duplication of it › The thin wrappers are all inline hence no performance overhead as well  This is called the ‘Hoisting idiom’
  • 27.
     Happens mainlybecause the template method definitions have to be in the header file  They may in turn pull in other headers and hence lead to large include sizes and hence more compile time  More work to the compiler in case one uses template meta-programming
  • 28.
     To reducecompile times, we can use the ‘Explicit instantiation’ technique in cases where large includes are warranted  Here basically you give the method body in a .cpp file and put only the template class definition in the .h file, but explicitly instantiate the template in the .cpp file where the methods are defined for all types expected