In this video, we'll see how c++ looks for catch handlers
when an exception is thrown.
Suppose we have a function and it throws an exception.
If the function doesn't handle its own exception,
then the function terminates and it's removed from the call stack.
Then c++ looks at the call stack to determine the function
that's now at the top since that function must have called the function that just terminated.
If this function used a try block, then the catch handlers are checked for a match.
And if one is found, it fires, and we keep processing
as we've seen in the examples already.
But if there is no try block
or there is a try block but it doesn't have a catch handler that matches,
then the function is removed from the call stack
and the process repeats. This is called stack unwinding.
If we get to the main function
and the exception isn't handled there either,
then the program terminates since the exception was not handled.
Remember, what happens when a function is removed or popped from the stack,
all of its local variables are destroyed.
This means that for local objects their destructors are called.
If we have to free memory with delete,
then these statements may not execute at all, and we will leak memory.
This makes it critical that you implement classes that require resources
as RAII classes.
We talked about RAII in the smart pointer section of the course.
Make sure that you clean up your resources and your destructors
since you never know when they might be called,
and you don't want to leak any memory or leave files or resources in unstable states.
Stack unwinding is one of those things that's best understood with an example.
So let's head over to the IDE, and I'll show you an example of a program
and how stack unwinding works.
Okay. So I'm in the IDE. I'm in the section 18 workspace in the StackUnwinding project.
And what I've got here is just some really simple function calls.
There is no exception handling. There's no throwing. There's no catching.
There's nothing special going on here. We will add that in a minute.
This is our base case. So I just want to go over this to make sure that you understand
what happens when we've got this function calling.
So let's start here. Here's my stack. Remember, all this happens in the stack.
So here's my stack. Remember, all the function calls
happen on the stack. So I've got my main. So that means that I've got main on the stack.
So main is activated on the stack. We print out starting main,
and then we call function a. Well, function a is up here.
Function a gets pushed on the stack.
So this would be function a here,
and it displays starting function a. Then it calls function b,
so we go to function b now, function b gets pushed on the stack.
It displays starting function b. It calls function c.
And function c is put on the stack,
so you can see it's a real nice orderly process of function calling.
It displays starting function c.
Then it displays ending function c. Then it's popped off the stack.
B completes now, and says ending function b.
And it's popped off the stack,
a says ending function a. It's popped off the stack,
finally, we're back here, and we end up printing finishing main.
Okay. So each function announces itself and then it says I'm done.
And if we run this project, I'll clear this screen here,
and if we run this project what we'll get is exactly what I just said,
let me move this over here so we can see it.
Here you can see we're starting main,
then we're calling function a, function b, function c.
And now we're starting to unwind, right. Our program is finishing. So function c is done.
Function b is done. Function a is done and then main.
This is what you would expect. This is normal control flow
when there is no exception handling.
Now what we're going to do is we're going to add a couple of throws in here and a catch.
So you can see exactly what happens when we're doing stack unwinding
with exception handling. Remember, exception handling
definitely could change the normal control flow of your program.
So this is the normal flow of our program assuming we've got
no exception handling and no exceptions and no problems at all.
All right. So let's say that function c throws an exception,
and we'll just throw 100, we could throw anything.
The point is we're throwing an exception. So I'm just going to throw 100 here.
And in my main where I call function a right here,
I'm going to enclose that in a try block.
So there's my try block,
and I'll indent that.
And then what I'll do is I'll put a catch handler here
and that will expect an integer
by ref, we'll call it ex.
And we'll just write something like std cout,
let's just say caught error
in main.
Okay. Something real simple like that.
Now let's walk through this and see what we expect to happen.
We're here. Main begins and it
prints out starting main.Actually, let me close this up just a little bit so I've got a little bit more
room to work.
Okay. Perfect. Everything fits just right.
Okay. So we're here in main, and we're going to print out starting main.
Then we're going to try function a. Function a could throw an exception.
So I'm coming over here.
I call function a, and it prints out starting function a.
And it calls function b.
Function b begins displays
function b. Starting function b, it calls function c,
which displays starting function c. Now it throws an exception.
What does that mean? That means that this function is now done
because it's not catching its own exception here.
So this code right here will not execute.
We will never see this function complete normally
because it's being pulled off the stack right now.
Okay. Now what happens. Well, now we go back to whoever called function c,
which was function b. So now we're back here
and does that have a catch handler for an integer?
No, so this guy gets popped off the stack,
right. So this won't execute either. We go back to a,
right, because a called b, it doesn't have a catch handler.
This won't execute either. And now we finally get back to the main,
the main does have a catch handler. This will fire,
and our program will finish normally.
But what's happened on the stack is real, real different, right.
You'll notice these three functions got ripped off the stack immediately.
All its local variables were destroyed, destructors were called if if any.
So you've got to be real careful with your objects here. Make sure that their destructors
are correct because they just got called, and they didn't get called as you expected
them to be called. So let's run this and see the output now.
Now notice the output, Now notice the output,
We're starting main. We're starting a. We're starting b. We're starting c, right. Just as we said.
Now c throws an exception right here on line 23. It doesn't catch it.
So c is gone right now. Notice how the ending function c is never called.
We go back to b. It doesn't catch an integer.
It's gone. We never see the ending function b.
We go back to function a, it doesn't catch it either.
So notice what happens here, right. We never see the ending function a message.
We finally come back to the main.
Main catches the exception. There you go, caught error in main and it finishes normally.
So this is a good example of stack unwinding.
You can see exactly what's going up that chain right
now. If I do something like --
let's try in function b, let's say, right here,
let's say that we're going to call function c,
let's just try to call function c and see what happens.
And in this case, we can catch
the integer.
And what I'll do is I'll copy that output statement that I created just down here in main real quick.
And I'll paste it right in here
and I'll just simply say caught error in. This is function b.
Okay. So now let's walk through this example and see what's going on.
Let me close this up again.
And I'll scroll up just a little bit. So hopefully, we can fit it all in here.
Okay. That's function a up top.
All right. So here's main. We're trying function a.
Function a could throw an exception or anything
called by function a could throw an exception as well.
So I'm calling function a. It says starting function a, and it calls function b.
Function b displays starting function b,
and it tries to execute function c.
So what's going to happen here is function c executes, and it throws an exception.
This will not execute.
Function c is now gone.
But now we come back, and function b is handling the exception.
So now this will work, and this will work. And from now on,
function b and then function a will all execute normally.
Okay. So let's run this and see what we get.
All right. Look at this. What have we got. We've got starting main.
Then we're starting a. We're starting b. And we're starting c.
Now c throws that error, right.
So c is gone. Notice how the ending c is never printed.
We catch the error right here on line 21.
And then we continue normally because we've handled the exception.
So we've got ending b and, then we come back here, and end a.
And finally, we finish off our main.
So this is stack unwinding. I encourage you to play around with this example,
put some throws and catches.
It's really the only way to really understand it is to play with it.
Put some try some catches, throw in different places and see what's happening.
And you'll be able to trace through this. And once you really understand what's going on,
you'll definitely understand exception handling and stack unwinding.
And it's critical that you understand that.
Because remember, things are going to get thrown off the stack, so you've got to be sure
that you're handling your resources correctly.