>> All right. So next we're going to talk
a little bit about the -- chapter nine, which is the third
chapter on C Plus Plus classes. Now, just to kind of recap in the first
two -- in the first chapter on classes, we just talk about what a class is, and
public and private parts of a class. We talk about the concepts, like
member function and member variable. We talk about constructors
and their use. In the second chapter, we talk in a
lot more detail about constructors and destructors, assignment
operator, copy constructor. And we talk about the general issues
surrounding dynamically allocated resources to a class. There's two more parts that
encompass the whole object-oriented programming paradigm. And probably the most important
one to date is this chapter nine, where we talk about inheritance,
which is a critical piece of object-oriented programming. So again because software
engineering is so important, we always come back to,
what are the goals here? Well, our goal in -- is
the concept of reuse. It's always good to be
able to reuse things. And your first real programming
assignment, stats, what you did is create a function
called mean and a function called median that arguably could be reused in
a number of different programs. And you also wrote a test
[inaudible] to help you make sure that they were actually
working correctly. But clearly the mean and
median functions could be used in other programs, whenever
you need the mean or some part of the median of some data. So that is a way code gets reused,
and that's the modularity way. Another way code can get
reused is through classes. If you build a class,
then that class can be put in a library, and you can use it. The class called product that you're
developing for a current assignment, can be -- can be used
and, as part of a system of inventory control, for example. And the inventory would access things
by product, by product objects. And so reusing that -- the code
to developing for class product -- over and over again, every time somebody
uses the product for something different in the general inventory
management system. So classes also falls into that
realm of object-based programming, which is a way of encapsulating data
and the functionality that is tailored for that data in a [inaudible] class. Another way to reuse code is
through class inheritance. And that's what we're
going to talk about today. Another thing that's important
is interface consistency. So interface consistency can be
handled through function overloading. So if you always want,
for example, the mean -- if you want mean to be able to
calculate the average or mean of, an array of floating point numbers
instead of an array of integers, in C Plus Plus, you're allowed
to call that by the same name but give it a different signature. So that in a program, you can
call mean of pass an array, and if that array is an array of floats, then the mean for floats
will get called. If that array is an array of ints,
the main for ints will get called. And the client programmer doesn't have
to worry about the distinction there, because they have the same name. That's overloading. There's another way to -- so
that's consistency of interface. The interface is the name
of that function -- mean. Another way to enforce more elaborate
interface and sort, an entire API, is through class inheritance. So class inheritance comes up in both
code reuse and in interface consistency, and so what the heck is this? All right. Class inheritance, you have a
base class -- that's just a class. And you have a derived class, which
is inherited from that base class. We think of the base class as a
parent, the derived class as a child. So you have a parent/child class system. You can have a class that is
a child of the child, as well. So in a sense, the original
base class is now a grandparent, and we don't really keep using that kind
of terminology, but we do say ancestor and descendant in a hierarchy
of classes. So what I'm going to develop here
is a little example, as we go. But let's see -- so all member of the base class are
inherited by the derived class. So whenever base class has
something, like a member function, it's inherited by the derived
class or member variable. The base class member functions
can either be inherited as-is, or they can be overridden --
that is to say, overloaded -- with a different implementation
for the derived class. So in the narrative -- which you
could get by clicking Narrative or clicking here, you'll
see that we've tried to get sample implementations
of solid and box, here. But let's look at these classes. So here is a class called Solid. And I'm thinking geometry here. Notice that it has a public sector,
and at this point, no private sector. It's got a SolidType
method and a Volume method. SolidType returns a character string. Volume returns a float. [Inaudible] This effectively establishes
an interface for solid objects. So SolidType essentially
returns the name of that particular kind of solid object. And the Volume returns the volume
of the particular solid object. But when you just say a solid
object, there's no really good way to describe it in any more detail. But clearly, for any of those
things, we'd like to know what kind of solid it is and what its volume is. So as an example, we know what
a Box is, if we derive Box from Solid, we would do it this way. So we have a class Box and the -- in
red, you see a colon, public, Solid. So that is indicating that the
class Box is publically inherited from the base class Solid. So Box can have a constructor,
and you'll note that it had an L, a W
and an H parameter. And each of them are floats. And it would make sense that
you would have a length, width and height associated with those. In the private sector of a box, we've
got length, width and height stored. In the public sector,
we have the SolidType and how would we -- how would we define? We could define that by saying -- by
returning the string quote box unquote. And finally we could
implement Volume for a solid by returning length times
width times height. Now, those are implemented
in the narrative. So for example, Solid:SolidType
returns just the word generic, because we don't know
what kind of solid it is. And for lack of anything better,
we'll return 0 for its volume. For Box, we implement the
constructor [inaudible] way with an initialization list. We implement Volume by returning
length times width times height. Notice that's a calculation
done at the moment of return. We never calculated the
Volume and stored it anywhere. But we did store length, width and
height, so we can get the volume easily. And the SolidType is quote box unquote. All right. So here is a synopsis of what
we did, a review of what we did. We got the class Solid, got class
Box, showing the inheritance. And here are all the
implementations on the right-hand side. Now, the methods can be
inherited or overridden. So the prototype for a method is
always inherited by the child class. But the implementation
is inherited or not. And that's your choice, as the designer. So how do you -- how do you tell
the compiler whether you're okay with the base class implementation
or you want to write a new one? You tell the compiler that by repeating
a prototype in the derived class. So if you want to override the volume
function, as of course we do in the case of a box, and we repeat the prototype
in class Box, and that obliges us to provide an implementation
for class Box, for that member function column, Volume. Now, notice that we're using
the scope resolution operator, when we go to implement
functions, right? So for example, suppose we had... I forgot to mention,
the Cube class here. So we're going to have our class
Cube that inherits from Box. So it's going to have a constructor
that will take only one argument. Its Volume function is going to be
the same as -- its volume as a -- as a box and therefore, we
do not need to override it. We can inherit the implementation. And so we comment that out,
knowing that it's still in the API, but it's going to use the base class or its parent class implementation
for that function. On the other hand, the SolidType
is no longer just a plain box. It's now a cube, so we
are going to override it. So we repeat the prototype
of that function, because we want to override it. We do not repeat the prototype for
that function, because we're satisfied with the way it inherits
from the parent class. So in other words, the Volume of a cube
is its length times its width times its height. Also notice that a Cube constructor is
calling the parent class constructor in its initialization list. So the parent class is Box
and Box takes three arguments of length, width and height. What -- Cube only takes one --
the length of one of its sides, and those are all the same. And that's the definition of a cube. So we call Box constructor,
which, that side for argument, in each of the three length,
width and height locations. And finally here's the override
of the SolidType method for Cube. Now, we've used, as I was about to say, we've used this [inaudible] resolution
operator in our function implementations to indicate the class to which
that function [inaudible] belongs. You can also use it in other ways to
invoke a function, to call a function. So for example, suppose I invented
a method in Cube called ShowLineage. And the lineage of a Cube
would be calling SolidType, and then it would call Box::SolidType. But it would call Solid::SolidType. When I call SolidType for
Cube, it would output Cube. When I call SolidType
for Box, it outputs Box. When I call SolidType for
Solid, it outputs generic. By the way, this is all compilable
in code, so you always cut -- copy, rather and paste code out of
the sides or the narrative directly into a file open with Emacs. It's just a matter of it cleaning up,
and you'll get code you can compile and run and see how our examples
actually work on a computer. So the derived class needs
its own constructors. Now, remember, the derived class
inherits stuff from base class, but it can't really inherit a
constructor from base class, because the constructor has
the same name as the class. Right? So the constructor for the
derived class has to be defined, protoyped and implemented
in the derived class. And there's some special
rules about that. The constructors for the derived class
should call a parent class constructor. It should call it in the
initialization list, not in the body. Notice that the calling order is
bottom up, so if A has a child class B, which has a child class C, the C
constructor will call the B constructor. The B class constructor, and the B
class constructor will call the A class constructor. So they get called in
the order C constructor, B constructor, A constructor. But because they're in
the initialization list, they get executed in the opposite order. So I'm going to go to the board
and try to illustrate that. I need to move the mic -- the camera. So we're going to have a class A,
and we're going to have a class B, which publically inherits from A. And I'm going to have a class C,
which publically inherits from B. So if you were drawing pictures,
you have a class C, and -- I'm sorry, a class A and B. We draw an
arrow up, to indicate that B inherits from A. And a class C, draw an
arrow up, to indicate C inherits from B. So the C constructor -- I'm going to leave that picture there,
to indicate what I've written out here. So the C -- let's say the default
constructor was C, which would be this, and it's -- in an int list,
will call the B constructor. We might do some other stuff, too,
and then you'll have its body, which may not have anything in it. The B constructor will call the
A -- its parent constructor -- parent class constructor, which would
be A and possibly not do anything else. And of course the A constructor
is there too. So notice that if you create a C object
-- you'll do it with a C constructor -- it will call a B constructor. Well, the B constructor will go up
here and try to construct this thing as a B object and realize, "Oh,
I've got to call the A constructor." So now you get into that A constructor, and the A constructor
looks something like this. And it, you know, might do stuff inside of its braces there,
as might the other two. So the question is when does this
stuff get executed, in the braces? Well, C, and it's an int
list, calls the B constructor. B, and it's an int list,
calls the A constructor. We haven't gotten inside of
these braces yet, at all. So A, up here, gets called,
right, its constructors. So this code in the A
constructor here gets executed. Okay, when that call returns, the B
constructor's body can then execute. And when that call returns, the C
constructor can execute its volume. So C constructor calls
the B constructor, which calls the A constructor, when
then executes and returns to here, which then allows B's constructor
to execute, which returns into here, which allows the C constructor
to execute. So the call is in the order of C, B, A.
But the execution is in the order of A, B, C; from parent to
derived, down the hierarchy. So we'll check out a code
example in the narrative, here. So notice that I've got
class base and class derived. Class base -- all these constructors
and destructors do is tell you on screen that they've been called. So this is just for demonstration
purposes. So this tells you the base
constructor has been called. The base C constructor's been called. And Derived and Derived. And in main, I'll just
declare a base class object and a derived class object
and then do nothing else. And when I compile it, I'll
get a warning, saying, "Hey, you didn't really use those variables." But that's okay. So... I believe I have that code in here. Well, it's a little more
elaborate, I guess. Yeah. That's more elaborate. So let me just do -- let me just... just do that example. What I'm going to show
you is how this can work. So I will copy this code right
out of the lecture slide. And stuff it into an Emacs file,
Emacs open file called EG.cpp. Now, let me copy function main... ...stick it into this same Emacs file. So in this file, I've got the
class base and class derived. I've got all that in there. I'm going to need a [inaudible],
an iostream... So I'm going to try to compile that. And it compiled. So now I'm going to run it. Surprised it didn't give me a
warning about not using my variables. But what you see what happened is
-- let me remind you of the code. So in function main, I
declared a base object and a derived object, and that's it. That's all I did -- declare two objects. Period. So the program comes
to life, creates a base object, creates a derived object
and then terminates. Now, if you run that program, you
can see the constructors happening. So there's the base class
constructor being executed. The base class constructor
gets executed again, because the derived class
is being built. Remember, the derived class constructor
calls the base class constructor, which then executes and then, the
derived class constructor executes. So at this point right there, the two objects have been created
-- the base and the derived. Now there's nothing left,
and so they go out of scope. The first one to go out of scope
will be the derived, and it -- so its destructor will get called and then its parent class
destructor will get called. And then finally, the destructor
for the base class will get called. So what that illustrates
is that the destructors -- you don't make explicit
calls to destructors. But when an object of a
derived type goes out of scope, the destructor for it gets called and
so does the destructor for its parent and so on, all the way up
the chain of inheritance. And the destructors get called in the
reverse order from the constructors. So the destructors get called
from the derived to the parent, whereas the constructors get
executed in the opposite order. So that's what that illustrates. Now, remember, variables
get inherited, as well. Then the question is, when you inherit
a variable as a derived class -- well, Cube inherits length,
width and height from Box. The question is, can Cube directly
access those inherited variables? And the answer is -- in the way
we did it, the answer is no. Because the inheritance is public,
but the variables are declared private in class Box, and so they can't
be accessed by a child class. Now, there's a way around that. There's two ways around that. The preferred way is to simply provide
public method access to those variables, so the derived class can use it,
just like a client could use it. Or another way is to use, instead of
private, a slightly less stringent form of protection, called protected. And what protected does is still
shield things from interference from client programs but allow
derived classes to use the variables. So that's this keyword, protected. So if we change Box to
protected, from private -- we had private there before -- then that's going to allow Cube to
access length, width and height. Now, an alternative would
be, in product class Box, have a method called SetDimensions, so
we could keep the dimensions private, which would not allow Cube
to access them directly, but setDimensions would
be a public method, and Cube therefore could
call SetDimensions. So Cube could implement -- set
aside by calling SetDimensions. So we've talked about public, and
we've just mentioned protected, and we talked about private. So that's three categories
of protection. And we talked about public inheritance. It turns out there's
protected inheritance and private inheritance, as well. And so in this -- I like to
think of three questions. What are the access rights of the
derived class to base class stuff? What are the access rights of the
derived class to pass to its children? And finally, what are the access rights
of clients accessing the derived class? And so you've got three levels of
protection -- I'm sorry; yeah -- and three levels of inheritance
and three questions to answer, which gives you a total
of 27 possible answers. Please have that memorized by tomorrow. Of course, I'm joking. In fact, this is extraordinarily
complicated -- extraordinarily complicated,
hard to remember and -- I mean, some of it's easy to remember. If it's public, you can access it. If it's private, you can't. What's hard to remember is the
interplay between that sort of halfway zone called protected. And one of the questions
that will appear on our discussion forum this week is --
noticed in Strustrep's [phonetic] talk that -- noticed in Strustrep's
talk, that he had indicated that perhaps the word
"protected" might be deprecated in future versions of C Plus Plus. And there are some software
engineering reasons for that. But one reason would be just to
reduce the complexity of things like we're just looking
at here on the screen. We've got 27 different possible -- 27 different possible
answers to some questions that have to do with access rights. And that's pretty tough. Another interesting thing that you have
in C Plus Plus, and you don't have, for example, in Java -- and Java always
-- Java, people like to say, "Hell, this is one of the benefits of Java,
that we are not allowed to do stuff." But in multiple, and that
is multiple inheritance -- so this is an extremely interesting
phenomenon, where in C Plus Plus, a class can have two different parent
classes and inherit from both of them. And there's obvious things. I mean, the two different
parent classes -- each have a method of the same
name -- that's not smart, right? So you don't want that to happen. And you don't want them to
have the same names for data. But beyond that, it works pretty
well, and you might ask, "Well, why would you want to do that?" And here, I'm going to
give you an example. So we have a class called Material. Okay? Material can have a constructor, and a constructor might record a
density, a strength and a cost, in some sense, for that material -- a name, Visqueen; a density,
a strength and a cost. Those are methods that tell the client
what the density, strength and cost of that material are and store
the data in the private sector, so you can see how this
class would get assembled. And then we could have a
class called Cardboard. It would inherit from Material. And it would have a --
probably Material up here -- a generic material, would have default
density, strength and cost 0, probably. But cardboard would have a
certain density, a certain strength and a certain cost, per square foot. Cardboard's constructor would call a
Material constructor in its init list. Okay. So there's Cardboard. But now here is the [inaudible]
to the example. We're going to have a
class called Carton. Then that Carton is going to
inherit publically from Box. We've already said what Box is. And it's going to inherit
publically from Cardboard. So it's going to be what? A cardboard box. So it will have a constructor that
will give its length, width and height and its density, strength and cost. The constructor will call its -- call
constructors from each of its parents. So we'll call the Box constructor with
the length, width and height arguments. And the Cardboard constructor with the
density, strength and cost arguments. And that will make you a box --
I'm sorry -- a carton object. Bam. And that's -- you can see
that that's a very good idea, because Box has a certain conceptual
nature, and material has a certain, very different conceptual nature. And you can combine the two
and have a richer concept. You'll have some experience
doing this in your first project, where we will talk about trucks. And there will be a truck
called a tanker truck. And the tanker truck will inherit
from a base class called Truck -- well, actually, a base
class called Vehicle and also a base class called
Cylinder which is, you know, a geometric object kind of class. So a vehicle with a cylinder
will be called a tanker truck. And so on. And you know, so that [inaudible] tanker
truck but attributes of a vehicle -- so it will have a DOT
number and all that -- and it will have also the attributes of
a tank or cylinder, where it can put -- store liquids and haul
them on his truck. By the way, while we're waiting
for the web to come alive here, another place where we will run into
multiple inheritance is in talking about the actual input/output
classes in iostream. Global inheritance is used in
constructing the i/o system in C Plus Plus, in a
very interesting way. So let me take a break here.