Runtime Errors

The course is part of this learning path

Start course
Overview
Difficulty
Intermediate
Duration
1h 51m
Students
13
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 last lecture, we discussed logic errors which are errors that are usually caused by logic problems in your code. In this lecture, we will discuss a very common type of error called runtime errors which are errors that are usually caused by circumstances beyond the programmer's direct control. Sometimes we say these types of errors are external errors, because they originate externally or outside of our programs. For example, even though we haven't talked about file input and output yet, you can imagine that if our program is being installed onto a person's own system, and our program expects there to be a file filled with data that it needs. That if the user moves or deletes that file or if there's a corruption during the download that damages the file that this could cause problems with our programs, right? And unlike the errors we saw before, which were largely a matter of bad code, runtime errors are generally out of our control. Another example is that there are some functions in C++ that interact with the operating system you're running code on. If one of the operating system services fails, or won't grant you some type of access permission to your program, then your program will fail also and that is out of your control too. While you can't control if someone removes a needed file or their operating system denies permissions or any number of other problems, you can often write defensive code that responds to such situations. Although a common example would be runtime errors related to file IO. As we mentioned before, we haven't done file IO yet. So instead, we're going to throw a special runtime error using an example. What I mean when I say that we will throw an error in our code is that we will write the code where the exception originates.

So, we will reach deeply into our class constructing abilities from the previous section, and we will write a humorous but demonstrative little class and program to demonstrate the runtime error class in action. We're going to be creating a Person class representing a person, a very simple model of a person at least. Pay super close attention to how we make this class and implement the exception, throwing, and handling. It will help you for a later challenge and even in the projects at the end of this section. Let's create a new Visual Studio project called PersonFun. So, 'Create a new project', 'Empty project', next. And we will call this PersonFun. And of course, we will create our three files. We've got the cpp file right here. We've got this will be Person.cpp and we need main.cpp and then we'll use Person.h as well. Okay, let's fill in the main.cpp skeleton code like we normally do.

So, include iostream and we're going to also probably need to deal with exceptions. So, we probably need stdexcept as well, and also our Person.h file. So, stdexcept, and then of course Person.h. int main() return 0; Nice. Back to our Person.h file, we will put our include guards in. #define PERSON_H #endif and then class Person with the public section and of course a private section as well. Private data, string name, we should include the string library. All right, now public, we need the constructor and we have string getName() const and void set(string name). Now let's fill out Person.cpp. #include "Person.h" We need to include stdexcept because we need it in here. namespace std. And now we have a couple of these methods we need to fill in. I'll go grab them over here and paste them in here. We can give them bodies. And we need of course, let see here we've included string in there so that should be okay.

Now we do need to put the name of the class and scope resolution operator as well. So, we've got this Person:: same thing on these. Excellent. Now for this one, I'm actually just going to call setName, and the name because we'll do the exception handling from setName, or did I put set or set name? We have a little issue here, meant to do setName. All right, No problem. So, we fixed that setName. Okay now it's fixed here. See, no big deal but it's sort of live television so you get to see me mess up too. All right, See it happens to the best of us. Now return name. And now what about setName? If the name is not equal to John, and this is an acceptable way to compare strings in C++. If you're from java, we had done this before. But in case I didn't mention it, if you're from java, you know that this would not be the way you would compare them.

PYou'd use the equals method. It's a little bit different. All right, as long as the name is not John, let's set the internal name equal to name. Else, we're going to do an indication of a runtime error or an example rather. So, the runtime errors message should be John? That guy is too ugly for an object. So, of course we're trying to be funny. All right, sort of. All right. So, it says, "John? that guy is too ugly for an object" and we're throwing this runtime error. This is a built-in error or exception that we have available to us. Note carefully the string we pass into the constructor. Okay. And then this string goes to setName, that goes in here. If it's not John, then we set it internally, the data internally. But if it is, we pass it along to the constructor of the runtime error. Excellent. Finally, let's fill in main.cpp. Now I could break this up into a separate function, but I think for our purposes, just to keep things simple, we'll do this. So, we have tried and catch what you've seen before and it's a runtime error. So, in this case though, we'll use const runtime_error & err, and we'll just print out err.what() again. You can do any number of things in here, but we're just keeping it very simple. So, we get used to what exceptions are and what they do. All right. Inside the try block, I'll put all these initialization is here. I'll have Bob created, person2 is Sally, Person person3 is the dreaded John, and then Person person4 is William. Okay. And now maybe we'll try printing out some names. So, person1.getName() You'll see the this will have some interesting results. getName. And of course, person3, and this needs to be endl instead of end. And then of course, person4. All right, excellent. So, we of course want to run it as well. 'Debug', 'Start without Debugging'. And we never even get to any of these because it catches the exception John? That guy is too ugly for an object? And that's the data that's stored in the internal message that is inherited from exception and that the what method that's inherited returns.

So, this is some of the magic of inheritance is that exception actually defines the what method but anything that inherits from exception has the what method as well. And as long as they don't touch it, they don't, they leave it alone, it will print out exactly what the exception class says, and that would be the message passed on to the constructor. So nice, we catch the exception since someone tried sending the name to John which our Person class doesn't allow. Obviously this is a silly exception, but it hopefully demonstrates that we can use our knowledge of what our classes should be and what they should do to guide if or how they throw exceptions. Another little syntactic feature you might encounter is the noexcept specifier applied to functions including member functions. In the C++ 11th standard, it is recommended that any function- global or member function, have the noexcept specifier listed after it if that function does not throw an exception. So, in Person.h and Person. cpp, just to demonstrate, let's add our noexcept to the end of getName. So, you'll notice it comes after const.

