Presentation is loading. Please wait.

Presentation is loading. Please wait.

Polymorphism and Virtual Functions. Topics Polymorphism Virtual Functions Pure Virtual Functions Abstract Base Classes Virtual Destructors V-Tables Run.

Similar presentations


Presentation on theme: "Polymorphism and Virtual Functions. Topics Polymorphism Virtual Functions Pure Virtual Functions Abstract Base Classes Virtual Destructors V-Tables Run."— Presentation transcript:

1 Polymorphism and Virtual Functions

2 Topics Polymorphism Virtual Functions Pure Virtual Functions Abstract Base Classes Virtual Destructors V-Tables Run Time Type Identification (RTTI) Dynamic Cast

3 Objectives At the completion of this topic, students should be able to: Design classes that enable polymorphism * Properly use inheritance * Include virtual functions Correctly use polymorphism in a program * Store pointers to dynamically created objects in arrays of base class pointers Explain how V-Tables are used to make polymorphism work Explain why a virtual destructor is needed Safely access base class functions in a program, using rtti and dynamic casts.

4 Introduction : The Orchestra

5 From Inheritance... Recall that you can store the address of a derived class object in a base class pointer. What happens if you use the base class pointer to invoke a function that exists in the base class, but has been over-ridden in the derived class?

6 If you use the Animal pointer, it treats jumbo as if it were an animal. For example … jumbo bigAnimalPtr bigAnimalPtr -> printMe( ); The function in the base class is invoked because we are using a base class pointer.

7 Virtual Functions class Instrument { public: virtual void play ( ) const; void display ( ); protected: string name; }; the keyword virtual says that we can over-ride this function in a derived class. But, when we use a base class pointer to point to the object, and invoke the virtual function, the system will automatically find and execute the function in the derived class, not the base class! Moreover, if we have many different derived classes, the system will find the correct function for the object that the pointer points to. This is called late binding, or dynamic binding.

8 Instrument BrassWoodwindPercussion 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; }

9 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 …

10 Instrument BrassWoodwindPercussion 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 function gets invoked!

11 Rules for Polymorphism In the base class, the keyword virtual must precede any function that you want to call using polymorphism. In the derived class the signature must exactly match the signature of the function being over-ridden. If the signature is different, the compiler considers it to be a different function, not an over-riding function. The actual implementation of the function in the derived class will be different than that in the base class. The function is invoked through a base class pointer that contains the address of the derived class object.

12 Pure Virtual Functions In the previous example, we provided an implementation for the play( ) function in the base class. Often times you will not need to provide an implementation for a virtual function in the base class. You can do this by making the function a pure virtual function. Pure virtual functions have no implementation.

13 class Instrument { public: virtual void play ( ) const = 0; void display ( ); protected: string name; } the =0 notation signals that this is a pure virtual function. There should be no implementation of this function written.

14 Abstract Classes If a class contains at least one pure virtual function then it is impossible to create an object of that class. Why? Such classes are called abstract classes.

15 Virtual Destructors If an object is destroyed by applying the delete operator to a base class pointer, only the base class destructor is called. This can leave derived class data members on the heap with no way to delete them. This problem is solved by making the destructor in the base class virtual. Polymorphism then causes the derived class destructor to be called first. It will automatically call the base class destructor, insuring that all data is properly removed from the heap.

16 V-Tables When the compiler encounters a class definition that contains virtual functions, it builds a v-table for that class. The v-table contains the addresses of 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

17 When the compiler encounters a derived class definition that inherits publicly from this base class, it copies the v-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

18 Now, for any function in the derived class that over-rides a virtual function in the base class, the compiler sets the address for that function to the derived class function’s address. 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 function does not match the one in the base class, so no change is made to the v-table.

19 Brass’s v-table play ( ) ~Instrument( ) display( ) Instrument::display( ) Brass::play( ) Brass::~Brass( ) Now, when an object of the derived class is created a pointer to the class’s v-table is added to the object. Brass myTuba ( some parameters ); myTuba member data

20 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 member data Instrument * orchestra; orchestra = &myTuba; orchestra

21 Invoke the play( ) function Brass’s v-table play ( ) ~Instrument( ) display( ) Instrument::display( ) Brass::play( ) Brass::~Brass( ) myTuba member data 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.

22 What happens now if you want to invoke the display (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 it is not in the v-table.

23 So, if you write orchestra -> play ( 5 ); You will get a compiler error, because Orchestra is a base class pointer, and the function play ( int ) is not defined in the base class.

24 As you have seen, when you store the address of a derived class object in a base class pointer, you lose information about the class that the object belongs to. Thus, it seems impossible to invoke the derived class function play (int) with the statement orchestra -> play (5);

25 We could cast the pointer to a derived class pointer … but what if we are not sure that the object being pointed to really is an object of the Brass class?

26 Run Time Type Identification Run Time Type Identification (RTTI) provides a way to find out what kind of an object is pointed to by a base class pointer. To find the type of object being pointed to, we use a special function typeid ( ). To use this function we must #include.

27 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 );

28 We can determine whether or not an object pointed to in the array of base class pointers is a Brass instrument by writing for (int i = 0; i < 3; i++ ) { if ((typeid (*orchestra[i])) == typeid (Brass)) { // code } dereference the pointer and get the typeid of what it points to. then compare it with the typeid of the Brass class.

29 If we determine that the object pointed to is, in fact, an object of the Brass class, then we can downcast the 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 Casting dynamic_cast (orchestra[i]) -> display (5); this is a base class pointer this casts the pointer to a Brass pointer and we can now invoke the display (int) function!

30 Notes 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


Download ppt "Polymorphism and Virtual Functions. Topics Polymorphism Virtual Functions Pure Virtual Functions Abstract Base Classes Virtual Destructors V-Tables Run."

Similar presentations


Ads by Google