+ All Categories
Home > Technology > Static and Dynamic polymorphism in C++

Static and Dynamic polymorphism in C++

Date post: 22-Apr-2015
Category:
Upload: reachanil
View: 70,407 times
Download: 1 times
Share this document with a friend
Description:
This slide examines the cost/benefits of both static and dynamic polymorphism in C++
29
Polymorphism in C++ 24th Aug 2007
Transcript
Page 1: Static and Dynamic polymorphism in C++

Polymorphism in C++

24th Aug 2007

Page 2: Static and Dynamic polymorphism in C++

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

Page 3: Static and Dynamic polymorphism in C++

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 other…and that’s where the confusion

Page 4: Static and Dynamic polymorphism in C++

Any of them works – example// Using virtual method based polymorphism// lock is a virtual method. SingleThreading and Multi-threading are 2 derived classes which define lock() differentlyvoid func(BaseThreadingPolicy * pThreadingPolicy){

pThreadingPolicy->lock();};

// Using template based polymorphismSingleThreading 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 classtemplate <class T> func(T* pThreadingPolicy){

pThreadingPolicy->lock();};

Page 5: Static and Dynamic polymorphism in C++

Any of them works - Contd

• 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

Page 6: Static and Dynamic polymorphism in C++

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

Page 7: Static and Dynamic polymorphism in C++

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

Page 8: Static and Dynamic polymorphism in C++

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

Page 9: Static and Dynamic polymorphism in C++

Design of reusable data structures – Inheritance or Templates?

IndexIndex Inheritance based Inheritance 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

Page 10: Static and Dynamic polymorphism in C++

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

Page 11: Static and Dynamic polymorphism in C++

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– Can’t do that with templates

Page 12: Static and Dynamic polymorphism in C++

Choosing your polymorphism

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

Page 13: Static and Dynamic polymorphism in C++

Vanilla or Strawberry? I think I’ll 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 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

Page 14: Static and Dynamic polymorphism in C++

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]

Page 15: Static and Dynamic polymorphism in C++

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]

Page 16: Static and Dynamic polymorphism in C++

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’

Page 17: Static and Dynamic polymorphism in C++

IllustrationClass Base { public:

Base();virtual void func1();virtual void func2();virtual ~Base():

};

Class Derived{ public:

Derived();virtual void func1();virtual ~Derived();

};

Page 18: Static and Dynamic polymorphism in C++

Illustration (Contd)

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

Page 19: Static and Dynamic polymorphism in C++

Illustration (Contd)

Base Object

Base Object

Base Object

Derived Object

Derived Object

Derived Object

Base class vtable

Derived class vtable

Page 20: Static and Dynamic polymorphism in C++

What happens at runtime?

• 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

Page 21: Static and Dynamic polymorphism in C++

So, what’s the cost?

• 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!)

Page 22: Static and Dynamic polymorphism in C++

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

Page 23: Static and Dynamic polymorphism in C++

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)

Page 24: Static and Dynamic polymorphism in C++

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

Page 25: Static and Dynamic polymorphism in C++

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

Page 26: Static and Dynamic polymorphism in C++

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

Page 27: Static and Dynamic polymorphism in C++

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

Page 28: Static and Dynamic polymorphism in C++

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

Page 29: Static and Dynamic polymorphism in C++

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


Recommended