>> OK. We think we're recording now. So if we don't make it, we're going
to start talking about Chapter 10. What we did in the first part of class
is review Chapter 9 on inheritance. And I advice doing that before diving
into chapter 10, because we're kind of assuming you understand
how inheritance works when we start talking
about polymorphism. Now polymorphism is how
objected-oriented program-- programming is implemented
in many languages. And this-- What this word
polymorphism, comes from many shapes or multiple shapes, shape shifter. The idea is that at one runtime of a
program, you can change object type. During the run of a program, you
can point to different objects and operate and on the block. So, what are the-- for polymorphism
it's going to use class inheritance. It's going to use method overloading, and it's going to use this
new concept we'll introduce in this chapter called
virtual functions. And it uses dynamic binding
which is another fancy way of saying we're going to use pointers. So, you'd need pointers,
virtual functions, and a class hierarchy
and overloaded methods. You need all four of those
things to make polymorphism work. So, what we're going to do is
to introduce this, you know, make a class hierarchy if
you want to think of that as framework that's fun too. We're going to make a
framework of classes that have certain inheriting properties
and just engineer the functionality of these classes to do nothing more
than demonstrate how polymorphism works. So the whole purpose of this class and
what we're going to do all today is just to demonstrate polymorphism. So here, we see derivation
table for the B hierarchy. OK. So we have a base class B
and we have derived class D1. And derived class D2. And your derived classes, D1, derives
from base, D2 derives from base. We're going to put in two more
classes, D3 which derives from D1, and D4 which derives from D2. So we have a framework which I
could draw, I'm going to try to do that real quick on the board. [ Noise ] Whiteboards are great for modeling
class [inaudible] and stuff. So we got a base class and we have D1, derived class which is
derived from base. Got to look at my-- And
D2 is derived from base. So we will draw that this way. And then we have D3 which
is derived from D1. So D3 is here, it derives from D1. And D4 is here and it derives from D2. So that is our class hierarchy, or if you like the cool word,
framework for programming. And this will be a framework
for polymorphic programming. I'll turn this back. Since it has to be there, you
may as well watch me talk. You're not watching Blackboard. OK. So notice-- So our functionalities
are going to be just report to the screen what's been called. So for example, class B constructor
just says, "I'm the B constructor." And the destructor says,
"I'm the B destructor." We're going to have functions, public
methods for the base class called F, G and H. And they're just going to say,
"OK, the B version of F got called, the B version of G got called, and
the B version of H got called." So, what you see on screen is
exactly what function got called. OK. So here is our D1, deriving
publicly from the base class. And while D2 also derived in
publicly from the same base class. So D1, of course, I'm
actually implementing it in the class here for simplicity. So what you see here is the
initialization list for D1. It calls the base class
constructor, that's the same chart. And then the D1 constructor says,
"Hey, D1 constructor got called." The D1 destructor says, "OK,
the D1 destructor got called." And now, G will be overloaded for D1 and
reports that-- that it's been called. H will be overloaded for D1 and
report that it's been called. Remember, we had an F in the base class
and we're not overloading F in D1. OK. [Inaudible] for D2, right. So, it's the same as-- looks like
the implementation of D1 except that D1 it applies by D2
and all the statements. OK. D3 inherits from D1. And so, we make a call to the D1
constructor and then we say, "OK, the D3 constructor got called." D3 destructor says, "The
D3 destructor got called." We're going to overload
H in D3 and report-- all it does is report that it
got called in the D3 version. D4, the [inaudible] the same,
but we're going to overload F and H. Now remember F is one that
was in the base class but not in the first layer of derived classes. So, F is skipping a generation and
getting overload in the grandchild class but not in the child class. [ Pause ] OK. So, the first test is we're going
to make a client program called-- it's a program, it says int main, and
you're going to declare the variables and then it's going to allow you to
enter a type number and types available to your 1, 2, 3, and 4 for each of
the different derived classed like D1. Actually zero as well. Zeros for the base class, 1 is for D1,
2 is for D2, 3 is for D3, 4 is for D4. And you can see that if it's 0, it calls
b dot G-- dot F, G and H. If it's 1, it calls d1 dot F, G, and H. If it's 2,
it calls d2 dot F, G, and H and so on. All right. So, what I will do is
I'm going to pause this. OK. I have to take a time out
and get logged in to linprog. I forgot to do that before
starting the recording. So here's what I want to do here. This is my linprog login, full screen. I'm going to just create a file with
e-max and I'll call it demo1.cpp, we've already done [inaudible]. Actually, I've already done this, so--
but, before I got this I just copied and paste it this right out
the lecture note slides. So you can do the same if you want to. So let's make sure-- yeah, so we got B
constructor, destructor, F, G and H. D1 and from F, G, and H. So yeah, this is the same class hierarchy that's
mentioned in the slides, in main. OK. It's exactly that program. So what I'm going to do is
just get out of e-max and-- [ Pause ] -- compile demo1 with my
command line, a compiler. And notice-- OK, I-- that's forgetting
to run, you know, the program. And then this is the program outputting
that phrase, declared variables. So, let me just point out where that is. Declaring variables, it says. Yeah, declaring variables. And then it declared B, then it
declared D1, and D2, and D3 and D4. Well, declaring B called
the B constructor. Declaring D1 called first--
ends up with the first call of the D1 constructor followed
by call with the B constructor. Similarly, declaring D2 calls
the D2 constructor followed by a call to the B constructor. D3 or over in here acts from
D1 and then there is from B. So D3 constructor gets
called followed-- So, first-- I think I said that backwards
a minute ago. First, you're calling the
base class constructor. I mean you're executing
a base class constructor. And then the parent class constructor,
and then this class constructor, right. So let's look at the slide and
you'll see what I'm talking about. So remember D and it's [inaudible]
calls the base class constructor. So it executes first and then the
derived class constructor executes. And you see that happening here. So these two represent
the declaration of D1. First, the base class
constructor executes and then the derived class
constructor executes. S is declaring D2. First, the base class constructor
executes then the derived class constructor executes. This is declaring D3. First the grandparent
constructor executes, then the parent constructor executes. And then the D3 constructor executes. And finally, that is
the declaration of D4. So those three calls appear on screen
just because of that declaration. That declaration ends up the three
constructor calls being-- being ran. So, there's nothing else. This reinforces how important it
is to get your constructors done and get them right because
they're going to execute and you want it would be good for you. OK. So that's-- and then we declare
this int variable and then we got into this loop, it's still executing
in linprog and sitting there with this prompt waiting
for me to enter something. OK. So there's that prompt,
Enter type number. So I'll first enter type number 0. And of course, that's the base
class and it calls F, G and H. And there's no surprise
that the base class versions of those are the ones that get called. So, let me do one for
derived class D1, right. And our call d1.F, d1.G, and d1.H.
But because G and H were overloaded, I got the derived versions of those. Because F was not overloaded,
I inherited the base version of F, and that's what got ran. That's directly observable and
the fact that D1 inherits from B, but we only overrode G and H, but it
still inherits F in the original version which is the F-- the
parent class version. So just continuing on,
I'll enter some more. If I enter say 3, that's going to
call d3.F, d3.G and d3.H. So I'm going to enter 3 here and we'll
see what happens. So again, notice-- remember that D3
only overwrote H. And so it inherited from its parent, D1, the implementation
of G and it inherited all the way from its grandparent implementation
of F. So B, F, D1 and G and D3, H were all triggered
by those three calls. It looked like D3 calling F,
D3 calling G, and D3 calling H. So I guess even more interesting if
I do D4 because remember the D4 is where we decided to overload
F in the grandparent-- in the grandchild generation but it was
not overloaded in the parent generation. And this shows you that that's
perfectly OK because here, we get the-- and when I call d4.F, I get the D4
version of F and I call it d4.G, I get D2 because we didn't overload
it in the grandchild generation. We did overload H and there it is. So I'm going to get-- I just want to
quit and go back to my slides here. So, this was our test client program
running on that framework we built. No surprises. It shows you how inheritance
and overloading works. I guess the technical term is overriding when you're doing base
class and derive class. So we override, that's
really kind of an overload. So this was running the test client and
you've seen it live so we don't need to belabor that or want to. Typically I have to react to my clicks. Wait a minute. So now, we're going to
do the dynamic case. So, we have-- And what we're going
to do is use pointers to objects. And this brings up a rule about
object-oriented programming and that is you can have a
pointer to the base class type. You can't-- You can instantiate
that pointer to anyone of the derived types you want to. So here, I have a pointer
to base class type. But I'm going to instantiate
it to a derived type, D1. First of all, that's a little
surprising, you can do that. You can't just do that
with two arbitrary types. You can have a pointer declare and
instantiate it with a float object. That won't work. But when one is a derive of
the other, you can do it. And so because this has happened, so
the question is, what is going to happen and what constructor is going to get
called here, and which version of F, G and H are going to get called, and
finally, what about the destructor? [ Pause ] Well, I'll just-- You can take this--
you make yourself a client program, may even have it in the examples but-- and try this and what you will discover
is that exactly as we have it written so far, even though this
is a-- I'm sorry. So what you will see is it exactly the
D1 version of all the signs get called. And that's not a surprise
because it's a D1 object. Remember the arrow notation is how
you select members with a pointer. That's equivalent to a D
reference followed by a dot. [ Pause ] So what you would really like is to-- [ Pause ] -- have the method chosen be invoked
at runtime by this operator 'new'. So, let me go back to this. If you run this, we need
to run that experiment. So let's do that. I think I have the full
program [inaudible]. Yeah. So what-- here's what
happens if you run that. This, even though you have a
derived client type object, you will output the base class version
of F and the base class for, you know, G, and the base class version of H.
So, it was legal what we did here, with the functions that get
called or the base class for versions of the functions. Now, derive-- might be
derived class data that those base class
functions are operating on, it would use the derived class theta. But probably this is not
what you want or expect. So how do you make this select
the derived class versions of the functions at runtime? So remember this is runtime. So the program is running, we
can instantiate the pointer at runtime and make this call. And so, it's a little bit intricate,
does a couple of things you have to do. One is you have to give the functions
that you want to be selected at runtime. You have to give them
a virtual modifier. That's all you do, is you just put
the word virtual in front on that. That's signals the compiler that
the function might be selected at runtime instead of compile time. Let me just say that this
declaration made the-- made these-- the versions, these functions
that are going to get called, that selected that at compile
time because of that declaration. Prefer that and select it at runtime. So we'll use that 'virtual'
keyword to all the functions. Let's say we did only by H, then what
would happen is this is the same code we have before. If we run that code, F and G
will be the same, but now for H, we'll get the D1 version
of H. And that's of course caused by this
'virtual' keyword. Of course, we had to recompile but--
And we'd put that 'virtual' keyword by the way in the base class. And by the way, sometime
we got to say it, you make the D structure
virtual as well. So you had to make all of your--
all your D structures virtual. So you have any virtual function,
make the D structure virtual. >> All of them? >> All of them. [ Pause ] >> Virtual also be used
on the race [phonetic]? >> Say that again. >> Virtual also be used on a race? >> It's only pertains
to functions, not that. [ Pause ] OK. There's also a notion
of dynamic cast. So you can cast, you can-- a dynamic
cast is a cast that happens at runtime. And you can use a dynamic cast
to push a pointer to base, site down to one of the derived types. And you need that only if-- you need that only if the derived
class has public stuff in it that is not in the base class. So if the derived class
has an extra function that the base class did not have. Then because your original pointer
is a pointer to the base class, that pointer will not know about those
function names unless you dynamically cast it down to that derived time. OK. So, how about showing
this table here and run a complexified [phonetic] is
just a little bit more just for purposes of showing you that I can have this-- I'm adding a B2 here and I'm going to have the D5 class
inherit from D3 and B2. So with -- that's D5
is going to illustrate. D5 is going to illustrate
multiple inheritance. So let's go to our-- So this is
implementing all these things. So for D5, I'm going to-- so these
are the two classes that we have and already implemented, B2 and D5. So B2 is just exactly
like we've been doing. B2 is going to have a function,
F2 and G2, and H2, different names and it's going to call
its F2, G2 and H2. And it's got a constructor
and destructor. So, D5, remember is going
to inherit from D3. So therefore, it's going to
inherit an F, G and an H function. And it's got to inherit from B-- derived from B2 and then therefore
inherit an F2, G2, and H2. And so, you can decide to overload
any set of those you want to. And we got to overload G, H, and G2, H2. Note that the constructor makes a
call first to the D3 constructor, and then to the D2 constructor,
those are its two parent classes. And finally, it lets you know that
its constructor has been called. So what I'm going to do for
function G is two things. We're going to call a B2 version of G2 and then let you know
that D5 G has been called. And I'm going to call B2 version
of H2 for H and let's you know that D5 version of H has been called. For G2, I'm going to just let you know that D5 version has been
called, same for H2. So, we have a similar client program. It's going to operate now on-- and that client program is
going to look like this. It's just going to start
off just like before. It's going to add a 'case' for D5. It's going to-- so, what it's going
to do is when you enter a number like if you enter 5,
it's going to create-- it's going to let that base class
pointer point to a D5 [inaudible]. After that switch statement, you're
going to call F, G and H. And if n is 5, we're going to do a dynamic
cast and call H2. Then we're going to delete the pointer. So, way that-- that code is
completely visible in the-- yeah. So that code is all in the-- [ Pause ] Actually, I'll set a
[inaudible] it's not in there. So I guess that is in demo2. So let's look in demo2.cpp. I think I've already told
you about my typing skills. So, this is demo2. Here's class B, D1, D2. No, that's not it. [ Pause ] I knew that one [inaudible] because I'm
not seeing virtual by the functions. [ Pause ] There we go. Let's call it EG1. I'll make sure that's clear when I
do your distributions [inaudible]. So here's your class B. Notice
that I got a virtual destructor on virtual H. Notice that in D1, I've got a virtual destructor
under virtual H. OK. So I don't have the-- So
this will be a good one. This is the same hierarchy
we had before, but with some of the
functions declared virtual. And the first thing it does-- OK. What this is going to show
you is that when you go through that first while loop that-- [ Pause ] So I'm going to run eg1.x here. Remember what it is declares
the variables like before. So, I'm going-- if I do 1 for example,
I'm still getting what we expected, 4. But now, I'm going to hit minus 1 and-- [ Pause ] You got exactly what
I showed you before. So what I need to do is find
the [inaudible] demonstration. Pausing again. OK. We're back to the real life here. What I figured out is that somehow
I didn't put the source code in the files yet. And so, that source code is
here and I'm going to copy and show you how you
can do this yourself, copy and paste this stuff
into a demonstration file. I guess I'll call it classes.cpp. So let me just-- e classes.cpp
and I'm going to-- and copy all those classes
into that file. So those are my classes and I'm
going to save that as classes.cpp. [ Pause ] Now, I'm going to make a static
program and a dynamic program. Both of them are class program. So static is all that code. [ Pause ] Static.cpp. I want to paste all that stuff in there. I'm not sure if it includes
the right stuff. Yeah. We're including our classes.cpp. I see a [inaudible] the names
in here not that that matters. So now I'm going to do that. I'm going to do dynamic.cpp. That's this code. [ Pause ] And there we have it. If I'm the luckiest person in
the world, this will compile. So I'm going to go ahead and compile. What was the first one called, static? [ Pause ] And dynamic. [ Pause ] So static.x is just the same
program we were running before with everything declared like
[inaudible] the variables. Except we're running just slightly
enhanced hierarchy that's got D5 and B2 and it's got the second base class. And the derived class D5 which
derives off of D1 and B2. No. Is that right? No, D3 and B2. And so, just look at F. When
you declare that D5 object, it's first base class
constructor gets called. Then its parent class on that
side of the family gets called. And then its con-- then its--
well, I guess it inherits from B3. So that would have been
its grandparent constructor and then its parent constructor
and then its other parent-- other grandparent constructor. And then finally, its own constructor. So B, D1, D3, B2 and
D5 all got called by-- [ Pause ] -- that declaration out
of those constructors. And you can see why actually
if you go to D5, get public inherits from D3 and B2. So D5 in its initialization calls the
D3 constructor and the B2 constructor. But the D3 constructor will call
a D1 constructor in its init list. The D1 constructor will call its base
class constructor, its init list. So the call to D3 initiates a call to D1 which in turn initiates
a call to B constructor. The B constructor then
executes the D1 constructor, executes the D3 constructor, executes. And all of that is because
of that right there. Next, the B2 constructor is
called and that's a base class. So it executes itself and then
finally the D5 constructor executes which is what displays that. So that's-- that's final
thing that got displayed. And so, we could look
at-- let's just look at 5. So, what we did is call D5. [ Pause ] I guess I'm not clear
what that code has been. What did we do here? We're running static. OK. So if I hit 5, call D5-- yeah. OK. So it should call D5
on F, G, H an F2, G2, H2. Did that really happened here? Yeah, I'm sorry. So if you go back and look at
how we define those functions, those are the results of all
three of those calls there. [ Pause ] And then those are all the
variables going out of scope. And if you look at dynamic-- [ Pause ] What's going to happen here is we're
going to create object on the fly with operator 'new' and
then call these functions. And so, let's start off
with-- let's start off with 3. OK. So the 3 was-- it's going to
create a D3 object with operator 'new'. So that results in a call to B
constructor, the D1 constructor, and finally, the D3 constructor. So its grandparent, parent and its own
constructor get called in that order. Then it calls F, G, and H. So d3 dot-- or it calls F, G, and H
with the arrow operator. And because F was not virtual, we
get the base class version of F. In fact F wasn't even overwritten. And so, you're going to
get the base class version of F no matter what, doesn't overwrite. G is overwritten in D3 but
it's not declared virtual, so we still get the base class
version of G. H was declared virtual, and so we get the D3 version
of H with [inaudible]. So this is a call to D-- little
d3.F, little, little d3.G and little d3.H except with arrow
operator because it's a pointer to dynamically created object. And then that's the destructor. So let me show you the
code in the client program that is producing that output. We're now in the dynamic program. So, remember what we did is we gave it a
3 which created a new object of type D3. And of course, what that did is
call all those constructors, right. And then we call the three functions
with arrow operative from the pointer. We can't ignore that
because we did enter 5. And then we delete the pointer. And that's the cause-- is that delete
that calls all the destructors. So this is a little complicated to work
around in, completely laid out for you. Copy the code with your mouse into the
files compiling with your C3330 macro. Everything will work fine. You get-- You get running-- programmable
programs, executable programs. Print the source code out if you want
to, so you can look at the source code. You need to be able both look
at class hierarchy and see which functions are defined
where and which one is a virtual and make sure you understand when you-- especially in the dynamic case when
you run one of the derived class when you select one on the derived
classes while the functions are called or getting called, makes you
understand while that's happening. And there's going to be a combination
of what's the class hierarchy, which functions got overwritten and
which functions declare virtual. And the bottom line is the function is
virtual then it's most derived one will be called when you make a call from the functions not declared virtual
then the base class version will get called when you make a call. [ Pause ] Now, we didn't. I wanted to talk about the destructors. Let me just show you the--
I believe we got 6, right. I guess 6 was out of bounce. Was it 5? [ Pause ] Yeah. So that created
an object of type D5. So D5 in here is from both
B, for D1, for D3, and B2. So that's the one with
multiple inheritances. So the contractor when operator
'new' is called, type D5, all of these contractors get called
by that creation of that object. Then we call F and G, and H and
that results in all of that. You can look at the source
code and [inaudible]. And then we delete that object and then
do you see that destructors get called in the reverse order that the
[inaudible] was getting called when object goes out of scope. So it's a D5 object. So the D5 destructor gets called
its inherits from base class B2 so that destructor gets
called and then its parent on the left side gets
called, destructor. And then its grandparents
destructor on the left side. Its great grandparents
destructor on the left side. [ Pause ] Sorry. OK. So let's just look at the
slides for this-- where we are. That's the source code slide. This Running Static.x and Running
Dynamic.x is some commentary of how those things run. Showing you the destructor
calls and [inaudible]. You can duplicate this as much as
simply running your code and [inaudible] for the dynamic with some commentary. So if I'll hit 0, this is object type B,
object type D1, D2, D3, D4, and D5, and, you know, you can see for example in
D4, this is the object coming to life. If I call operator 'new', these are the
calls of the three functions F, G and H, with the arrow operator and this
is the object going out of scope of series of destructor calls. So operator 'new' creates
the object then we call F, G, and H. And then we delete the object for
each one of the types in the framework. So back in the slide index. These two slides I didn't
talk about yet. I'll skip from this dynamic Cast
of 2, the Source Code slide. I want to go back and talk
about Abstract Classes. An abstract class, we
mentioned earlier something about the JAVA relationship
of C++ to JAVA. When you-- In JAVA, inherent an
interface, that's the same in C++ as inheriting a-- an abstract class. An abstract class is one that has-- at
least one of its functions set equal to null which is kind of
a just a trick really. But what that does is
ensure that it's not legal to make an object of that type. So, because one of its functions
is null, you will get a compiler or if you try to create
an object of that type. So that's why it's called abstract. It's used only to define the interface. You could define some progress of it
too if there are variables that need to be introduced into the hierarchy
or introduce into the framework. At this high level, you can put them in. But mainly, the purpose of an abstract
class is to define the interface. They're all classes in this
hierarchy, must implement. And in fact, any time of set to null
has to be overwritten and implemented in any derived class because that null, if it's left null is
not going to compile. [ Pause ] On other hand, it's-- you know, you can
have non-zero functions like solid type. And if you have one there
that's not null, you should give it an implementation. So one-- another thing you can do
in abstract type is implement some of the functions assuming
it's appropriate to do so. And when you want to implement
and use to have a not nulled out and you can provide implementation. What you don't want to is
have them not set to null and also not provide an implementation. That will get you in trouble. So if you don't want
to implement something in the abstract class, set it to zero. Another point is the abstract
class has no constructor and the reason is you can't make
an object out of an abstract class. So there's no point to
having a constructor. And it's just wasted code. That cannot [inaudible] be
able to think that it's code that would execute [inaudible]. OK. And then there's the source
code we'd already looked at. So that is the last-- the fourth
and final chapter on Classes. C++ Classes Part 4: Polymorphism
and Object Oriented Programming. I'm going to pause this. I'm actually going to stop
this and start up a new video where I introduce the Sun Pass project
which is going to be an exercise in developing a framework and then running a client
program for that framework. That's going to be your first project. So I will now--