In the previous video, I mentioned that the c++ compiler must know
all about a function before it allows that function to be called.
I didn't say why though.
The reason for this is that the compiler can't check to see if
the number of parameters passed in and their types are correct
when the function is called, unless it knows about the function
being called before it's called.
There are two ways we can make this happen.
The first way is to make sure we define all functions
before they're used.
This is okay for many small programs, but it quickly
becomes very difficult to do.
And in the case of two functions that call one another is impossible.
So rather than have to worry about the order in which we
define functions, c++ allows us to create function prototypes.
Function prototypes tell the compiler all it needs to know about a function,
so that it can be sure that the function is being called correctly.
Function prototypes are also called forward declarations since you're
basically telling the compiler, hey compiler this is what this
function is going to look like, make sure I use it that way.
In smaller programs, all the function prototypes are generally
placed near the top of a program, usually after the pound includes.
As programs get larger, we split our programs up into multiple files,
so we include .h or header files.
And we include function prototypes in these files.
We'll see that a little bit later in the course.
Let's see what these function prototypes look like.
Here, I've defined a function called function name at
the bottom of the slide.
The function prototype for this function is the
first line of code you see.
Notice that it looks very much like the function header.
That's the part of the function definition right before
the body of the function.
With a function prototype, the compiler doesn't need to know what
the implementation of the function is, only its header information.
So in this case it knows that you'll later define a function
named function name that expects no parameters and returns an integer.
And if you use a function named function name, it will
enforce those conditions.
If you try to use it in any other way, say by calling it with an
integer in the parameter list, then you'll get a compiler error.
Pretty easy, right.
You can have as many prototypes as you need.
One per function, and it doesn't matter what order you declare them in.
Let's take a look at another one.
In this case, we have the same function named function name.
But this time, it expects exactly one parameter.
So we provide a function prototype to the compiler that has an
int in the parameter list.
Note that we can supply the name of the parameter if we wish, but
it isn't used by the compiler.
The compiler doesn't care about the name, only the type.
So either of these function prototypes can be used. Best
practice is to provide the parameter names for documentation purposes.
Let's see another example.
In this example, we have a function that returns nothing when called.
So the function prototype must include the void keyword, prior
to the function name, just like the function definition does.
In this case, the function expects two parameters, an
integer and a standard string.
So either one of these function prototypes in the slide is valid.
In one case, we provide the parameter types and names and the
other only the parameter types.
The function prototype for the say hello function we saw in
the previous video would look like this: void say hello and
then the empty parameter list.
Notice the semicolon at the end, that's very important.
We're not specifying a body, only the prototype.
If we try to call this function in any other manner,
we'll get a compiler error.
So let's see how the compiler uses this information in the function
prototype to help us write code.
The function prototype here says that I'll define a function
named say hello that expects no parameters and returns nothing.
Then I use or call the function in main.
The first function call is fine since it matches the prototype.
The second function call will cause a compiler error.
We told the compiler that the function expects no parameters,
but we're passing in an integer to the function call.
The compiler won't allow this.
The third function call is within an output statement.
Think about this one for a second.
You're telling the compiler to display the value that say hello evaluates to.
But the function prototype says that the function say
hello doesn't return anything.
So the compiler gives you an error since something's not right.
In this slide, I'm providing the same code that you
saw in the previous video.
However, in this example I'm providing function prototypes
for the say hello and say world functions at the top of the file.
Now the compiler knows about these functions and I can call them in
any order I wish in my program and the compiler will make sure
that they're called as intended.
Let's head over to the IDE and prototype some functions.
Okay, I'm in the CodeLite IDE and I'm in the section 11 workspace
in the function prototypes project.
What I'm going to do here is I'm going to take the sample that we
did in the previous video where we calculated the area of a circle and
the volume of a cylinder and so forth.
And I'm going to rearrange these functions to
actually break the program.
Now this worked out because I was very careful about how
I defined these functions and where I defined these functions.
So again, the compiler is going straight down and
reading the source file.
So it sees calc area defined here, calculator of a circle.
Then it sees it used since it's already seen it defined,
it's happy about that.
In this case, it's the same thing.
It sees calc volume of the cylinder, I use it down here.
It's happy about that.
So everything it's seen before it's being used.
But if I take some of this code, let's say that I take all of
this code actually, right before my main and I just cut it and I
want to paste it after the main.
Okay, so now we've got some issues because the compiler
is now seeing - let me clean up this whitespace a little bit.
The compiler is now seeing area circle function being called but
it hasn't seen the definition yet because it comes later.
That's going to cause an issue.
Same thing with volume of the cylinder.
So you're going to get issues here.
So let me run this or compile it actually, it won't run.
And there you go.
Area of the circle was not declared in the scope, so it has no idea
what that function's all about because it hasn't seen it yet.
Okay, you're going to get two errors here.
If we change the order and put this guy down here, we're going to get even
more errors because now we're using something before it's being seen.
So now we're going to get probably three errors, right.
So that's a problem.
How do we fix this problem?
Well, that's where function prototypes come in.
So let's let's use function prototypes.
They're really, really easy to use.
What I want to do is for every function that I'm defining,
I want to prototype it before I've defined it, so obviously
at the beginning of the file.
And we'll do it right after the namespace statement.
So let's look at this first one here.
I'm just going to copy it and paste it because basically that's what I want.
So I'm going to put it right here and I'll just put a little comment here
just so you know what's going on.
So here are my function prototypes.
Remember, the function prototype is only the name of the function
and what it expects and what it returns with a semicolon at the
end right here, no function body.
So now the the compiler sees that you've prototyped calc volume cylinder
to expect two doubles and return a double, so we can type check that.
Okay, let's deal with all of these.
So let's do the next one, which is my calculate area of the circle.
I'll copy that up here, and paste it, and put a semicolon on the end.
Now remember, we don't need to have these variable names
here, these parameter names.
The compiler could care less.
So I'm just going to get rid of this one just so you can see.
The compiler doesn't care.
All the compiler needs to know is what is the type of that parameter.
So in this case double is fine.
As I mentioned earlier, best practice is to put the names in
there for documentation purposes.
But I'll leave it out here just so you can see that it
actually does work just fine.
Okay, so now we've got those two.
Let's deal with area of the circle and then we'll deal
with volume of the cylinder.
So we'll put area of the circle right here.
The order in which you put the prototypes in doesn't matter because
the compiler is going to see them all.
So there's the area of the circle.
Notice this returns nothing and expects nothing.
Okay, and then we'll do the last one, which is the volume of the cylinder.
So I'll come back up and I'll put that last prototype right in here.
That's it.
Now if I compile and run, I'll get a clean run.
So we'll put 12 and type in whatever numbers you like.
So that's pretty cool, makes it really easy to not have
to worry about ordering oh my goodness you know what did I call
this, did I define this first.
This was a problem in early languages.
I remember using Pascal and there was just no way to get around
this, eventually they added something like forward declarations
which really makes things handy.
So this is just standard practice that c and c++ programmers use to
create those function prototypes.
Now what really is nice about this - suppose that I mess up my function
prototype and I tell the compiler that, that area circle function
that I'm going to define later, this is the way you should check
to make sure I use it correctly.
Well, I'm not using it correctly right there because I'm not
passing an integer into it.
So this won't compile.
This will give me an error right here.
It says too few arguments to this function right here.
So you've told it -- again, remember, it hasn't seen
this one down here yet right.
You've told it that that function will be used as in the prototype.
And here, you're not using it as in the prototype.
Okay, so let me get rid of that.
Also if I send in, let's say 5 into here or 4, right.
Again, this is not going to match.
You've just told it that this function here expects nothing.
But now you passed in an integer, so again it's not going to like
that, it's going to say too many arguments now to the function call.
Okay, so let's clean that up, and that's really all
there is to prototypes.
Now normally, as your programs get larger, these prototypes
will end up going in a different file, in a .h file.
And we'll get to .h files or header files, our own header
files when we talk about classes because that's very common to
create two pieces of the program.
One is the header information and one is the actual implementation,
very similar to what's going on here with iostream.
Those are really considered header files.
So you're going to grab a lot of information from there.
And that's usually where we put our function prototypes.
But there you go.
Let me run this again and make sure it's clean, got a clean run here.
So this -- I'll leave this code out there for you to play with as well.
Get used to these function prototypes, we do them all the time, and it
will really make your code better.
Let the compiler help you every chance it gets.