So, const noexcept. It's not required; it's just one of those things that a lot of people would prefer that we do, and it's part of the standard. I may or may not use it excessively throughout the course because it's just one more thing to remember, but you know what it means if you ever see it; it doesn't have to be foreign or scary or anything like that. All it is is it's saying, "Hey, this function or this method does not throw an exception." You notice we do not put it on setName because it does throw an exception. So, if the const specifier's present, then you have to put or you should put the noexcept specifier after const. So again, you might also see the throw exception specifier. So, in older code, instead of noexcept, you'll see throw like that. But in newer code, like C++ 11, you will see noexcept. And in fact, with C++ 20, throw in that context is deprecated; meaning it is actually removed rather than deprecated. So, it's actually not present. So, compilers that adhere to the C++ 20 standard may implement it for backwards compatibility, but the standard does not require it, so you can't depend on it being there.

So, use noexcept instead if you're going to use one of these. So again, well, it's not required. It's simply considered good programming convention to add the noexcept to the end of functions that don't throw exceptions. I'll use it sometimes while instructing to make sure you're aware of it. But I found that it intimidates students often more than it helps, especially when they first start learning the language. So, I bring it up to make you aware of it again,  but I may or may not use it as we go forward. Again, there's a lot of variety in code that we encounter. Lot's of it will be older code; lots of the code is in maintenance. Actually, the majority of money that goes towards software engineering is maintenance, not making and creating constructing new software. Sometimes you get to make a lot of fresh new code, but most of the time, you'll be maintaining older code. And it doesn't mean ancient, but a lot of the code is very old. So, where possible, adhering to different conventions and standards is a good idea. For this course, I make sure that you're aware of such standards but that you get more of the core knowledge and skills under the belt  before you even need to worry about some of these things.

I hope this helps you understand how we can throw our own exceptions and how the runtime error can be leveraged. Before we move on though, you guessed it; I'd like to issue you a challenge. I'd like you to create a project called DogFun and construct a class called Dog. The constructor will take the breed of the dog as a parameter. And you should make the class immutable  which if you recall means, once you set the data members of the class with the constructor, you don't modify them. So, no mutaters. For this class, you'll again add a little humor to it. And also tested out by creating a few different dog objects with different breeds. So, where do exceptions come in? I'm glad you asked. When you set the breed of the dog in the constructor, if the user tries to set the breed to poodle, then you will throw a runtime error with the message: "Poodle? That's not a real dog." I encourage you to test it out with different dog breeds: Boxer, Beagle, Golden Retriever, and, of course, Poodle. So, pause the video, and give this your best shot. Come back when you're done or if you need some help.

How did that go for you? Were you able to solve this challenge? Let's do it together. So, I'm going to close this project right here. We're going to go to 'Create a new project,'  'Empty Project.' We will call it DogFun. And you need three files: main.cpp, Dog.h, Dog.cpp. So, this time, I'll add the Dog.h file first, and then we have Dog.cpp, and, of course, main.cpp. So, main; add the skeleton #include stdexcept and, of course, Dog.h, 0. Now I'm going to organize these a little bit better. There we go, and then this one can go there. #ifndef DOG_H, #define DOG_H, #endif. using namespace std, class Dog. We have the public section and the private section. So, we're passing the breed, and we have, of course, rather getBreed. Not getGreed; that's silly. GetBreed() const. You could put noexcept, of course. Well, we'll just put const for now. And right here, #include Dog.h. We need the standard exception here. Dog::Dog, put the (string breed), return breed. I think that's it. Just two of them, yes. In the constructor, we can do this in the constructor. That's fine. If the breed is not equal to poodle, and we'll also say, we'll account for the capitalized version to Poodle. And we set the breed equal to the breed. If you didn't account for the capitalized version, don't kick yourself. It's not that big of a deal. Just wanted to be a little bit more thorough. So, the runtime error says, "Poodle? That's not a real dog!" So, it's just being funny.

Poodle owners are going to send me hate mail now. It's okay. I'm sure your dog is wonderful. Now, let's do try, and we have catch(const runtime_error & err). And, of course, in here, we just want to print out err.what(), and then try surrounds the quote dangerous code. So, we could try German Shepherd. It's very doubtful your code would look identical to mine. That's fine. So, we'll just keep it consistent. Capital G there. And theirDog as a poodle. I don't know who they are, but they're about to get a runtime error. So, now let's try to print these out. This might have been just going along our way, and theirDog.getBreed(). I made it a little too small there. Now let's run it. So, 'Debug,' 'Start Without Debugging.' And, of course, we get Poodle? That's not a real dog! And it stops execution there and jump straight to the catch. We don't get any of these printouts. So, we created a nice new class and we've thrown an exception from that class. We also wrote the appropriate code to catch the exception and responded by printing out what the error message contained. In our case, it's a funny little message about poodles not being real dogs. But in real industry grade software, you might print on an error to a file or send an alert signal to a security system or as the user to try sending the breed again because obviously they must have goofed if they thought a poodle was a real dog. So, I know I'm going to get some hate mail, but come on. That was fun, and it was a funny challenge. In the next lecture, we will discuss re-throwing exceptions. I'll see you there.

 

About the Author
Students
316
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