Você está na página 1de 29

Polymorphism in C++

24th Aug 2007

Overview
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

Kinds of Polymorphism
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 otherand thats where the confusion

Any of them works example


// Using virtual method based polymorphism // lock is a virtual method. SingleThreading and Multithreading 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 neednt have any virtual methods, nor do they need to derive from a common base class template <class T> func(T* pThreadingPolicy) { pThreadingPolicy->lock(); };

Any of them works - Contd


In the previous slide we have shown that templates arent 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 polymorphism
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 Polymorphism


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...)

Why all the excitement about templates?


Templates
Buy us generality in software, without the Abstraction penalty Are type safe Are non-intrusive Allow both reference and value semantics unlike virtuals

Design of reusable data structures Inheritance or Templates?


Index Inheritance based approach 1 Slower (virtuals) 2 Non type-safe (Crash!) Intrusive (put an int/char inside a class and derive from base) Reference semantics only (leaks, crashes..) Template based approach Faster Type safe

Non-Intrusive

Value or Reference semantics, both allowed

Sure choice - Templates


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

Why inheritance then?


Code and structure reuse from base classes OO Frameworks
Framework implementation requires Heterogeneous containers Reactor example Often depend on polymorphic return types Factory example Cant do that with templates

Choosing your polymorphism


Dont 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)

Vanilla or Strawberry? I think Ill have both


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 dont know the type of their derived classes Static attributes in base class are Shared Weakness of templates They dont lead to an Is-A relationship

Singleton using the van-berry flavor


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]

Object counter
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]

Cost of runtime polymorphism


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

Illustration
Class Base { public: Base(); virtual void func1(); virtual void func2(); virtual ~Base(): }; Class Derived { public: Derived(); virtual void func1(); virtual ~Derived(); };

Illustration (Contd)
Pointer to Base::func1 Pointer to Base::func2 Pointer to Base::~Base
vtable for the Base class

Pointer to Derived::func1 Pointer to Base::func2 Pointer to Derived::~Derived


vtable for the Derived class

Illustration (Contd)
Base Object Base Object Base class vtable

Base Object

Derived Object

Derived Object

Derived class vtable

Derived Object

What happens at runtime?


The compiler would have converted all your virtual method calls
Your call: pDerived->func2(); Its 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

So, whats the cost?


Direct cost vtable lookup (Put at about 510% 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!)

Is that all?
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. Theres 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

Cost of compile time polymorphism


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)

Template code bloat


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

Template code bloat (Contd)


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

Template code bloat (Contd)


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 nontemplate 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

Longer compilation time


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

Workaround
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

Summary
Prefer inheritance when
You want to reuse code/structure from the base class You want to develop OO frameworks which need to store heterogeneous elements in their data structures

Prefer templates when


You want generic data structures and algorithms Where Speed is important

Você também pode gostar