>> OK. So, today we're going to talk
about classes again using the lecture and that's Classes Part
2, that's Chapter six. Here's our first slide. So, the first thing we'll talk about
is what happens if you have classes with external resources allocate? So, a class constructor,
remember, prepares a new object by creating an environment in
which the number functions operate. We talked about class constructors
in the previous chapter. So, in this environment requires
some sort of external resource and that resource has
to be handled carefully. It has to be allocated
and deallocated typically. So, here's an example and
this is an example we're going to build on-- for much of this chapter. And that's example I'm calling IntArray. And as you could kind of guess already, it's intended to be an
integer array class. So, it's an object type IntArray would
behave like an integer array on an array of integers, but perhaps in a
little more user friendly fashion. So, we're going to have a class, the name of that class
is going to be IntArray. It's got a constructor. Notice that this constructor has
two arguments, two parameters. One is a size parameter and one is an
initial, an initial value parameter. And notice that those have
default values as well, so that if we don't specify size,
the size equal to 10, will happen. And if we don't specify
initial value for the elements-- [ Pause ] I'm not sure what that
little message was about. Value for the elements in the array, if we don't specify an initial value
the array elements will be automatically initialized to zero, that's
what that's intended to convey. Now, notice in the private
sector of this class, we have a variable size underscore
which is of type size underscore T, the type for size in nanometers
say for those environment. And we also have a pointer to
the integer and we're going to call that data underscore. I have written the data member
variable in red to indicate perhaps, give it some special attention. And the point being that a pointer to type int isn't a containing
object for anything. So in order to actually store
integers there, we're going to have to associate memory with that pointer. So, the way you might try to
do that is with a constructor. So, here's-- So, we define the class, of
course, there's other stuff we're going to come back to later
here, but the constructor, I'm giving a possible implementation
of that constructor, it's IntArray, double column, IntArray is telling the
compiler and anyone else that cares that this is a member
function of the class IntArray. That's what that first
IntArray double column is. And the name of that
function is also IntArray which tells you that's a constructor. And we have a size argument
and an value argument. Now, we initialize the size
with the incoming parameter in the initialization list. Now, you can't really do that,
you can't really put lines of code up here and in the net list. So, creating space associated with that
data underscore pointer requires doing something in the body
of the constructor. And what we'll do is allocate
size worth of int space to that data underscore point. And then we initialize all those
values with the initial value. [ Pause ] So, we've used both the size and
the ivalue to create an array of the integers over
several of that size and give them all the
prescribed initial value. So, that is an example of a constructor that actually allocates
resources to the object. So, this object, after it's
contracted owns, if you like, an array, a dynamically allocated
array of integer data. Now, we don't have any way of
manipulating that data at the moment, so obviously we're going to have
fill in some functionality here but this is just attacking the question of how you would create
all this array objects. So, just to review, if
you had client code, this is code that is
using the IntArray class. You could write lines of code that
looks something like this on the left. You can declare an IntArray A 1000
comma minus one, IntArray B with 100, and IntArray C with no arguments. And what would those mean? IntArray A would be 1000 integers,
each initialized to minus one. IntArray B would be 100 integers
initialized with a default value of zero that we gave earlier on. And IntArray C would be
10 integers initialized-- each initialized with the
default values of zero. Now, before we go into how building
usable functionality into this class, let's realize that I'm
going to back it up now. Let's realize that we now have a pointer
data that has been allocated space by operator new when the
object comes to life. When the object is built, it gets
space allocated by operator new. We need to make sure right upfront
here that when that object goes out of scope ceases to exist,
that allocation is given back. And that is done through
something called a destructor. So, we have a constructor
and a destructor. A destructor is called
when the object goes away. So, when it goes out of
scope ceases to exist. We saw last week that there can be
more than one constructor for a class. It turns out you'd never need
more than one destructor. There's only one destructor for a class
and its name will always be tilde, the name of the class and it
will always have no arguments. And by the way, also no return
type, just like a constructor. That tilde comes a little
bit chronologic, that tends to be not chronologic. So, you have the class and then
you don't have the class anymore. So, tilde, whatever the class
name is, is the class destructor. And it's possible but extremely
rare that you would call that destructor explicitly. It is called implicitly, automatically
when the object goes out of scope. And, we've already-- you already kind
of know what the destructor needs to do. And namely, it needs to call an
operator delete with brackets on that data pointer which was
previously allocated by a constructor. So now that we have a constructor
and a destructor, when the object A, for example, here goes into scope. it's-- once it comes to
life, it will own an array of 1000 integers each
initialized to minus one. When you-- when that goes out of
scope which is right at that brace, and that object goes out of scope, that
object goes out of scope at have brace and the destructor will
be called automatically, so the array will be deleted
right there at that brace. So notice that we are-- in our
constructor and destructor, we're managing the memory
for this IntArray class. But the client code uses it as if it
were just a normal variable, right? It can just clear an IntArray, use it
for a while and let it go out of scope which kind of gives you a hint
that these things are going to be extremely useful because now our
client program can make an IntArray on the file or-- and not worry
about managing the memory. [ Pause ] OK. We've talked about constructors of
which we're going to have several kinds and we've talked about the
destructor which there's only one. Anytime you have constructors that
are allocating resources to an object, you want your destructor to make sure
that those resources get deallocated. The ones we're able to deal
with in this class are memory but there could be other things too. It could be separate
threads of execution, putting in a multiprogramming
environment. There could be all kinds of things. [Inaudible] parameter. So, it's a resource that gets allocated
and the destructor needs to deallocate that resource, general principle. OK. So, now what would happen if we
want to copy some IntArray object to some other IntArray
object for example? Well, there are common situations when objects are copied
and that copy is implicit. When you pass an object by value to a
function as a parameter, as an argument, remember, what happens is the-- a copy is made for that
argument in the calling process. A copy is made and handed to
the function to operate on. Similarly, when a function
returns a value, a copy is made of that return value
and given back to the calling process. So those two situations which are
very common in programming and in which copies of objects are
made and it's done almost without you even having
to think about it. But, how do you copy an object
which has resources assigned to it? You've got to make sure you copy
the resources appropriately. And you can't rely on
compiler to figure that out because a compiler is
not designing the class. You, a human being is
designing the class. And so you, the human being
has to make a decision on what it means to copy these objects. So, another situation where objects
are copied is in declaring an object and telling the compiler
you wanted to be equal to the previously existing object. Well that's, you know, the third way
we might need to be able to make copies and we do that with something
called the copy constructor, I would manually do that,
something called a copy constructor. So we talked about the constructor
of which we can have more than one. In fact, that particular
constructor with its default values, notice that that actually represents
three different constructors. The parameter list constructor, a
constructor with only one argument of size parameter and a constructor
with two arguments assigned at an initial value parameter. And the destructor we talked about. So now, the copy constructor. It's a constructor, so it's
going to have the name. Its name will be the name of the
class and it'll have no return time. The copy constructor is distinguished from all the other constructors
by the parameter list. And the parameter list is
always a constant reference to the time being constructed,
being copied. So let's get this straight
what this means here. This object A, notice that we are
passing it to the copied and talk about reference and that reference
is not allowed to change the object. And so in effect, we're passing it in
by reference because after all, we don't yet even know how to make copies, right? So we're only passing a reference
to that object but we're saying but don't change it,
don't change the object. And what, what the copy constructor
is supposed to do is it's intended to be called as a new object is
being created and it's supposed to build a new object to be
exactly like the object A is-- that-- that is its argument. And so we-- our data will be-- well first of all and on the
net list you set the size, the new object to be the size of the
incoming object A and then we let A, that would be an allocation of an array. It's the same size as the one
for the preexisting A object. And then we just copy the data in
the array from the array that A owns into the new array at the
newly being built object owns. And so what we've done is ensure
that the two objects, the new object and the old one have the same size. They both have memory allocated
to them separately, right? So they each own a separate
little stock of memory. And finally, we make sure
that the contents of a memory of the new object is the same as
the contents of the old object. And so that's what we--
that's what we mean by how do you copy an IntArray object. [ Pause ] So, we haven't even got to
class functionality yet. What we're doing is taking
care of the housekeeping things that make classes and objects usable. So they have to know how to initialize
themselves, they have to know how to destroy themselves and they
have to know how to copy themselves if you pass something
about value around. Now there's one more way objects
need to get copied and that's through the assignment operator. We're going to get to
that in just a minute. That's very much like a copying in
driver with some complicating caveats. And one of the things
that we're going to need in the assignment operator
is an object needs to be able to refer to itself by [inaudible]. And so whenever you have an
object it and you're in an object, the keyword "this" is intended to
mean the address of that object. In other words, my address is this. And of course, we call it
this in the resource code because it won't really have
an address until runtime. But that this will then mean that
address that it's given at runtime. So we never have to declare this but it's always there
and it's always private. Now a unit access to the location of the
object and sometimes the object itself. So, the context and scope of this code
is we're implementing member thoughts of the class. And when we do that, the keyword this
is the address of the current object or if you like upon or
to the current object. Might be. You can take out either
way, they made the same thing. Just think of it is your--
the object's address, the object in which you're working
is that's that object's address. And we can reference that pointer,
star this and that's the actual object. So this is the address of the
or a pointer to the object. And therefore, star this
is the object itself. Now that's important because we're going
to need it in the assignment operator. And as you might imagine, A equal B
is something client programs are going to want to do, for any
kind of object, IntArrays. We would like to have an-- declare IntArray A, let's say 100 comma
minus one like the one we had before. And we'd like to then
say B equals A and know that B is an exact copy of the object A. [ Pause ] Now, there's-- the first I'll note is that the assignment operator has
so-called operator function syntax. In other words, you can call the
operator syntax which is A equal B but it's perfectly OK to use
operator function syntax. And what that-- if you look
at that, I'm emphasizing here that the operator equals the assignment
operator is a member of the class and they were calling
that member function. It's a member of the class-- of
course if A and B are the same type, they each have an assignment
operator but when I do A equals B, I'm calling A is assignment operator. And basically what its saying is I want
A as assignment operator to remake A in the image of B, very
similar to copy constructor. The difference is when you call
the copy constructor, it's implicit and it's being called
on a brand new object. So it's creating a new object. When you call the assignment operator,
both of the objects already exist. So in particular, the A object,
one that's being rebuilt, if you like, already has values. And so you got to worry about
getting rid of the old values. And in particular, getting
rid of the old-- that allocated resources
for A and then rebuilt. And then-- and only then rebuilding
A to look like the object B. [ Pause ] Wait. I don't know if you ever tried
it but it's perfectly legitimate to write A equals B equals C in code
and it turns out that works fine. And the reason it works is the way
assignment operator associates is right to left. So when I make that call highlighted
in blue, first, the first-- the first assignment operator
to be called is that one. In other words, this one
inside the parenthesis. And what that will do is
recreate B to look like C but then it must have a return value. What it does is return reference to B
so that A can now be recreated to look like the new B. And that's--
so that's another little twist that assignment operators have. They have to have a return value. So here is a prototype of an assignment
operator of an IntArray class. So let's look at that for a moment. We have the name of that operator. The name of the operator
is operator equals. That whole string is the name
of this operator function. I noticed that we prototyped
it as if were a function. So we have open paren, the argument,
the parameter list and then close paren. And also note that there's only
one thing in the parameter list and that is an object of
the same type as the class, pass down by constant reference. So the parameter list for the
assignment operator is the same as the parameter list
for the [inaudible]. One different thing is that it has
to return something and it's going to return a reference to
something of type IntArray and that specifically is going
to be a reference to itself after it has rebuilt
itself to look like B. [ Pause ] All right, so I'm going back. I think three of attempts here. Successively better until it
get one that actually works. Here is the first attempt. Name it what we do with
the copy constructor. Let the size be B dot size. Data B new int bracket
size and then copy the data from B or in over into my data. That's basically what we do
with the copy constructor. And now we have to return our self
reference and so we return star this. And here's a problem, what happens
to memory already allocated to A. In other words, those A had been-- had come to life originally
as an array of 100 integers. Well, as soon as I make that call,
the thing that data underscore used to point to has been orphaned. It's not the allocated memory but
that act right there loses its address and where it can never get back. So that's that memory that got
allocated and then orphaned, we can't get back to it again. And it's just going to flow out there
forever as long as a program runs. This is called the memory leak. OK, so we can't fix that. And the way we fix it is but first it's
leading a data associated with that. So we delete data underscore and
then go through what we did before. OK, that should work, right? Except what if somebody
wrote A equals A? Now, that might seem like a silly thing
to do but it could happen accidentally because A and B-- I mean, well B could
be some renamed version of A. Right. So B could be a new name for
A. Never mind how we could do that but it's possible. So B could an alias for A even
though that you don't know that when you write the code. So, what-- the point is,
you don't want A equals A to be an illegal thing to write in code. You don't want self-assignment to be
illegal, but what if you did that. With this code right here,
the first thing we're going to do is delete the memory allocated to
A and then we would try to make a copy of that memory that we just deleted. That wouldn't work either, well
that would get all kinds of errors because as you delete memory, you're
not supposed to access it anymore. OK. So, the fourth-- the
third attempt now works. So what you do is you just simply
protect against self-assignment, in other words, if the set--
if B is really an alias for A, there's nothing to do, right? They're the same object. So why do any work. And so, you'd make a protective
mechanism if this, remember, that's A's address, is not equal
to B's address then you're OK. Being at the assigned
object means you have-- the two objects have the same address. It doesn't mean they're equal objects. I mean the same objects. So what is the self assignment, one
object to itself literally that you want to avoid and this makes a simple test. Do the two objects have
the same address? If not, then we go forward. If they do have the same
address, there's nothing to do, so we just return the self-reference. So with all those caveats, we've now
got a more or less bullet proof way to define the assignment operator
for class IntArray and the principles on this is generalized to any class that
has dynamically allocated resources. OK. So we kind of summarize all this
by saying that we have a proper type. Of course native type. The proper type means informally what's
being proper type means is it's a user-- a type that can be handled
just like a native type. It's got an assignment operator that can
come in the scope and go out of scope, we never have to manage memory for them. You can pass on that value to functions. You can return as values
for all the functions. You can use one to initialize
another in a declaration. You can assign one to the other and you've even got the right
associative assignment operator defined correctly. So everything works just
as if you're playing around with simple numbers or something. And that's what we mean
by our proper type. So in the client code, it can do
wonderful stuff like declare IntArray A, B is 100 integers IntArray
and there's lots of zero and C has 100 integers IntArray,
initialized to minus one. We can do B equals A and that means that makes I guess A would
be the default kind on top of the list 10 integers
initialized to zero. Saying B equals A would have the effect
of B allocating those 100 integers that B had originally and reallocating
is placed for 10 and copying the values from A to B. And similarly, you
can write A equals B equals C and that makes A, B and-- A and B each
equal to of what a complete copy of C. And when these things are out of
scope, their destructors get called. One little factoid is I declare I mean
IntArray B, C that destructor were to get called and they reverse orders. So, first destructor of
C and then destructor of B and then destructor of A. [ Pause ] What I would like to do
is [inaudible] format, let me just say how many
slot I've got here. I've got quite a few-- quite a
few a slides so I'm just going to, I'm going to take a break from this. And have some parameter
that will create some code. So, what I'm going to do here
log in the [inaudible] log. I'm actually on profile
[inaudible], yeah. [ Pause ] OK. Let me-- I might as well
just make this full screen. I'm going to go into my-- where [inaudible] director
is and get into examples. Then I'm just going to create a
create a little file X dot CPP, oh my. You see, I've done this before. How about Y dot CPP. OK. So, I'm going to start at
with some-- I'm going to want to-- I'm going to want to use
input output some other side. [ Pause ] Include iostream. I'm going to declare a class. [ Pause ] Alt A. And yes, I always do
the extra slide for that. So, I won't forget semi column. Now, I'm going to have public [ Pause ] I'm going to RA or constructor
and a destructor. [ Pause ] And that's all, turn in off. Now I can do int m-- oh wait. I wan to implement this thing. So, this is going to be A, A, that's. I must have the caps
on, I'll just do it. OK. A, A, to the constructor
all I'm going to do is just-- [ Pause ] This is a-- K4 is not
cooperating very well. I got a quote there. I'm waiting for it to-- here. Just kind of wait until
I get another quote. It should be both of them. So my constructor is just kind of
print out the name, you know, the fact or the [inaudible] is called. And-- [ Pause ] My destructor is going to do
essentially the some thing and so my A class destructor-- [ Pause ] That is where-- strange
what this keyboard has done. Well, see how the first
one, until I hit the second. OK. Another tilde, if
I have a tilde again, I'm just going to show you real quick. I can't turn on that. All right. So now, int main-- I'll
show you a couple of sign. And one I'm going to do class
A, a and class A, b. OK. I'm going now compile that and run it. And of course all was going
to do is create A, create B. And then call the destructor in that. Another things I want to do is
show you that you can make-- [ Pause ] A, an array of A objects,
the 10 of them. And then they go down the scope as well. OK. [ Pause ] OK. So, we're going to use
[inaudible] telling us system warning. But now, I should be able to-- well,
not executable y dot X. And let's-- what I want to point out is that
the A constructor got called once, that was for the declaration
of one object. Then it got called 10 more times. One, two, three, four, five,
six, seven, eight, nine, 10. Wants for each object in that array and
then you'll see eleven calls to the A, class A destructor as well. Again, one [inaudible] we got
a simple variable of type A and then 10 more recovery on
array of 10 [inaudible] objects [ Pause ] You can really learn a lot by creating
classes like this that do nothing but the constructor and destructor, just kind of let you
know when they do a call. Can really--