polymorphism and virtual functions. topics polymorphism virtual functions pure virtual functions...
TRANSCRIPT
Polymorphism and Virtual Polymorphism and Virtual FunctionsFunctions
TopicsTopics
PolymorphismVirtual FunctionsPure Virtual FunctionsAbstract Base ClassesVirtual DestructorsV-TablesRun Time Type Identification (RTTI)Dynamic Cast
ObjectivesObjectives
At the completion of this topic, students should be able to:
Design classes that enable polymorphism* Properly use inheritance* Include virtual functionsCorrectly use polymorphism in a program* Store pointers to dynamically created objects in arrays of base class pointersExplain how V-Tables are used to make polymorphism workExplain why a virtual destructor is neededSafely access base class functions in a program, using rtti anddynamic casts.
Introduction : The OrchestraIntroduction : The Orchestra
polymorphism.swf
From Inheritance . . .From Inheritance . . .
Recall that you can store the address of a derived classobject in a base class pointer.
What happens if you use the base class pointer toinvoke a function that exists in the base class, but hasbeen over-ridden in the derived class?
If you use the Animal pointer, it treatsjumbo as if it were an animal. For example …
jumbo
bigAnimalPtr
bigAnimalPtr -> printMe( );
The function in the base class is invokedbecause we are using a base class pointer.
Virtual FunctionsVirtual Functionsclass Instrument{ public: virtual void play ( ) const; void display ( ); protected: string name;};
the keyword virtual says that wecan over-ride this function in aderived class. But, when we use a baseclass pointer to point to the object,and invoke the virtual function, thesystem will automatically find andexecute the function in the derivedclass, not the base class!
Moreover, if we have many different derivedclasses, the system will find the correctfunction for the object that the pointer points to.
This is called late binding,or dynamic binding.
Instrument
Brass Woodwind Percussion
virtual void Instrument::play ( ) const{ cout << “I am an Instrument.” << “ I have no sound.” << endl;}
void Brass::play ( ) const{ cout << “I am a Brass Instrument.” << “ I go ” << sound << endl;} void Woodwind::play ( ) const
{ cout << “I am a Woodwind Instrument.” << “ I go ” << sound << endl;}
void Percussion::play ( ) const{ cout << “I am a Percussion Instrument.” << “ I go ” << sound << endl;}
Instrument *orchestra[3];
orchestra[0] = new Percussion (some parameters);orchestra[1] = new Woodwind (some parameters);orchestra[2] = new Brass (some parameters);
orchestra[1]->play( );
What happens when …What happens when …
Instrument
Brass Woodwind Percussion
virtual void Instrument::play ( ) const{ cout << “I am an Instrument.” << “ I have no sound.” << endl;}
void Brass::play ( ) const{ cout << “I am a Brass Instrument.” << “ I go ” << sound << endl;} void Woodwind::play ( ) const
{ cout << “I am a Woodwind Instrument.” << “ I go ” << sound << endl;}
void Percussion::play ( ) const{ cout << “I am a Percussion Instrument.” << “ I go ” << sound << endl;}
This functiongets invoked!
Rules for PolymorphismRules for Polymorphism
In the base class, the keyword virtual must precedeany function that you want to call using polymorphism.
In the derived class the signature must exactly matchthe signature of the function being over-ridden. If thesignature is different, the compiler considers it to bea different function, not an over-riding function.
The actual implementation of the function in thederived class will be different than that in the baseclass.
The function is invoked through a base class pointerthat contains the address of the derived class object.
Pure Virtual FunctionsPure Virtual Functions
In the previous example, we provided an implementationfor the play( ) function in the base class. Often times youwill not need to provide an implementation for a virtualfunction in the base class.
You can do this by making the function a pure virtualfunction. Pure virtual functions have no implementation.
class Instrument{ public: virtual void play ( ) const = 0; void display ( ); protected: string name;} the =0 notation signals that this
is a pure virtual function. Thereshould be no implementation ofthis function written.
Abstract ClassesAbstract Classes
If a class contains at least one pure virtual functionthen it is impossible to create an object of that class.
Why?
Such classes are called abstract classes.
Virtual DestructorsVirtual Destructors
If an object is destroyed by applying the delete operatorto a base class pointer, only the base class destructor iscalled. This can leave derived class data members on theheap with no way to delete them.
This problem is solved by making the destructor in thebase class virtual. Polymorphism then causes the derivedclass destructor to be called first. It will automatically callthe base class destructor, insuring that all data isproperly removed from the heap.
V-TablesV-Tables
When the compiler encounters a class definitionthat contains virtual functions, it builds a v-tablefor that class. The v-table contains the addressesof all of the virtual functions for the class.
class Instrument{ public: virtual void play ( ); virtual void display ( ); virtual ~Instrument ( );};
Instrument’s v-table
play ( )
~Instrument( )
Instrument::play
Instrument::~Instrument
display( ) Instrument::display
When the compiler encounters a derived class definitionthat inherits publicly from this base class, it copies thev-table from the base class for the derived class.
class Brass{ public: void play ( ); void display ( int ); ~Brass ( );}; Brass’s v-table
play ( )
~Instrument( )
Instrument::play
Instrument::~Instrument
display( ) Instrument::display
Now, for any function in the derived class that over-ridesa virtual function in the base class, the compiler sets theaddress for that function to the derived class function’saddress.
class Brass{ public:
Brass’s v-table
play ( )
~Instrument( )
Instrument::play
Instrument::~Instrument
display( ) Instrument::display
void play ( );
Brass::play( )
void display( int);
~Brass( );
Brass::~Brass( )
the signature of the display functiondoes not match the one in the base
class, so no change is made to the v-table.
Brass’s v-table
play ( )
~Instrument( )
display( ) Instrument::display( )
Brass::play( )
Brass::~Brass( )
Now, when an object of the derived class is createda pointer to the class’s v-table is added to the object.
Brass myTuba (some parameters);
myTuba
memberdata
Store the address of the object in a base class pointer
Brass’s v-table
play ( )
~Instrument( )
display( ) Instrument::display( )
Brass::play( )
Brass::~Brass( )myTuba
memberdata
Instrument * orchestra;orchestra = &myTuba;
orchestra
Invoke the play( ) function
Brass’s v-table
play ( )
~Instrument( )
display( ) Instrument::display( )
Brass::play( )
Brass::~Brass( )myTuba
memberdata
orchestra -> play ( );
orchestra
1. dereference the pointer to find the object.
2. locate the pointer to the v-table in the object.
3. dereference the pointer to access the v-table
4. locate the play( ) function in the v-table and use the corresponding address to invoke the play function.
What happens now if you want to invoke thedisplay (int) function from the Brass class?
class Brass{ public: void play ( ); void display ( int ); ~Brass ( );}; Brass’s v-table
play ( )
~Instrument( )
Instrument::play
Instrument::~Instrument
display( ) Instrument::display
the function display (int) was not defined in the base class, and it is not virtual, so itis not in the v-table.
So, if you write
orchestra -> play ( 5 );
You will get a compiler error, becauseOrchestra is a base class pointer, and thefunction play ( int ) is not defined in thebase class.
As you have seen, when you store the addressof a derived class object in a base class pointer,you lose information about the class that theobject belongs to. Thus, it seems impossible toinvoke the derived class function play (int) withthe statement
orchestra -> play (5);
We could cast the pointer to a derived classpointer … but what if we are not sure thatthe object being pointed to really is an objectof the Brass class?
Run Time Type IdentificationRun Time Type Identification
Run Time Type Identification (RTTI) provides a wayto find out what kind of an object is pointed to by abase class pointer.
To find the type of object being pointed to, we usea special function typeid ( ). To use this function wemust #include <typeinfo>.
Suppose you have the following situation:
Instrument *orchestra[3];
orchestra[0] = new Brass (some parameters);orchestra[1] = new Percussion (some parameters);orchestra[2] = new Woodwind (some parameters);
We can determine whether or not an object pointedto in the array of base class pointers is a Brassinstrument by writing
for (int i = 0; i < 3; i++ ){ if ((typeid (*orchestra[i])) == typeid (Brass)) { // code }} dereference the pointer
and get the typeid of whatit points to.
then compare it with thetypeid of the Brass class.
If we determine that the object pointed to is, in fact,an object of the Brass class, then we can downcastthe pointer and invoke the derived class function.
In this case we use a dynamic cast. The dynamic cast checks at run time to be sure that the cast is safe.
Dynamic CastingDynamic Casting
dynamic_cast<Brass*>(orchestra[i]) -> display (5);
this is a base class pointerthis casts the pointer
to a Brass pointerand we can now invoke
the display (int) function!
NotesNotes
• There is some overhead involved with the use of RTTI• The typeid function should only be used for comparisons• Dynamic casting also involves some overhead• Dynamic casting only works with pointers