Rethrowing Exceptions

The course is part of this learning path

Start course
Overview
Difficulty
Intermediate
Duration
1h 51m
Students
18
Ratings
5/5
starstarstarstarstar
Description

In this course, we will discuss the topic of exceptions and debugging. We'll see that exceptions are objects representing exceptional situations and that these are usually problems in our code or in the environment in which our code is running. By using exception handling, we will know how to respond to problems when they arise.

Learning Objectives

  • Learn the proper syntax and techniques to work with exceptions
  • Understand inheritance in the context of Object-Oriented Programming

Intended Audience

  • Beginner coders, new to C++
  • Developers looking to upskill by adding C++ to their CV
  • College students and anyone studying C++

Prerequisites

To get the most out of this course, you should have a basic understanding of the fundamentals of C++.

 

Transcript

In the previous couple lectures, we've talked about both logic and runtime errors and what they usually represent and how we might respond to them. We've explored what exceptions do in general, and how we can use them to signal problems in our code, and how we can respond accordingly. We discussed how to catch exceptions that are thrown by code in different constructs that we use, that were written by someone else. And we also discussed how we can throw exceptions from our own functions when we want to signal that something has gone wrong. In addition to being able to throw exceptions in your own code, it is sometimes useful to be able to catch an exception and perform some sort of processing or displaying information to the user and then to rethrow the exception. This allows it to be caught again by a different segment of code somewhere else that may have to be able to perform its own processing.

This often occurs if you write some code that handles the exception in your own library function or a portion of the program that you're responsible for.

But to also pass the exception along so that whoever called it, whether it was you or someone else using your code, can handle the exception in their code as well. Let's jump right into an example. Let's create a project named RethrowFun1. 'Empty project', RethrowFun1. And in this case we're just going to create main.cpp. 'New item', main.cpp. All right. Let's write a couple of functions to demonstrate what's happening in addition to our main. So, we've got iostream, stdexcept, we've got main(). And now for the couple functions. So, processPositive(), it takes a num. Void doSomething also takes a num as well. So, let's fill in these functions with definitions. So, I'm going to take both of these, copy them and put them down here below main, and of course give them some bodies. So, we've got the body there, and of course on doSomething. Not a very creative name, but it's the point across. All right. There we go.

Now, for process, well actually let me think here. For doSomething, we are going to print some stuff out. And then for processPositive, we're also going to do the same thing. But, what's going to happen is processPositive is actually going to initially throw the exception, and then doSomething is going to rethrow the exception, so after it calls processPositive. All right. So, inside of processPositive, which is where our exception will originate, we're going to say, "Welcome to the positive integer processor!" Okay. And if the number is greater than or equal to zero, that means it's a positive or really non negative integer but we'll let some of the specificity slide here. And if it is greater than or equal to zero, we're going to say, "Good job! You passed in a positive num to processPositive." All right, that should be a lowercase e of course. And then over here in the else, we're going to say throw invalid argument, ("Negative number passed in!"). Now what we want to do in the doSomething function is we're going to put a try catch.

Catch is going to catch an invalid argument reference, and in this case in the try all we're doing is calling processPositive on the number. So, you're basically just passing it along. And then in here, we put ye do something could process that number. All right. What do we do in catch? In catch, we say "doSomething says there is a problem!" and then we do this. That is called rethrowing. All you do is you type the word throw, and if you don't specify throw error, it will do that by default. So, it will throw the error that you have here. So, it knows that the one that we are in the catch block four is the one that need to be rethrown. Now you could throw a completely different exception, but in this case we're re throwing the same exception. All right, so those two functions are done and now we need to go up to main and fill it in. So, we put int input. Oops, probably need a semicolon. Right? All right. And we're going to catch a and rather invalid_argument exception again.

So, same thing in the... Let's take care of the catch block this time. So, we'll put main says there is an error and then we're going to say err.what. Here we go. What about try? So, right here I'm going to ask the user to enter a number to process. So, user input is often the source of our problems because the user may not want to play by the games that we've set forth or rather the rules that we've set forth in the game. So, they may decide I'm going to put something completely different that they weren't asked for. So, cin >> input. All right, doSomething on input, and now we're going to put "Yay! main was able to completely ", I'm going to put a space there and will go to the next line. "Process the num!". So, if it gets passed to doSomething that means it does. It is able rather to process the number. All right. Looks pretty good to me. Now, we're going to run it first with a positive number and then with a negative number to see what happens.

Okay, so let's run this and test it out. 'Debug', 'Start Without Debugging', enter a number to process. So, we pass in 15, so it says enter a number of process we passed in 15, and it calls doSomething immediately. And eventually it's going to say, Yay! main was able to complete. That's the last thing that happens. But after we get the doSomething called on 15, let's doSomething 15 comes in. It calls processPositive. So, it goes in the processPositive. It's greater than or equal to zero. So it says Good job! You passed in a positive num to processPositive and then it breaks off of this and then goes to where it was called in doSomething again and then says Yay! doSomething could process that num and then it returns control back to main where it says your main was able to completely process the num. Now, what happens if we enter a negative number and trigger an exception? So, -20, welcome to the positive integer processor. So, we get that right here doSomething says there's a problem because we called that first it says there's a problem so we rethrow it.

