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.
- Learn the proper syntax and techniques to work with exceptions
- Understand inheritance in the context of Object-Oriented Programming
- Beginner coders, new to C++
- Developers looking to upskill by adding C++ to their CV
- College students and anyone studying C++
To get the most out of this course, you should have a basic understanding of the fundamentals of C++.
In the last lecture, we learned about re-throwing exceptions. This skill is important so that we know how to pass an exception through. Sometimes, multiple function calls and layers of code so that different levels of code can respond accordingly. In this lecture, we will learn about how to create our own Custom Exceptions, although we should make an effort to try and use the exceptions that are available as part of the built in hierarchy in C++. Sometimes it is useful or even necessary to be able to create and use our own types of exceptions, especially if they differ from the way we would handle other built-in or custom exceptions.
Earlier in this section, we briefly discussed inheritance, which is one of the three primary principles of object oriented programming. With inheritance, we can inherit code that is already working without having to rewrite it. This is one of the advantages of inheritance. As far as the exception hierarchy is concerned, we've seen a few examples of exceptions that inherit directly from exception or from one of its direct children such as logic error and runtime error. Now, let's create our own exception class type inheriting from the exception class itself. First, let's create a project called custom exceptions. Create a new project. Empty project and then call it CustomExceptions. Okay, All right, give it a minute.
And in this case we're only going to create two files, which I'll talk about in a second. So, we create the main file, C++, we'll call it main.CPP and then we're just going to create a header file for the AngrycatException, which is going to be for a class, but you'll see why we're only creating a .h file. 'Add New Item', 'Header' AngryCatException. All right. Make sure we spelled things right. Exception. Okay. Good. Now, this is because for our purposes the AngryCatException classes so simple. We won't go to the trouble of making a full implementation file. Some people prefer putting significantly simple implementations in a simple header file and not creating the full implementation file.
And that's okay. In our case, since we will be using inheritance, we will be inheriting essentially everything we need, in our case we only really need the what method and the constructor of the class that we extend. Let's fill in some of the code. We'll start with the code for the custom exception. So, we'll delete our #pragma once here because that is still non-standard. We'll call it ANGRY_CAT_EXCEPTION_H, #define ANGRY_CAT_EXCEPTION_H and then #endif. Let's include stdexcept using name space standard and we're going to use some inheritance here. So, it's going to inherit from runtime error. Okay. So, instead of the custom error inheriting from just directly from exception, let's use runtime error because we'll consider it a kind of runtime error.
All right. AngryCatException, but we could if we wanted to have a completely different category. No fun intended category, Meow. Okay. Not funny. All right. So, we could have a completely different category if we inherited from exception directly, but we'll use runtime exception. That's okay. Okay. For here, we're going to use the initialization list. So, runtime error. So, the runtime error class which represents runtime exceptions. So, "Made kitty angry." So, that's the very simple, we're going to keep the body actually empty because we don't need to do anything else. We're just passing on the data to the runtime error constructor. So, a little bit of explanation is required here.
First, again, there's a colon followed by public, followed by the name of the classes that we know about. In this case, it would be the class that we're inheriting from, there's only one this time. So, public runtime error, the colon between our custom class and the one that we're inheriting from is right here. So, without getting into too many details at this point we're expressing that we want to publicly inherit from the runtime error class, which is again, one of the two major top level children of the exception class itself. For our purposes, this means that ultimately our class will expose the public methods that we inherit from runtime error. The what method is of particular importance to us and that's one that we want exposed because it reflects the message that is stored to be displayed to the user or used for some other purpose. The question arises, where does this message come from? For our custom class, we have a single message that we pass in upon instansiation of the object. The message "Made kitty angry" is passed into the runtime error base class constructor using the syntax shown next to the AngryCatExceptions constructor involving a colon followed by runtime underscore error constructor, right here. So, now for main, let's use main. All right. In main, I didn't write the skeleton this time, but no biggie. No problem. Angry_Cat_Exception.h and again, we don't even have a CPP file so you don't have to worry about that. All right, inside here let's have int numTreats set to zero initially. And then we're going to print out, "How many treats do you want to feed kitty?" How many do you want to give to the cat and then you enter numTreats and then we have a try-catch. The catch this time instead of catching a runtime exception or some other exception we're catching our custom exception. So, Angry Cat Exception and note that if we wanted to also catch general runtime exceptions, since we're already inheriting from it with the AngryCatException, you should put the second catch underneath this one. Otherwise, this one would never be reached because if we have one above it that catches all runtime exceptions since because of inheritance, since AngryCatException is a runtime exception, it would be caught.
So, we don't need that right here. But that's just a note. Be very careful the order. Okay, All right, so we've got this. Oops. Okay, so in catch we're just going to print out "what" there we go. And then the try we're going to actually call a function called feed kitty. So, I might want to have at least the stubbed here numTreats. So, that's really what's going to happen. I'm going to go down here also and put feedKitty numTreats, here we go. And then feedKitty gets called in the try block inside of main. feedKitty pass it numTreats and obviously this one is going to be the one that actually could throw an exception. All right. So, if numTreats this is inside feedKitty, if numTreats is less than three we'll say, we want to throw an AngryCatException. My cat likes at least seven little treats. So, kitty is happy with numTreats, if it gets past the point. So, actually let's do this. I will say no that already has a space. So, we'll put treats and then end don't. So, if it gets past this, it means that we've given the kitty three or more treats. So, it'll say kitty is happy with and then however many treats we gave them. Right? pretty cool. So, to review in this file, we create a feed kitty function that takes a number of treats. If the cat gets less than three treats, she's going to be angry. Otherwise we simply print out that the kitty is happy with the treats and indicating the number of treats? All right now, inside the main function itself, we obtain input from the keyboard, an integer representing the number of treats that we're going to feed the kitty. We write code to catch the Angry Cat Exception and print out the message that it has. You'll recall that we set the message not directly since it's private in the exception ancestor class itself, but by passing it as an argument to the runtime error class constructor and then that will actually pass it to the exception class, and then exception can access the private data directly. Let's see what happens, when we run this program with some different input. So, let's go to debug. Start without debugging. How many treats do you want to feed kitty? Let's try five. Kitty is happy with five treats. Now, I notice there's a little space issue here so I'll fix that. Happy with. Okay. That's what I was being silly because I saw the space here and I didn't I forgot that I needed one there because we have a word or number in this case in between. All right, so five worked. We can see it again just to make sure it works. There we go kitty is happy with five treats and then let's try less than the three, which is the cut off. So, let's try two treats made kitty angry. All right. So, we can see our custom exception works great. Why might we even need a custom exception of creating one is so simple and doesn't really change much about it.
The answer is it helps us distinguish it from other types of exceptions. Although, we can try to use any of the exception types that already exist and probably should most of the time. Sometimes custom types are useful or even necessary and now you know how to create your own custom exception types. That's pretty awesome. And as you probably guessed, I have a challenge for you. Using this same project, I want you to modify the custom AngryCatException class, so that it has a second constructor that can set a custom message by passing it to the runtime error constructor like we did right here, but yours will actually take a parameter. Make sure to test it in the main file. Perhaps, you could modify the feed kitty function to throw a parameterised AngryCatException that says something that kitty still is not satisfied, but maybe not as angry as before. If you give her under a certain amount of treats. So, pause the video and give it your best shot. Come back when you're done or if you need some help. How did that go for you? Let's work on it together. So, inside of here, inside of the class AngryCatException.h. Let's put AngryCatException, Const, string reference err and then we're just going to pass it on to the runtime error that we're inheriting from. And since, that's compatible with what we're passing in there, we will don't need to do anything else in here. So, that's really all it is. How do we test this in main? Well, first of all we have to modify feedKitty. So, if the number of treats is less than three, then it's angry. But if the numTreats is less than six, maybe we also have an AngryCatException but this time I'm still not happy. Okay, now could we have put this seat right down here in an 'else'? Sure. But since these two throws break execution, it's not really necessary to put an 'else'. If we had other stuff to do beyond this and these did not break execution then it would be necessary to put an 'else' right here. All right, let's run it. We're going to test it with some different test cases here. How many treats do you want to feed kitty? Let's try two, that made kitty angry. All right, next one, give kitty four treats, I'm still not happy but not angry. Right, still not happy though. Now, here's the sweet spot with my cat. One of my cats like seven treats, kitty's happy with seven treats. Nice job everyone. We can see that we now allow the client code of our exception to use the default exception message with a no argument constructor call to AngryCatException or a parameterised constructor where they can specify a message. Nice job everyone. We can see that, we now allow the client code of our exception to use the default exception message with a no argument constructor called AngryCatException or a parameterised constructor where they can specify a custom message, the one that you wrote. In the next lecture, we will be looking at how to use the debugger, which is a very useful tool for helping us find errors and see values in memory. I'll see you there.