In this video, we'll learn about how to implement member methods in our c++ classes.
You'll be glad to hear that class methods are implemented
in a manner very similar to the way we implemented functions.
But I'd say that they're generally less complex.
This is because member methods have access to the classes member attributes,
so we don't have to pass so many arguments around.
C++ gives you many options regarding where to write code for member methods.
You can define the member methods right inside the class declaration.
If you do this, then the methods become implicitly in line.
While this is okay for small and relatively simple methods,
we tend to implement larger, more complex methods outside of the class declaration.
In order to do so, we need to tell the compiler that we're implementing a method
that belongs to a specific class.
So we use the class name
followed by the scope resolution operator and then the method name.
This unambiguously tells the compiler that you're implementing a method
for a specific class.
In c++, it's also very common to separate
a class's specification from its implementation.
This makes the class much easier to manage.
We usually create a header or include file
that has a .h or .hpp extension
for the class specification and then a separate .cpp file
for the class implementation.
First, let's look at implementing methods inside the class declaration.
In this example, we're declaring an account class.
Notice that the account class has a private double named balance,
and two public methods: set balance and get balance.
Set balance sets the balance to the value passed in by the user,
and get balance returns a copy of the account balance.
These public methods are necessary
to access the balance since the balance is private.
Notice the syntax is exactly as it was for defining regular c++ functions.
Also notice that the public methods
have direct access to the private balance because they're members of the same class.
Now let's see how we would implement these methods outside the class declaration.
In this example, we're defining the set balance and get balance methods
outside the class declaration.
Inside the class declaration, we simply provide the method prototype or declaration.
This tells the compiler the name of the methods,the parameters they expect and the return type.
The compiler can then type check the method implementation when it sees them.
In order for the compiler to know that these methods belong to the account class,
we have to prefix the method names
with account followed by the scope resolution operator.
Then we implement the method as we normally would.
In this case, the compiler knows that the two method implementations
are for the method declarations in the account class we provided.
As our programs get larger, we really want to separate class specification
and class implementation into different files. Let's see an example of how we can do that.
In this example, we create a file named account.h.
This is an includer header file
that will pound include in our .cpp files
whenever we need to use the account class.
Notice that in this file we provide the specification for the account class.
Before we can use or pound include this account.h header file,
we need to take care of a potential problem.
If this file is included by more than one cpp file,
then the compiler will see the declaration for the account class more than once
and give us an error about duplicate declarations.
In order to solve this, we can use what's called an include
guard that ensures that the compiler will process this file only once,
no matter how many times it's included.
An include guard is just a series of pre-processor directives that guarantees the file will only be
that guarantees the file will only be included once.
The way it works is we wrap up our entire class declaration in this include guard.
The first line is pound if not defined
underscore account underscore h underscore, all in uppercase.
This checks to see if the preprocessor has a symbol named
underscore account underscore h underscore.
This simple name can be anything you want,
but best practice is to use some sort of combination of the file name
in uppercase with preceding and ending underscores.
Any name is fine as long as it's unique in the program.
If the preprocessor has that name defined,
then it skips the entire file and goes to the pound end if at the end.
In other words, it doesn't process the file since it's already seen it
because the symbol has been defined.
If the symbol has not been defined, then the symbol is set
and the file is processed as usual.
As you can see, it's pretty simple
but not very elegant compared to more modern programming languages.
So now this is what the account.h file typically contains.
You can see the include guard wrapping the class declaration.
Now we can provide the implementation for the account class
in a separate file named account.cpp.
Many compilers also support the pound pragma once compiler directive.
The effect of this directive is the same as the include guard,
while many compounders support this directive, some don't.
So check your compiler documentation to be sure.
Now we can provide the implementation for the account class in a separate file
named account.cpp. Let's take a look at that.
This is the source code for the account.cpp file.
Notice that we include account.h and it's in double quotes.
Up to this point, our includes have used angle brackets with no extensions.
There's an important difference between the two forms of include.
Include with angle brackets as in include iostream
is used to include system header files
and the compiler knows where these are located.
Includes with double quotes tell the compiler to include header files
that are local to this project, the compiler also knows where those are.
Notice that this file implements the two methods declared in the account.h header file.
Now our account class is complete,
the specification is in account.h
and the implementation is in account.cpp.
How do we use an account in our main cpp file though.
Let's see in the next slide.
Here's our main.cpp file.
Notice how we include iostream as well as account.h.
It's important that you always include .h files, never include .cpp files.
Then we just write our main as we normally would.
When the program is compiled,
both the main.cpp
and the account.cpp files are compiled
and then linked to produce the executable.
Let's see how all this works in the IDE.
Okay. So I'm in the IDE, and I'm in the section 13 workspace
in the ImplementingMethods1 project.
What I'm going to do here is
I'm going to implement these methods in this account class and we'll implement some of them
inside the class declaration and some of them outside the class declaration.
Then we'll create another project ImplementingMethods2
where we'll separate the accept specification from the implementation
and we'll create the account.h file and the account.cpp file.
Okay. So let's start here.
What we're doing here is we have our class account right here,
and we've got two private attributes: name and balance.
And then we've got the methods. And the methods are really what we're interested in here.
Now in this case, I've got six methods, all public.
We've got a set balance and a get balance.
And you can see that these methods have been implemented in line
right inside the class declaration.
This is easy to do, and this is okay to do when you've got methods like
these that are really really straightforward methods.
They're done. They're implemented, and we're all set.
Now we can also implement methods outside the class declaration.
Notice these last four methods.
I've got set name, get name, deposit and withdraw.
And what you see right here are basically the function prototypes or the method prototypes.
They're not defined here. These are only declarations.
What we want to do is we want to define them outside the class,
and you'll notice the class begins here on line 8, and it ends right here on line 26.
So what we want to do is define them outside the class. And let me show you how to do that.
It's really straightforward.
What we've got here is -- so let me show you how easy it is.
Here are the methods that have been implemented outside the class.
We're starting on line 28, right here. The method is set name, which is this method right there.
It's called set name.
Now it's not just good enough to say void set name, that would just
define any method.
We want to define the set name for the account class.
So we need to prefix the name of the method set name
with the name of the class followed by the scope resolution operator.
Now when the compiler sees this, it knows okay,
you're defining the set name method that you declared in the account class.
So it's going to come up here, and I'll be able to type check
all of these things together and make sure that there's no mistakes.
Okay. So you can see what's going on here.
In this example, it's really straightforward.
A string is being passed in,
and the name is set to whatever string was passed in, which is n. Remember, name is
one of the attributes of the class.
It was private, and that's okay. This is a member method. So it has
access to private information.
Get name simply returns a copy of the string.
Okay. And in this case, we've got our deposit or withdraw.
So you're going to pass in an amount to deposit.
This method would somehow verify that that amount is indeed there,
so that you can deposit it.
And all it does is it increments balance by whatever the amount is.
And it returns true. Obviously, you would return false if it didn't verify.
Okay. And then the last method that we'll implement is
this account withdraw method.
Again, it's the same idea. We prefixed the name of the method
with the name of the class because it's a class method.
And here's the amount. And what do we do here?
Well, we look at the balance and we subtract the amount from it.
If that's greater than or equal to 0, then there is sufficient funds.
We'll just decrement the balance by the amount,
and we'll return true. Okay. Simple as that.
If the funds aren't there, then we'll just return false.
So let's see how we can use this in the main,
and it's really, really straightforward. I've got some break points in here that we'll follow in a second.
So what I'm doing here is I'm creating an account called Frank account.
And I'm setting the name to Frank's account.
Now I can't say Frank account.name
because that's private. So I've got to go through this public method called set name.
I'm doing the same thing for balance. Frank account.set balance to a 1000.
I can't say Frank account.balance equals a 1000 because balance is private.
Okay. So at this point, I've got an account.
It's got Frank's account is the name, a 1000 is the balance.
And then I want to deposit 200 into that account.
I'm going to call Frank account.deposit 200.
If that returns true, then the deposit will okay, and I'm going to just display that.
If that returns false, then the deposit was not allowed for whatever reason.
Now I'm going to call some withdrawal methods here.
I'm going to call the withdrawal method on the Frank account with 500.
So I'm going to check, I should have a 1000 in there,
right. Actually I should have 1200 in there because I just put 200 in there.
So I'll check to see if 1200
minus 500 is greater than or equal to zero which it is,
and the withdrawal should be okay.
I should have 700 left after that.
Then I try to do another withdrawal with 1500 and that should fail.
Okay. So let's let's run this through the debugger here.
Right now, I'm at line 53.
And I'm going to set a watch to Frank account.
That way I can see that as it changes, really easily,
and that's already set here.
So what I'm going to do is I'm going to hit next.
And remember, I'm right here on line 53. So at this point,
I've just created the Frank account object.
Now I want to set that name. Notice that the name is empty right now and the balance is garbage.
I'm going to set the name to Frank's account. So when I select next,
you can see that that's changed right here to Frank account.
And now I want to set the balance to a 1000, so we should see that change here as well.
Again, remember, these two attributes are private.
That's why we're going through those public methods to change them.
Now what we're going to do is we're going to deposit 200 into that account.
So let's step through here.
And what we're going to do now is we're going to jump to this method right here.
I've got a breakpoint in there, that's where I'm at right now.
Assuming there was some business logic here that would verify that that deposit amount was there,
I'm going to increase the balance
by 200, so I should have 1200 here when I finish and return true.
And I'm back now. You can see I've got 1200 here.
And I'm going to return true, and it should display deposit okay over here.
And exactly what happens.
Now we're going to withdraw 500. Well, Now we're going to withdraw 500. Well,
So let's try that. And you can see I'm right in here now.
I've jumped to this function, this method right here.
So balance is 1200 minus 500 is greater than or equal to 0, so we should be good.
In that case, I'm going to decrement the balance by the amount, and I'm going to return true.
And I'm right back here now on line 63.
Notice I've got 700 left in the account, and it should display
withdrawal okay.
The last example here is I'm going to withdraw 1500. I don't have 1500.
So it should fail.
So I'm going to go back up here if balance amount is greater than or equal to 0, it's not.
So it's going to go straight to false, which it's going to return.
And then when I come back to my main,
I'm right here on line 70 and it should display not sufficient funds.
And that's exactly what it does.
Okay. So there you go. This is a real simple example of
implementing these class methods both inside the
class declaration and outside the class declaration.
Okay. What I'm going to do next is we'll create a new project called ImplementingMethods2.
And what we'll do is we'll create the account.h and account.cpp,
and then we'll have a main cpp that runs the whole thing. So I'll set that up and be right back.
Okay. So I'm back. I'm still in the section 13 workspace,
but I've just created a new project called ImplementingMethods2.
And all it has in it is a main cpp file that you're looking at right here.
What we're going to do here is we're going to create our account.h file,
our account.cpp file.
And then we'll use this main cpp
file as a driver so that we can create some objects and so forth.
Okay. So what do we do? Well, we need to create an account class.
Every IDE has its way of creating a new class.
CodeLite is no different. If you select the source directory and right click,
you'll have an option for new class. We'll select that.
And then you get this pop-up dialog, and it's going to ask you, what's the class name?
Well, the class name is account, again with a capital a.
Don't worry about namespace. And then the block guard is what you saw with the include guard that
i talked about.
This is that pound define.
So I'm just going to say underscore account
underscore h underscore you can really do anything you like, you can even
leave it blank and it's going to use its own way of doing it.
Okay. So that's it. There's some more advanced options here,
which should all be off at this point.
And you can choose to create a .hpp file instead of a .h file,
I'm not going to. And you can also choose to use the pragma once command.
Again, I'm not going to. I'm just going to use the block guard.
You select okay.
And what happens here on the left-hand side, you'll see that
we've got two files that were created. We've got the account.h file
and the account.cpp file, that's exactly what we want
Now we're going to start with the account h file, notice the wrapper.
Okay. Now I'm going to get rid of what it generated for me.
And what I'm going to do is I'm going to copy and paste the account that we were working on.
And I'm just going to copy and paste the declaration of the account.
So let me do that right now, and I'll paste it right in here.
It should be pretty familiar.
There's the class account. Those are the two private attributes.
Let's see what we've got here? Now we've got to do a couple of things. We're using a string here.
So what we want to do is here we want to pound include string,
so our account header file is complete.
And we really don't want to use the namespace in here. So
I'm just going to prefix this with standard.
And let's see I've got a string here as well.
And I believe I've got one here as well.
And I think that'll do it. So that's my account
.h file. You can see it's exactly as we had before.
There's my account declaration and it's wrapped up in that guard.
Okay. Now let's go to the account cpp.
Now you'll notice that it's generated the include for me.
And also it's generated these two functions, these two member functions.
We've got to get rid of those. This is a constructor and that's a destructor.
We'll talk about those in a couple of videos.
But for now, I just want to get rid of them.
And what I want to do is I want to copy and paste
those four methods that we wrote. Remember, the ones we wrote outside the class.
So I'm going to copy them right in here like that. There's my account.
There's my -- and again, I'm going to prefix this with standard,
wherever we're using strings.
And I think that was the -- right here's the other one.
That's it.
Now what we'll do is we'll go to the main.
And what we're going to do here is we're going to include
account.h
because we're going to need it in our main. And if you remember the code for the main,
again, I'll copy and paste it because it's really straightforward.
That was the code for the main where we created the Frank account,
we set the name for the account and so forth.
Now what we're going to do is I'm going to save this and we'll compile the whole thing.
We'll just basically do build and run, control F5. What's going to happen is
the .cpp files will both be compiled and everything will be linked
into an executable. Now we may run into some errors here,
and we'll fix those as we go. So I'm going to hit control F5,
no errors.
Everything worked great. You can see I run the file and i get
deposit okay, withdrawal okay, not sufficient funds, exactly like we had before.
We could run this through the debugger if we like.
But it's really as simple as that.
Throughout the course, this is the way we're going to start writing code now. We're going to have
the specification here
and the implementation in the .cpp file.
And you can see -- again, let's do this one more time,
you can see the account.h the header or the include file
that is our declaration for the account class.
We have to wrap that up so that the compiler only sees it once.
We're including string here because we've got our string
class that we're using right there.
And we really don't want to use name spaces inside these header files
because everything that includes them is going to get that all that name space stuff,
which is really not necessary.
So what we've got there is that we've got our private stuff, we've got our public stuff.
We've defined these two methods right inside the class declaration.
And then these other methods we've defined outside the class declaration
in account cpp.
Now in order for the compiler to understand what you're talking about here,
you have to include account.h.
Once you include account.h, the compiler now knows what this is.
Okay. Once that's done, you go to your main and you can see the main is now
pretty self-contained. You're just including account.h,
it's really really simple to do. The idea is just
you're going to have a lot of header files and a lot of cpp files
in your application, and everything gets compiled and linked into a single executable.