And then what happens is we have the main says there's an error and then our error was negative number passed in. And where did that come from? That came from when we initiated the invalid argument. So, that actually caused the negative number passed in to be stored in the message that the exception stores. And then back up in main after it says main says there's a problem it prints out that error. So, err.what, awesome. So, we could see that there was an issue, doSomething which was called by the processPositive initially catches the exception and then prints its response. Obviously, as I've said, you can do far more complex things in the catch body when it's necessary, we just want to see what happens. So, we're just printing air information which is sufficient for our purposes. After dosomething catches the exception in response, it rethrows the exception. Remember the rethrowing that happens right here, that's the rethrowing inside of doSomething.

Then the catch block in main catches the exception, and responds and then finally prints out the original exception message using the what() member function, method of the exception class. So, that's pretty awesome. We have a lot of power at our disposal right here. Now I'd like to issue you a challenge using the same project. RethrowFun1, you're going to do the following. In the process positive function, if the parameter passed in is greater than 100, we will also consider it invalid. Create additional condition so that if this occurs, print "processPositive says the number is too big!" to the console and then throw an out of range exception with the message "Number cannot be greater than 100". And doSomething at a second catch block that catches out of range exceptions, print the message: "doSomething says the number is too big" and rethrow it as well. You can place your new catch block under the first catch block. This enables your code to handle multiple exception types and responds differently depending on the exception type. Finally, in main, add another catch block that catches out_of_range exceptions. Print the message "main says the number is too big." And then on another line, print the message from inside the exception object using the what() method. So, this one will stretch your abilities a little bit, but shouldn't be too hard I hope. Therefore, pause the video and come back when you're done or whenever you want to see my solution. How did that work out for you?

Did you complete the challenge? Let's do it together. All right. So, we can start at the doSomething if we want to, or we can start in processPositive. It doesn't really matter. But in processPositive, since that's kind of where everything originates at the bottom, we're going to do an extra little test here. So, before the greater than or equal to zero. Because if we put it after we're going to have a problem. If (num > 100) right here, and then we're going to add an else to this. Now you notice if I switch these, this would never be true because, I mean it's true, but this would catch it first and then it would skip over it. So, we need this one first. So, the order does matter. So, this one's going to say, "processPositive says the number is too big!" Alright. Then throw out_of_range exception("Number cannot be greater than 100!") Good. Now, what do we do about the doSomething? So, inside of doSomething, we have a single catch currently, but we can put the out_of_range catch underneath it. You could put it above in this case, it wouldn't really matter because invalid argument and out_of_reach are not in the same direct hierarchy. In other words, one isn't a child or descendant of the other, they're siblings essentially. We've got (const out_of_range& err) "doSomething says the number is too big!" << endl; And then we rethrow the exception. Very briefly as I mentioned, if these were in the same exact hierarchy, let's say for the sake of argument, that invalid_argument was a child of out_of_range, this would be the correct order. But if out_of_range was the child of invalid_argument, we would want to put invalid_argument later because you want to put the more specific exceptions earlier. Otherwise, they would never get caught because in that case an out_of_range would be, or is a invalid_argument. So, if you ever put catch const exception, for example, which is the top of the hierarchy, you can't really have anything past it because it'll never get there. Because if it sees, say an out_of_range exception, it will look and say, "Oh it catches exceptions? Well out_of_range is a regular exception too?" So, it'll satisfy it and then you'll never get the correct solution. But these are not descendants of one another, so we're good. Now, back up in main, let's put an additional catch so we put catch (const out_of_range reference& err). And we put "main says the number is too big!" << endl; Alright. And then, of course, err.what() << endl; Good. "Main says the number is too big!"err.what(). All right. Now, what we should do is run the application. So, we got to run it with a negative value, in range positive value, and then a value greater than 100 so we can see the different effects. So first of all, let's run this. And let's put -44. So that's our less than zero situation that we had earlier. So doSomething says there's a problem, there's an error, negative number passed in. So, that worked. Now what if it's within range? Debug, Start Without Debugging, what if we do 25? It'll say good job, Yay, and then Yay. So, it works fine. What if we do 500? See what happens with 500. ProcessPositive says the number is too big, doSomething says the number is too big, and main says the number is too big, and then we finally get the exception information that was passed in to the out_of_range exception ("Number cannot be greater than 100!") That's where this comes from. Excellent. Awesome job everyone. We've learned how to rethrow exceptions so that more than one segment of code can respond to different types of exceptions. That is an important skill when you start making your code more robust and fault tolerant and also flexible so that others who use your code can make choices based on the issues that arise. In the next lecture, we will learn about how to create our own custom exceptions. I'll see you there.

 

About the Author
Students
535
Courses
20
Learning Paths
4

John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.

Covered Topics