Lesson 5
Throwing Exceptions in C++
Lesson Introduction

Hello! Today, we will learn about throwing exceptions in C++, which is vital for writing robust programs. Think of exceptions like traffic signals. When driving, if you see a red light, you stop to avoid collisions—a clear sign something needs attention. Similarly, exceptions signal problems in our code, allowing us to handle errors smoothly. By the end, you'll know how to throw and catch exceptions, making your programs more resilient.

Throwing Exceptions

To "throw" an exception means to signal that something unusual has occurred. In C++, we use the throw keyword to do this. It’s like raising your hand to say, "Wait, there's a problem!"

Here’s a simple example. Imagine you ask a vending machine for a snack that's out of stock. The machine needs to inform you that it can't complete your request:

C++
1#include <iostream> 2#include <stdexcept> // For std::runtime_error 3 4 5int main() { 6 int stock = 0; 7 if (stock == 0) { 8 throw std::runtime_error("Out of stock!"); 9 } 10 return 0; 11}

Output:

1terminate called after throwing an instance of 'std::runtime_error' 2 what(): Out of stock!
Catching Exceptions: Part 1

Imagine we have a function that can throw an error:

C++
1#include <iostream> 2#include <stdexcept> 3 4void give_order(int order, int stock) { 5 if (order <= stock) { 6 std::cout << "Here is your order!" << std::endl; 7 } else { 8 throw std::runtime_error("Out of stock!"); 9 } 10} 11 12int main() { 13 give_order(1, 10); // Here is your order! 14}

In this case, everything is fine, and the function is successfully executed. But if we call it like this, it will throw an error:

C++
1int main() { 2 give_order(5, 0); 3 // terminate called after throwing an instance of 'std::runtime_error' 4 // what(): Out of stock! 5}

This way, our function is designed to warn the program with an error that something went wrong. However, we don't want the program to terminate every time this error appears. Let's learn how to handle it.

Catching Exceptions: Part 2

In C++, we can handle exceptions with a special try-catch block. The program tries to execute the code inside the try, and in case it fails with an exception, executes the code inside the catch.

Let’s modify our example:

C++
1int main() { 2 int order = 5; 3 int stock = 3; 4 5 try { 6 give_order(order, stock); 7 } catch (const std::runtime_error& e) { 8 std::cout << "Error: " << e.what() << std::endl; 9 } 10 11 return 0; 12}

When the give_order function is called with order = 5 and stock = 3, it will throw a std::runtime_error because the order exceeds the stock.

The catch block catches exceptions of type std::runtime_error. If the exception is caught, it prints the error message using e.what() where e is the exception object caught. This prevents the program from crashing and allows you to handle the error however you want.

This way, your program continues to run and provides a meaningful handling of an error.

Catching Multiple Exceptions: Part 1

Sometimes, different types of exceptions can occur, and it’s essential to handle each type uniquely. In C++, you can catch multiple exceptions by adding multiple catch blocks. Each catch block handles a specific type of exception.

Here’s a meaningful real-life example. Imagine a bank application where you need to process a transaction. Different errors can occur during the process, such as insufficient funds or an invalid account:

C++
1#include <iostream> 2#include <stdexcept> 3 4void process_transaction(double amount, double balance, bool account_valid) { 5 if (!account_valid) { 6 throw std::invalid_argument("Invalid account."); 7 } 8 if (amount > balance) { 9 throw std::runtime_error("Insufficient funds."); 10 } 11 std::cout << "Transaction processed successfully!" << std::endl; 12}

The process_transaction function can throw either a std::invalid_argument if the account is invalid or a std::runtime_error if there are insufficient funds.

Catching Multiple Exceptions: Part 2
C++
1int main() { 2 double amount = 50.0; 3 double balance = 50.0; 4 bool account_valid = true; 5 6 try { 7 process_transaction(amount, balance, account_valid); 8 } catch (const std::invalid_argument& e) { 9 std::cout << "Error: " << e.what() << std::endl; 10 } catch (const std::runtime_error& e) { 11 std::cout << "Error: " << e.what() << std::endl; 12 } 13 14 return 0; 15}

In this code:

  1. In the main function, we try to call process_transaction with account_valid = true.
  2. If a std::invalid_argument is thrown, it is caught by the first catch block.
  3. If a std::runtime_error is thrown, it is caught by the second catch block.

This method allows you to handle different types of exceptions separately, providing more specific error management in your programs. In this case, the program will execute successfully and print out "Transaction processed successfully!". Let's look at other options.

Catching Multiple Exceptions: Part 3

Firstly, it is possible that the account is not valid:

C++
1int main() { 2 double amount = 50.0; 3 double balance = 50.0; 4 bool account_valid = false; // Not valid! 5 6 try { 7 process_transaction(amount, balance, account_valid); 8 } catch (const std::invalid_argument& e) { // This exception is caught! 9 std::cout << "Error: " << e.what() << std::endl; // Error: Invalid account. 10 } catch (const std::runtime_error& e) { 11 std::cout << "Error: " << e.what() << std::endl; 12 } 13 14 return 0; 15}

In this case, the first catch block works.

Catching Multiple Exceptions: Part 4

Secondly, it is possible that there are not enough funds on the balance:

C++
1int main() { 2 double amount = 50.0; 3 double balance = 25.0; // Not enough! 4 bool account_valid = true; 5 6 try { 7 process_transaction(amount, balance, account_valid); 8 } catch (const std::invalid_argument& e) { 9 std::cout << "Error: " << e.what() << std::endl; 10 } catch (const std::runtime_error& e) { // This exception is caught! 11 std::cout << "Error: " << e.what() << std::endl; // Error: Insufficient funds. 12 } 13 14 return 0; 15}

In this case, the second catch block works.

Catching Multiple Exceptions: Part 5

Lastly, there can be the both problems at once:

C++
1int main() { 2 double amount = 50.0; 3 double balance = 25.0; // Not enough! 4 bool account_valid = false; // Not valid! 5 6 try { 7 process_transaction(amount, balance, account_valid); 8 } catch (const std::invalid_argument& e) { // This exception is caught! 9 std::cout << "Error: " << e.what() << std::endl; // Error: Invalid account. 10 } catch (const std::runtime_error& e) { 11 std::cout << "Error: " << e.what() << std::endl; 12 } 13 14 return 0; 15}

In this case, the first catch block will work as it is the first one. The second catch block won't be executed.

Lesson Summary

Great job! Today, you've learned how to throw and catch exceptions in C++. Just like having a good set of tools helps you fix things, knowing how to handle exceptions helps you fix problems in your code more smoothly.

To recap:

  • Throwing exceptions signals that something has gone wrong, using the throw keyword.
  • Catching exceptions with try and catch blocks helps you manage these errors.

Now it’s time to put what you’ve learned into practice! You’ll move to hands-on coding exercises where you’ll throw, catch, and handle exceptions, making your programs more robust. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.