In this video, we'll see a use case that's commonly seen in c++,
and we'll see how we can use exception handling with it.
Suppose we have a function. Of course, this will also work with methods.
And that function is called calculate average.
It expects a sum of integers that we can assume has already been calculated somewhere else,
and the total number of elements used to get that sum.
And what we want to do is determine the average and return it as a double from this function,
pretty straightforward stuff.
Also notice that we don't want integer division
since we want to return a double, so we're using a static cast
to convert one of the division operands to a double. Cool.
So, so far pretty easy, right? Well, almost. What if total is 0?
What do we do? What do we return?
We could return 0, but 0 might be a valid average.
We could return negative 1,but negative 1 might be a valid average.
We could return some other double that represents an error,
but which one since just about any double could be a valid average.
We could rewrite this function to return a Boolean
and that Boolean would indicate success, right. True if it worked, false if it didn't,
but then how do we return the average.
We'd have to use a reference parameter and do it that way or use a tuple,
makes the function really clunky to use.
So let's see how we can write this function using exceptions and exception handling.
We can rewrite the function without changing its interface and still provide a guarantee
that the function will not divide by 0 by throwing an exception.
First we check if total is equal to 0.
If it is we, don't want to do the division.
Instead, we throw 0.
In this case, we could throw anything and c++ allows us to throw just about any type.
But let's use 0 again which is an integer, and we've done it previously.
If we throw the exception, the division will not happen, and the function will terminate.
Now c++ will try to find an exception handler
to catch that integer that the function threw.
How does it do that. Well, the handler isn't in this function so where else might it be.
It may not exist at all, in which case, the program will terminate.
But if an exception handler exists,
it must be in one of the functions on the call stack that called this function, right.
And c++ will unwind the stack looking for it.
We'll see how this works in a few videos.
So let's see how this function might be called in a way that would allow us to deal with the exception.
Now this code could exist in any function anywhere in your program.
Notice that we have a try block. And in the try block, we call the calculate average function.
We know this function might throw an exception.
And if it does, we'll deal with it in the catch block.
But first let's see what happens if no exception is thrown.
Again, we're assuming that sum and total have been declared and initialized.
In this case, average is assigned the return value from the function and we display it.
Then we skip the catch block completely since no exceptions were thrown,
and we execute the last statement that displays by.
If calculate average does throw an exception,
then the remaining code in the try block is not executed.
So we do not do the division by 0.
And c++ searches for a catch block that expects an integer
since that's the type of the exception that was thrown.
In this case, it finds it.
Notice that our catch handler expects a reference to an integer.
Again, best practice is to throw by value and catch by reference.
We'll talk more about that later.
So in this case, the code in the body of the catch handler executes,
displays an error message to the error stream.
When the catch block completes, we transfer to the last statement, and we display by.
That's it. It's possible to have multiple catch handlers
as well as catch all handlers that will catch any type of exception.
We'll do that in the next video. But first, let's head over to the IDE,
and we'll rewrite the miles per gallon example
as a function and use exception handling with it.
Okay. So I'm back in the IDE. Again, I'm in the section 18 workspace,
and I'm in the MPG function project.
What I've done here is I've taken the calculation for miles per gallon,
and I've made it a function.
So in this case, it expects miles and gallons which are both integers and returns the double,
which is the miles per gallon.
And I'm simply dividing miles by gallons.
And again, I'm casting miles to a double so that we do double division and get an accurate result.
That's it. My main is just like it was before the user enters the miles, the gallons,
and the only difference now is that on line 19 right here.
You can see that I'm calling calculate miles per gallon,
and I'm passing in the user entered values.
So let's run this as is
without any exception handling, without any error checking at all.
So we'll run this, and let's say I entered 120 miles
and I entered 0.
There's my result, which is infinity. That's not what I want.
And we'll deal with that in just a sec. So
we know that we don't want to divide by 0. So what
we can do up here in the function is we can say if gallons
is 0,
we want to throw an exception. And we'll just throw -- again, we'll throw 0,
which is a simple integer.
Now let's run this. You'll see something that's a little bit different
now. When I call calculate miles per gallon here
and I use a 0 for the gallons,it's going to throw this exception.
That exception is not being caught anywhere.
So it's going to go all the way up to the top in my main, that's where I'm at now,
and our program is going to terminate. It's going to terminate abnormally.
So let's run this so you can see what that looks like.
So again, I'm going to enter 120, and I'm going to enter 0 for the gallons.
And you can see right there it says -- I'll cancel that, you can see it's
terminate called after throwing an instance event.
This application has requested the runtime to terminate
it in an unusual way, a bad termination, right.
Basically, an exception was thrown that was not handled,
that will always terminate your program.
So how do we handle that exception. Remember,
the mile -- to calculate miles per gallon function up here
has no clue what to do when its gallon is 0. All it knows is
I can't do a divide by 0, and should i just print an error message?
I don't know what to do, so someone else do it. I'm just throwing the exception.
So what we can do down here is
we can write a try block around this code that could potentially throw an exception.
So let's do that. I'll say try,
and I'll close off the try block here. And we'll indent this area right here.
So I'm going to try to execute that function. If the function works properly,
then I just print out miles per gallon. If that function throws an exception,
then we're not going to divide, right, because it's thrown it before the division,
it's not going to assign miles per gallon. Basically, the try block will stop,
and it's going to look for a catch handler, right. So let's write the catch handler.
Our catch handler looks kind of like this.
What am I going to catch? Well, I'm going to catch an integer by reference.
And right here, I'm simply going to print std cerr
tried to divide by 0 or something like that.
Okay. That's it. So you can see now that the error is detected in one place,
and it's all in one file here just for instruction purposes.
But this could be anywhere in your program, right. The error has been detected here,
and it's been handled here. Okay. So let's run this now.
And we'll do it -- again, 120 and 0
try to divide by 0 by our program terminated normally.
We handled the exception in a controlled manner,
and that's really what it's all about.
Okay. Now in this case, there could be some issues, right.
We need to catch some other exceptions, and that's what we'll do in the next video.
What if miles is negative,
but that doesn't make sense. I traveled negative 100 miles per gallon.
So we want to also throw an error if miles is negative
or if gallons is negative for that matter, right. How can you have negative gallons?
So in this case, we're just doing a simple test so we don't divide by 0.
But in the other cases, which we'll do in the next video,
we're going to check to make sure that we've got valid values here. So if miles is negative
or gallons is negative, then my calculation cannot be correct.
So we'll throw another exception there,
and we'll catch multiple exceptions. So we'll do that in the next video.