You are on page 1of 15

Institute of Telematics | University Lbeck

C++ Program 7. Polymorphism

Dr.-Ing. Dennis Pfisterer http://www.itm.uni-luebeck.de/users/pfisterer/

C++ Polymorphism
Major abstractions of C++
Data abstraction (using classes) Class relationships (using inheritance)

However, often upcasting does not offer the desired behavior Example
class Car { public: void start() { cout << "Car::start()" << endl; } }; class BMW : public Car { public: void start() { cout << "BMW::start()" << endl; } }; int main(int argc, char** argv) { Car *car = new BMW; car->start(); //Calls Car::start(), not BMW::start() delete car; }

Example: intro.cpp

C++ Polymorphism Standard behavior


The type of the pointer, not the type of the object determines the method to invoke

Desired behavior
The type of the object, not the type of the pointer should determine the method to invoke

Concept called polymorphism


Even if it looks the same (same pointer type), it may behave differently (different types of objects) Key concept for the understanding of object oriented programming

C++ Inheritance: Dynamic Binding


Compiler decides at run-time which method is invoked
Depending on the type pointed-at object Key-word virtual

Example
class Car { virtual void start () { ... } // Car::start }; class BMW : public Car { virtual void start () { ... } //Redefinition: BMW::start redefines Car::start }; Car *car; BMW *bmw; car = new Car; car->f(x); //Invokes Car::f bmw = new BMW; b->f(x); //Invokes BMW::f car = new BMW; //UPCAST !!! car->f(x); //Invokes BMW::f since the objects type is BMW

Example: introvirtual.cpp

C++ Inheritance: Dynamic Binding


Compiler adds type information to the classes data structures to determine the objects type at run-time
Keyword virtual tells the compiler to not to perform static binding (only for virtual methods)

Compilers implement this behind the scenes by adding vtables


They list address offsets of virtual functions for this object Method invocations require additional level of indirection (performance)

Derived Memory address

Base

Base

TypeInfo

Base*

TypeInfo

Derived*

Base*

C++ Inheritance: Name hiding Recall


Redefinition of non-virtual identifiers

Virtual methods behave differently


Not called redefinition, but overriding No different return types are allowed (to guarantee a common polymorphic interface)

Exception
Derived class may return Derived* instead of Base*

C++ Inheritance: Behavior in constructors Problem


Derived classes have not been initialized while the in the constructor of the base class runs As a result, virtual functions can not be invoked as they might invoke non-initialized code

Behavior
In constructors, virtual methods of the current class are invoked Same applies to destructors

C++ Inheritance: Why is virtual an option?


Why is virtual an option if it triggers the correct behavior?
C++ philosophy: If you dont use, you dont pay for it

Virtual function calls need slightly more performance


Additional level of indirection through vtable Inhibits several other performance optimizations possible with static binding

Dont be worried about efficiency in the first place


When designing classes, use polymorphism (virtual) extensively Optimization is possible later if the class concept is designed well

Example: exhaustiveexample.cpp

C++ Inheritance: Virtual destructors


Objects are deleted from memory using pointers
In conjunction with polymorphism, which destructor is called? Base* b = new Derived; b->do_something(); delete b; Same behavior as with non-virtual methods: Type of the pointer determines the function (here: destructor) invoked

Classes using polymorphism (virtual) must implement virtual destructors


class A { public: virtual ~A() { } virtual f() { } }

C++ Inheritance: Designing interfaces


Often, base classes only provide an interface and no functionality
What is start() supposed to do for Vehicle or Car ? Vehicle and Car provide the interface to the outside BMW and VW provide real functionality

10

How to avoid implementing of such interface methods?


Vehicle

Car BMW VW

Lorry

Bike

C++ Inheritance: pure virtual methods


C++ provides the concept of abstract classes
Pure virtual methods have no implementation A class containing pure virtual methods can not be instantiated Abstract classes provide a common interface that sub-classes implement

11

Syntax
virtual method-signature = 0;

Examples
class A { virtual void f() = 0; }; class B { virtual void f(const int x&) const = 0; };

C++ Inheritance: pure virtual methods


Interface and implementation
class Lamp { public: virtual void on() = 0; virtual void off() = 0; }; class LightBulb : public Lamp { public: void on() {} void off() {} private: };

12

Usage
void switch_on(Lamp& l) { l.on(); } LightBulb lb; switch_on(nl); //Polymorphism LightBulb::on()

Example: purevirtual.cpp

C++ Inheritance: Summary polymorphism Properties


The type of the object pointed-at is important, not the type of the pointer Only for functions declared to be virtual

13

Advantages
Code (new behaviors) can be added without requiring changes to other classes Easy implementation of interfaces

Disadvantages
Dynamic binding introduces run-time overhead

C++ Inheritance: Object slicing


Passing derived objects by reference or pointer is always safe
f(Base *b) { } or f(Base &b) { } Derived d; f(&d); or f (d);

14

OK, pointed-at object is still of type Derived

Passing derived objects by value leads to object slicing


f(Base b) { } Derived d; f(d);

b is a copy of d of type Base (which may not be what you wanted)

C++ Inheritance: Down-Casting


Sometimes its necessary to perform a safe down-cast
Type* t = dynamic_cast<Type*> variable; Returns NULL if the conversion was not possible Only applicable to pointers

15

Introduces some runtime overhead


Should not be used too often If used very often, there is a good chance of your design being flawed

Example
Vehicle *v = new BMW BMW* bmw = dynamic_cast<BMW*> v; VW* vw = dynamic_cast<VW*> v; if( bmw != NULL ) { } else if( vw != NULL ) { }