 Hello and welcome back. In this session, we are going to study about polymorphism and virtual functions in C++. Here is a quick recap of what we studied in the last lecture. We studied about objects of base and derived classes and also pointers and references to such objects. And we also looked at some special kinds of inheritance like multiple inheritance and diamond inheritance. In this lecture, we are going to start by recapitulating one of the member functions that we have seen earlier in the example of the bank account classes that we have studied in the earlier lectures. This member function is the print info function. We will look at it again and we will use that to motivate polymorphism in C++ programming. And we will also study about virtual destructors and abstract classes which are particularly relevant when you are talking about polymorphism. So, well what is polymorphism? The dictionary meaning of polymorphism is the condition of occurring in several different forms or the ability to assume different forms or shapes. And in computer science, we pretty much use it in the same sense and you see this word polymorphism has these two parts. The poly part means many or much and the morph part means form or shape. So, even in computer science, we are going to refer to polymorphism when a particular object or a particular entity can assume different forms or shapes depending on the context in which it is used. We will see examples of this very soon in the next few slides. In fact, we have already seen some form of this in some of our previous lectures. If you recall, we had statements like this in programs that we had seen in previous lectures. And in an assignment statement like this, we had studied in an earlier lecture that we could take an object of a derived class and assign it to an object of a base class. Now, recall that an object of the derived class actually has everything of the base class within it and it may have some additional data members or member functions. And an object of the derived class like D here can therefore, also be viewed as an object of the base class if I want to use it like an object of the base class because it has everything that an object of the base class has. So, if I want to use an object of the derived class as an object of the base class like in this assignment, I can use it I can use D like an object of the base class as well. So, the object D can therefore, be viewed as having multiple forms either an object of the derived class or an object of the base class depending on the context in which I want to use it. In this particular assignment statement, I am going to treat the object D as an object of the base class and only access data members of it which are present in the base class. We have already seen this earlier in a previous lecture. I just wanted to highlight that we have already seen such instances when the same object is treated as either belonging as either an object of a derived class or as an object of a base class depending on the context in which I am using it. Now, let us also look at the print info function from the savings and current bank accounts that we had seen in an earlier lecture. So, here was the base bank account class which had two data members ID and balance and let us say this was the method print info it is a public method in the base class and this just prints out base followed by a new line. Here is the derived class savings which has two additional data members called age and ATM and this redefines print info such that it now prints out savings followed by the new line. And here is another class which is derived from the base class this is the class current which has its own additional data members amount and overdraft and it redefines print info in its own way and this method prints out current followed by a new line. So, if I had a main program like this in this note that I have instantiated an object of the base class an object of the savings class and an object of the current class. I also have a pointer to the base class and in this assignment statement I am taking the address of S which is an object of the savings class and I am assigning it to BPTR which is actually a pointer to the base class and then I am invoking the method print info with respect to BPTR. So, BPTR arrow print info and here I am doing the same thing again, but starting from the address of C which is an object of the current class. So, here the address of C which is an object of the current class is going to be assigned to BPTR which is a pointer to the base class and then I am going to invoke BPTR arrow print info and finally it returns 0. So, the point to note here is that in this assignment statement I have taken an object of a derived class taken its address and then assigned it to BPTR whose type is a pointer to the base class, but because an object of the derived class can also be viewed as an object of the base class therefore, this assignment is fine. And now when I invoke BPTR arrow print info, I am actually invoking the print info function of the base class because BPTR is a pointer to the base class. So, therefore, base will be printed out over here. Similarly, over here since C is an object of the derived class current therefore, it can also be treated like an object of the base class. And therefore, when I take the address of C and assign it to BPTR whose type is a pointer to the base class this assignment is fine. I am effectively treating C as an object of the base class which I can do because it is derived from the base class and therefore, it has everything that the base class has. And then when I invoke BPTR arrow print info because BPTR is a pointer to the base class therefore, print info of the base class is going to get invoked and base will get printed out over here. Now the question is here when I said BPTR is assigned the address of S, I probably wanted BPTR to behave like S. So, when I invoke BPTR arrow print info, I probably wanted this to behave like the print info method of the savings class. This assignment basically has the intent that I want BPTR to behave like S. And so, this print info presumably has the intent that we wanted print info from the savings class to be printed, but of course, that is not going to happen in the C++ program. So, the question is how do I print info from the savings and the current classes at these two statements by invoking BPTR print info over here and again by invoking BPTR print info over here. So, you might ask why do I want to do this? Well, when I am writing a program maybe I do not know a priory whether BPTR is going to be assigned the address of S or whether BPTR is going to be assigned the address of C. Maybe there is an if condition here and if that condition is satisfied then I am going to assign BPTR to the address of S, otherwise I am going to assign BPTR to the address of C, but regardless of which assignment happens I want to print info, I want to call BPTR arrow print info. So, in this particular case I do not know before the program executes whether the condition in that if statement is going to be satisfied and therefore whether BPTR will get the address of S or whether BPTR will get the address of C, but what I do want is that if BPTR is assigned the address of S then the print info of the savings class should be invoked and if BPTR is assigned the address of C then the print info of the current class should be invoked. So, in this straight line program we have shown a very simple example, but in a real program whether BPTR gets assigned to the address of S, whether the address of S gets assigned to BPTR or whether the address of C gets assigned to BPTR might depend on some condition and we may not be able to know this a priori. So, whenever we are making the call of BPTR arrow print info there is no way to a priori say whether I wanted to call print info of the savings class or whether I wanted to call print info of the current class and in such cases this question is very relevant that I want to call print info of the class depending on whether the address of S got assigned to BPTR or the address of C got assigned to BPTR. If the address of S got assigned to BPTR I want to call print info of the savings class otherwise I want to call print info of the current class. So, the question is how do we do this in C plus plus well this is what we want we want BPTR arrow print info to behave as print info in the savings class if this assignment was made and we wanted to behave as print info in the current class after this assignment is made and as I said if there is an if condition which is guarding whether this assignment is made or this assignment is made a priori just by looking at the program we may not be able to determine whether BPTR will take on the address of S or BPTR will take on the address of C. So, a priori it is not possible to determine whether BPTR print info should behave like print info in savings class or it should behave like print info in current class this is something which has to be determined at runtime while the program is running. If this assignment is done then I want BPTR print info to behave like print info in the savings class otherwise if this assignment is done I want BPTR print info to behave like print info in the current class. So, how do we do this in C plus plus programming C plus plus actually provides a very elegant way to do this and this is called virtual functions and we are going to study a lot about this in the next few slides and virtual functions essentially implement polymorphism in function calls as you can see here I want the same function called to behave either like print info of the savings class or like print info of the current class depending on the actual context when I am running whether BPTR was assigned the address of S or whether it was assigned the address of C. So, I really want this function to behave in one of two different ways which is going to be determined at runtime. So, this is nothing but an example of polymorphism and the way we are going to implement this polymorphism in C plus plus is through virtual functions. So, this is how we use virtual functions here I have the same class base with the same data members, but instead of saying that I am defining print info to do something here printing out base followed by new line I say that well I wanted to do this, but I am going to add this keyword virtual before the actual function definition and what this does is this actually tells the compiler that well do not really use this function if the context in which print info is getting called dictates that some other print info should be used and where is that other print info going to come from from the derived classes. So, here the savings class is derived from the base class and it is defining what print info should behave like and here is the current class again derived from the base class and this is deriving what print info should be doing and this virtual basically says that if you have a pointer to an object of the base class first figure out whether this pointer was really pointing to an object of the savings class or whether it was pointing to an object of the current class note that an object of the savings class can also be viewed as an object of the base class and similarly for an object of the current class. So, this virtual specification here basically says that determine what print info to call at runtime and not at compile time. So, at runtime let us figure out what is a pointed to the base class pointing to if it is pointing to an object of the base class then we will call this, but if it is actually pointing to an object of the savings class like we saw in an earlier example then it should actually call this and if it is actually pointing to an object of the current class then it should actually call this. So, here is the main function which is actually the same as the main function that we saw in the previous slide. However, here since in the base class I have declared print info to be virtual when the compiler compiles this main function and when it encounters BPTR arrow print info over here it actually does not go and try to put a call to the print info member function of the base class although BPTR is a pointed to the base class instead what it does is it says that at runtime we will determine what BPTR is really pointing to, because I have specified print info to be virtual and then depending on what it is really pointing to we will call print info. So, if it is really pointing to an object of the base class we will call this function otherwise if it is pointing to an object of the savings class then we will call this function otherwise if it is pointing to an object of the current class we will call this function. So, that is what is going to happen at these two statements. So, the specification virtual in the class definition instructs the compiler that when it is compiling this program, it should not compile these two statements directly to calls to the print info member function of the base class. Instead at run time, it should determine what VPTR is pointing to and accordingly call the print info member function of the appropriate class which could be the base class or the derived class. So, here we are assigning the address of a savings object to the base pointer which is fine we have seen earlier, but now because VPTR is really pointing to an object of the savings class and because print info is declared as virtual in the base class definition. So, therefore, this print info will be the print info definition from the savings class and you will see the output savings over here coming from this statement. Here the same pointer variable VPTR is now assigned the value of the address of C where C is an object of the current class. So, when we say VPTR arrow print info over here because print info is declared virtual in the base class definition. So, the print info of the base class is not going to be called here instead at run time it will be determined what VPTR is pointing to VPTR is in this case pointing to an object of the class current and therefore, print info as defined in the class current will be called and therefore, you will see on the output current being printed here. So, you see that this print info VPTR arrow print info behaves in one way here and behaves in the other way here and this is basically saying that this function print info is polymorphic it can take one of two different forms depending on what VPTR is really pointing to and this is going to be determined at run time when the program is executing and how do we specify this when we are writing the program by saying that the print info function in the base class is virtual. Now, here we have the same program but now we have augmented it with another method another function here in the base class but note that this method is not a virtual function it is our usual standard function or method and it prints out base call followed by a new line and let us say in the savings class I have redefined it and in the current class I have redefined it as well and now here we have the same main program as we saw earlier except that now I have VPTR arrow call at these two locations. Now, this is very interesting because VPTR is a pointer to the base class and in this assignment statement the address of an object of the derived class which is going to be viewed as an object of the base class is assigned to this pointer. And then when I say VPTR arrow call remember VPTR is a pointer of the base class. So, in the base class I will have to go and see what call is and call has not been declared as virtual. Therefore, this VPTR arrow call should do exactly what the method call in the base class asks it to do which means it should just print out base call. However, VPTR arrow print info it is the same VPTR arrow print info here because print info in the base class has been declared as virtual. Therefore, here it is going to invoke the print info function from the object that it is really pointing to in this case it is an object of class savings and therefore, savings gets printed here. And now once again VPTR is assigned the address of C. So, this is once again fine because C can also be viewed as an object of the base class. And when we again do VPTR arrow call VPTR is appointed to the base class. So, VPTR arrow call means we have to go to the base class see what the method call is and it is not a virtual method. So, therefore, whatever this method call in the base class is saying we have to do that. So, it is going to print base call here, but in the very next statement when I use the same VPTR to say VPTR arrow print info in the base class because print info is declared virtual. Therefore, I have to figure out at this point what is VPTR really pointing to it is pointing to an object of the class current and I have to use the print info function from the class current and so current is going to be printed out over here. So, you see the different things that you can do when you declare a particular method is not virtual and when you declare another method is virtual. Everything which is not declared as virtual will be executed as defined in the base class because I am using a pointer to the base object here, but everything which is declared as virtual at runtime it will be determined what is it really pointing to and the definition of print info from that class is going to be picked up. Here is a different variant of polymorphism, here is the base class and here is the same virtual method and here is another method print and note that within print I have invoked print info and here is the derived class savings which has a definition of print info and here is the derived class current which has a definition of print info. Now, this method which is calling print info and print info being declared as virtual, so what method is it really going to call here depends on what object context in which I am calling this method print. So, to illustrate this let us look at this program, here I have three different objects of class base savings and current, here I am calling b dot print, so b is an object of class base. So, it is going to come here and b is an object of class base, so it must directly call the print method and in the print method I see an invocation to print info and print info is declared as virtual. So, I must go back and ask well what is the context in which this call is being made, the context in which this call is being made is in the context of an object of class base. So, therefore I should use the definition of print info as in the class base and this should get printed out. However, when I call s dot print, s being an object of class savings is also an object of class base and so the same print method of the base class is going to be invoked here and within the body of the print method there is an invocation of print info. Print info in the base class is declared as a virtual function and what does this mean? It means that at run time we must determine what is the context in which this print info call is being made, this is being called in the context of s and so we must not pick up this definition of print info, we must pick up the definition of print info as in the class savings and savings will get printed here. And here is c dot print once again c being an object of the class current is also an object of the class base. So, c dot print will cause an invocation of this method in the class base. In the body of that method print info is going to get called, but print info is declared as virtual. So, therefore we have to ask what is the context in which this method print info is being called, the context is an object of class current and therefore because print info is declared as virtual we must call print info as defined in the class current and therefore current will get printed here. So, you see that the function print behaves in three different ways depending on whether I am calling it in the context of a base object or a savings object or a current object although the function print is itself not a virtual function. And why does this happen because the function print in its body calls another method which is actually declared to be virtual. And so when I am trying to execute this method here I have to go and find out what is the context in which this call is being made and have to use the definition of print info from that context. Now, having seen a little bit about polymorphism and virtual functions let us consider an interesting situation as depicted in this slide. So, here I have a class A where everything is public and class B is derived from class A which is indicated by this arrow going from class B to class A. Inside class B I have a private data member z whose type is an integer pointer and this is the constructor for the class B and inside the constructor I allocate memory for a new integer and I store the address of the newly allocated integer in this private data member z. Here is the main function a p t r is a pointer of class A, a p t r is a pointer to an object of class A and in this statement I am allocating a new object of class B and assigning it to a p t r. But since an object of class B can also be viewed as an object of class A, so the address of the newly allocated object of class B can also be viewed as an address of an object of class A and therefore, this assignment is fine. Now, the question is somewhere in this main program suppose I say delete a p t r. So, because a p t r is a pointer to an object of class A, so delete a p t r will go and invoke the destructor of class A. However, I am really trying to deallocate the space allocated for this object of class B, because a p t r was really pointing to an object of class B and this object of class B had already dynamically allocated some memory for an integer and store the address of that in z. But when the destructor of A is called if I execute a statement delete a p t r when the destructor of A is called there is no way from the destructor of A I can access this private data member of class B and I can free up this space allocated for the new integer. So, the problem that we have is how do we delete resources or memory which is allocated inside the derived class, but using a pointer to the base class just like we have here. So, a p t r is a pointer to the base class I have assigned to a p t r the address of a newly allocated object of the derived class and I want to use delete a p t r, but I also want to free up the space that was allocated in an object of the derived class which is really what a p t r was pointing to when I have delete a p t r over here. So, here is a more fleshed out version of that example. So, here is the class A this is the constructor and let us say we just print A here followed by a new line here is the destructor let us say I just print tilde A followed by the new line. Here is the class B which is derived from the class A has the same private data member z as we saw earlier. Here is the constructor for B which allocates memory for a new integer and then prints out B followed by a new line. Here is the destructor for B which prints out tilde B followed by the new line and if this destructor of B gets called then it is going to free up the space that was allocated here in the constructor for that one integer. But the question is are we going to be able to call this destructor from the main program if our main program is written like this what is this main program doing a p t r is a pointer to an object of the class A I am assigning to a p t r the address returned by new B this is going to give me the address of a freshly allocated object of class B in the heap and then I have delete a p t r the question is when I call delete a p t r a p t r being a pointer to an object of class A it is going to try to invoke the destructor of the class A and in that process I will not be invoking the destructor of class B although a p t r was really pointing to an object of class B and therefore if I just invoke the destructor of class A and not the destructor of class B the memory that was allocated for z is not going to be freed up. So, let us see how this works. So, this is the declaration of a p t r and let us say I am I execute this statement because I am allocating an object of class B. So, the constructor of B is going to get called but note that B is an object of a derived class. So, when I call the constructor of B the default constructor of A is going to get called here and I am going to see A on the output and then I am going to come and execute the rest of the body of the constructor of B. So, a new integer will be allocated space for a new integer will be allocated its address will be copied to this private data member z and B will get printed out. After that when I am executing the statement delete a p t r because a p t r is an object because a p t r is a pointer to an object of class A inside the class A I have to come and look up the destructor and this destructor says just print tilde A and that is it I just return 0. So, note that the destructor of class B never even got called and whatever memory was allocated for the integer z let us say an integer takes 4 bytes of memory and these are the addresses of those 4 consecutive bytes in memory. Whatever space was allocated for this does not get freed up the memory for z is not freed and therefore, our problem that we wanted to free up this memory when we call delete a p t r because a p t r was really pointing to an object of class B and in the constructor for B we had allocated object for this integer. So, really when we call delete a p t r we wanted to free up that integer we do not want this allocated object of class B anymore. So, we wanted to free up the space allocated for that integer, but that does not happen if I use a normal destructor for the class A like we have seen all throughout. However, if I use what is called a virtual destructor then just like for virtual methods when I go to the base class and I am trying to invoke a method and I see that the method is declared as virtual then I must first find out what is the context in which that method is being called and then I must call the method from the appropriate derived class just like that we could declare the destructor of A also as virtual and in this case if the context in which the delete statement was being executed was the context of an object of class B then the destructor of class B is going to get called. So, therefore, the destructor of class A can be declared as virtual and now I have the same program and let us see what happens now. So, here is the declaration here is once again allocating a new object of class B which calls the constructor of B which in turn calls the constructor of A that prints out A over here then the rest of the body of B is executed which means space for a new integer is allocated and that address is stored in the private data member Z of the class B and B is printed out over here and after that I am going to execute delete APTR and when I do that APTR being a pointer to an object of class A I am going to come here and see what the destructor of class A does and lo and behold this destructor is specified as virtual. So, therefore, I have to find out the context in which I am calling this destructor the context in which I am trying to do this delete APTR which means basically what was APTR really pointing to it was actually pointing to an object of class B. Therefore, the destructor of B is now going to get called and if you remember from an earlier lecture when we are calling a destructor of a derived class first the statements in the destructor of the derived class are going to get called then the statements in the destructor of the base class are going to get called. So, that is exactly what is going to happen here. So, when I call the destructor of B first the statements in its body are going to get executed. So, you will print tilde B you will then actually go ahead and free up the memory that was allocated for the integer over here and after that you are going to call the destructor for the class A this is exactly what we have studied earlier that when we call the destructor of a derived class first the statements in the body of the destructor of the derived class are executed then the statements in the body of the base class are executed. So, now this is going to get executed and tilde A is going to get printed and now we are fine we have actually freed up the memory that was allocated for a new integer in the constructor for B even when I said delete APTR and how did we do this by using virtual destructors just like we used virtual methods earlier. When I say delete APTR and the destructor of the base class is declared as virtual I must first find out what is APTR really pointing to and call the destructor of the appropriate derived class. Now, there is one more important concept that we are going to study in the context of polymorphism and virtual functions and this is what is called an abstract class. So, at a very high level an abstract class is something which cannot be instantiated directly and we can only instantiate objects of classes derived from this abstract class. So, it is only meant to be used as the base class of some derived classes and we can instantiate objects of this derived classes and how do we implement an abstract class we implemented by writing a class definition in which we have what are called pure virtual functions we will see in a minute what are pure virtual functions and these pure virtual functions should be overridden by member function definitions of the derived classes. Remember that an abstract class is intended to be used only for deriving other derived classes and so those derived classes should have member function definitions which will override the pure virtual functions in the abstract class. When should we use abstract classes when using the base class directly has no meaningful purpose and it only makes sense to use it as a derived class. So, here is an example that we have already seen. So, if you recall the bank account example where a person does not just have a bank account it is either a savings bank account or a current bank account or maybe some other kind of bank account. So, really when we want to instantiate an object we want to instantiate an object of a savings account or a current account not just a bank account and so that is why instantiating the class base the base account by itself has no meaningful purpose and so therefore the base account could be specified as an abstract class. It is always intended that we are going to derive some other classes from it like a savings class or a current class and objects are going to be instantiated only for these derived classes not for the base class. So, here is an example. So, here is the base class and in this base class I have two member methods both of which I have declared as virtual, but then I have also this special thing assigned 0 and this is how we specify what are called pure virtual functions. So, when I write a virtual function and say equal 0 then what I mean is that this class in which this virtual function assigned 0 is being specified this is an abstract class this is a pure virtual function we are not going to give a definition of this function in this class and why is that because this is a abstract class it is always meant to be used as the base class of a derived class and in the derived class we are going to define what these member functions call and print info are. So, a virtual function which is specified like this which is the usual virtual function specification and then equals 0 this is called a pure virtual function it does not have a body at all and it immediately makes the class in which this is defined as an abstract class. So, here is the derived savings class in which note now that I have redefined the call and the print info methods here is the current class which is also derived from the base class and I have redefined the call and the print info method and here is a main program in which if I try to instantiate an object of the base class I will actually get a compilation error because this base class is an abstract class it does not even have a definition of the methods call and print info these are pure virtual methods over here pure virtual functions over here. So, you cannot really instantiate an object of the base class you can only instantiate objects of derived classes like I can instantiate an object of class savings or I can instantiate an object of class current. However, I can have a pointed to the base class and to that pointer I can assign the address of an object of savings class or an object of the current class like we have already seen earlier and then when I say BRO call I go to the base class this is an abstract class in which there is a virtual function which does not even have a definition. So, I have to go to the context in which this call is being made this invocation is being made and the context is B is pointing to an object of class savings. So, I have to invoke the method from the class savings and similarly for print info. So, here you will see savings call and savings being printed out over here the same B which is a pointed to the base class is being assigned the address of an object of the current class. And then when I say BRO call BRO print info B is just an abstract class it does not even have definitions of call and print info these are pure virtual functions. So, when I say BRO call I have to find the context in which this method is being invoked the context is that B is pointing to an object of class current. So, I have to use the definition of call as in the class current and similarly I have to use the definition of print info as in the class current and so current call and current are going to get printed over here. Here is another example of an abstract class this is the base class in which I have declared one pure virtual function. But there is another function here another member method here which is neither virtual nor is it pure virtual and here is a derived class class savings which has its own definition of the pure virtual function over here and here is the class current which has its own definition of the pure virtual function over here. In this case as you can see in the savings account the interest is like 10 percent here in the current account the interest is like 15 percent. And now of course in the main program I declare an object of class savings an object of class current and when I say S dot set balance S being an object of class savings inherits the data members and non-virtual member functions of the class base. So, S dot set balance really goes and invokes this note that this is not a virtual function of the class base. So, this is inherited by the class savings and this gets invoked and balance for the object S gets set to 20,000 and then we print this savings interest and then we call S dot get interest and S is now an object of class savings which is also an object of class base. But here get interest is a virtual function in the class base in fact it is a pure virtual function and it is redefined in the class savings. So, when I say S dot get interest it is this function which gets called and it returns the value that you will get by doing this calculation. So, in this particular case as I said S dot set balance really calls this S dot get interest calls this and 2000 gets printed. Similarly, C dot set balance C being an object of the class current which is derived from the class base and set balance being a non-virtual function in the base class. C dot set balance will again invoke this and so now the data member balance of the object C is going to be set to 20,000 and then when I call C dot get interest because get interest is a pure virtual function of the class base. C dot get interest will invoke the get interest as defined in the class current and it will return whatever is returned by this calculation which in this case turns out to be 3000. So, for abstract classes what you need to remember is that these are used only when the base class is meant for derivation purposes not for instantiation. You cannot instantiate objects of an abstract class. An abstract class helps in readability and understanding because the moment you see a pure virtual function you know that this is an abstract class. It also prevents accidental instantiation of the abstract class because the compiler is going to complain about that. So, in summary in this lecture we looked at polymorphism in C++ programming, we looked at virtual destructors and we also looked at the interesting concept of abstract classes. Thank you for your attention.