Ever had your C++ program crash unexpectedly? It’s frustrating, right?
Imagine you’ve written hundreds of lines of code only to have it stop working due to a tiny error.
That’s where exception handling in C++ comes in. It’s our way of managing errors and ensuring our program runs smoothly.
Exception handling in C++ is a way to manage errors gracefully. It catches errors as they happen and lets us decide what to do next. Instead of our program crashing, we can handle the problem gracefully. This process ensures that our codes remain robust and can recover from unexpected situations.
This blog will walk you through how to use exception handling in C++ with easy-to-follow examples.
Best Practices for Implementing Exception Handling in C++ Code
Wondering how to make your exception handling better? Here are some tips:
Keep normal and error-handling code separate: This improves readability.
Use specific exceptions: Handle different error types appropriately.
Clean up resources: Ensure you release resources like memory and file handles.
Always catch exceptions by reference: This avoids slicing and handles polymorphic exceptions correctly.
Document your exceptions: Make it clear which exceptions your functions might throw.
Use RAII (Resource Acquisition Is Initialization): This ensures resources are released properly by tying them to the lifespan of objects.
Get curriculum highlights, career paths, industry insights and accelerate your technology journey.
Download brochure
Limitations and Challenges of Exception Handling in C++
Exception handling is powerful, but it has its limits:
Overhead: It can slow down your program.
Complexity: Misuse can make your code hard to read and maintain.
No enforcement: C++ doesn’t enforce exception handling, leading to inconsistent practices.
Unpredictable control flow: Exceptions can make the flow of control hard to follow.
Resource leaks: If not handled properly, exceptions can lead to resource leaks.
Not suitable for all errors: Sometimes, error codes might be a better choice, especially for simple functions.
Importance of Exception Handling in Modern C++ Development
Why bother with exception handling? It keeps our programs running. Without it, any error can stop our program dead in its tracks.
With exception handling, we can:
Keep the program running: Even if an error occurs, we can manage it and continue.
Improve readability: Separate error handling from the main code, making it cleaner.
Make debugging easier: Identify exactly where things went wrong.
For example, when dealing with user inputs, we can’t always predict what the user will enter. Exception handling helps manage unexpected inputs without crashing the program.
Types of Exceptions in C++: Compile-Time vs. Run-Time
Let’s break down the types of errors we might encounter.
Compile-Time Errors
These are errors detected by the compiler before the program runs.
They include:
Syntax errors: Missing semicolons or incorrect keywords.
Type errors: Mismatched data types.
Compile-time errors are usually easier to fix because the compiler points them out.
Run-Time Errors
These occur while the program is running. They are harder to predict and include:
Division by zero
Invalid memory access
We manage run-time errors with exception handling. They are unpredictable and can cause the program to crash if not handled properly.
Keywords in Exception Handling: try, catch, and throw
Now, let’s dive into the tools we use for exception handling in C++: try, catch, and throw.
The try block contains code that might throw an exception. Think of it as a test zone. If something goes wrong, the exception is thrown to a catch block.
Catching Exceptions with the catch Block
The catch block catches exceptions thrown by the try block. It allows us to handle errors without crashing the program.
Throwing Exceptions Using the throw Keyword
The throw keyword is used to throw an exception. When the throw statement is executed, the current function is terminated, and control is transferred to the nearest catch block.
Example:
#include<iostream>
using namespace std;
int main() {
int y = 50;
cout << "Entering the try block." << endl;
try {
cout << "Within the try block." << endl;
// Throwing an exception if the value of y is less than or equal to 75.
if (y <= 75) {
// Throwing the value of y as an exception as y is less than or equal to 75.
throw y;
// This line will not be executed because of the throw statement above.
cout << "This message will not be printed." << endl;
}
}
// Catching the value of y thrown by the throw keyword from the try block.
catch (int val) {
cout << "Exception caught! Value of y: " << val << endl;
}
cout << "Continuing after the catch block." << endl;
return 0;
}
Explanation:
Output:
Entering the try block.
Within the try block.
Exception caught! Value of y: 50
Continuing after the catch block.
Using the try Block to Test Code for Exceptions
Worried about your C++ program crashing at runtime? You’re not alone.
Many developers face the same issue. Exception handling in C++ is our safety net. It helps us catch and handle errors gracefully.
The try block is where we place the code that might throw an exception. Think of it as a safety zone. If something goes wrong, control moves to the catch block.
Here’s a simple example. Let’s write a program to divide two numbers. We’ll ask the user for input and use a try block to handle potential errors.
#include <iostream>
#include <stdexcept>
double divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
return static_cast<double>(a) / b;
}
int main() {
int num1, num2;
std::cout << "Enter two numbers to divide: ";
std::cin >> num1 >> num2;
try {
double result = divide(num1, num2);
std::cout << "Result: " << result << std::endl;
}
catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 0;
}
In this code, the divide function checks if the denominator is zero. If it is, the function throws a runtime_error. The try block catches this error and prints a message.
Catching Exceptions with the catch Block
Caught in a loop trying to figure out where your program went wrong? Catch blocks are your friend. They catch exceptions thrown by the try block and handle them.
The catch block follows the try block. It catches exceptions based on their type. If an exception matches the type in the catch block, the code inside runs.
Let’s extend our previous example to catch different types of exceptions.
#include <iostream>
#include <stdexcept>
double divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
return static_cast(a) / b;
}
int main() {
int num1, num2;
std::cout << "Enter two numbers to divide: "; std::cin >> num1 >> num2;
try {
double result = divide(num1, num2);
std::cout << "Result: " << result << std::endl;
}
catch (const std::runtime_error& e) {
std::cerr << "Runtime Error: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "Unknown Error occurred." << std::endl;
}
return 0;
}
Here, we’ve added a generic catch block using ‘…’. It catches any exception not specifically handled by the previous catch blocks.
Throwing Exceptions Using the throw Keyword
Ever wonder how to signal an error in your code? That’s where the throw keyword comes in. It lets us throw an exception when something goes wrong.
When we throw an exception, we stop the current function and move control to the nearest catch block. This helps us handle errors immediately.
Let’s see another example. This time, we’ll check for negative numbers.
#include <iostream>
#include <stdexcept>
void checkPositive(int num) {
if (num < 0) {
throw std::invalid_argument("Negative number not allowed");
}
std::cout << "Number is positive: " << num << std::endl;
}
int main() {
int num;
std::cout << "Enter a number: ";
std::cin >> num;
try {
checkPositive(num);
}
catch (const std::invalid_argument& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
In this code, if checkPositive receives a negative number, it throws an invalid_argument exception. The catch block handles this exception.
Standard Exception Classes in C++ and Their Use Cases
Confused about which exceptions to use? C++ provides several standard exceptions. These help us handle common errors efficiently.
Here are some common standard exceptions:
Using these standard exceptions makes our code more readable and maintainable. Let’s see a table summarizing these exceptions:
Exception Type
Description
Example Use Case
std::exception
Base class for all standard exceptions.
Catch-all handler
std::bad_alloc
Thrown by new when memory allocation fails.
new operator failure
std::bad_cast
Thrown by dynamic_cast when it fails.
dynamic_cast failure
std::bad_exception
Used to handle unexpected exceptions.
Used in exception handling
std::bad_typeid
Thrown by typeid when applied to a dereferenced null pointer.
Dereferencing null pointers
std::logic_error
Base class for errors in program logic.
Base class for logical errors
std::domain_error
Thrown when a mathematically invalid domain is used.
Math functions
std::invalid_argument
Thrown when an invalid argument is passed.
Invalid function arguments
std::length_error
Thrown when a length exceeds its maximum allowable size.
String length errors
std::out_of_range
Thrown when an index is out of range.
Array index errors
std::runtime_error
Base class for errors detected during runtime.
General runtime errors
std::overflow_error
Thrown when an arithmetic overflow occurs.
Integer overflow
std::range_error
Thrown when a value is out of range.
Container index out of bounds
std::underflow_error
Thrown when an arithmetic underflow occurs.
Floating-point underflow
Defining Custom Exceptions in C++
Ever needed to create a specific error message? Custom exceptions are the answer. They let us define unique error types tailored to our needs.
To define a custom exception, we inherit from ‘std::exception’ and override the ‘what’ method.
Creating custom exceptions helps us manage specific errors in a more descriptive way.
Here’s an example. We’ll create a custom exception for invalid user input.
In this code, ‘InvalidInputException’ is a custom exception. If ‘checkInput’ receives a negative number, it throws this exception. The catch block then handles it.
Examples of Exception Handling in C++
Struggling to figure out how to handle errors in your C++ code?
Let’s dive into some practical examples. These will help you understand how to use exception handling effectively.
Example 1: Handling Division by Zero
Let’s start with a common issue – dividing by zero. This often causes programs to crash. We can handle this using exception handling.
#include <iostream>
#include <stdexcept>
double divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
return static_cast<double>(a) / b;
}
int main() {
int num1, num2;
std::cout << "Enter two numbers to divide: ";
std::cin >> num1 >> num2;
try {
double result = divide(num1, num2);
std::cout << "Result: " << result << std::endl;
}
catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
In this example, if the second number is zero, a runtime_error is thrown and caught, preventing a crash.
Example 2: Managing Invalid Array Index Access
Accessing an array out of its bounds can lead to serious issues. Let’s see how to handle this error.
#include <iostream>
#include <stdexcept>
int getElement(int* arr, int size, int index) {
if (index >= size) {
throw std::out_of_range("Index out of range");
}
return arr[index];
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int index;
std::cout << "Enter index to access: ";
std::cin >> index;
try {
int element = getElement(arr, 5, index);
std::cout << "Element at index " << index << ": " << element << std::endl;
}
catch (const std::out_of_range& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
If the user enters an index out of the array’s range, the out_of_range exception is thrown and handled.
Example 3: Custom Exception for Invalid User Input
Sometimes, standard exceptions aren’t enough. Let’s create a custom exception for specific errors.
Exception handling in C++ aids in error management and maintains the smooth operation of our programs.
We have covered the fundamentals of C++ exception handling in this blog. We started by comprehending the significance of exception handling and how it keeps program execution flowing smoothly.
We can gracefully manage errors by utilizing try, catch, and throw. Standard exceptions cover common mistakes, but custom exceptions can handle special instances.
By mastering these methods, we can make sure that our C++ programs are dependable, stable, and easy to use.
FAQs
Why is exception handling in C++ important?
Exception handling enables our program to effectively manage the runtime errors without disturbing the normal program flow.
Is it possible to catch multiple exceptions in a single try block?
Yes, it is possible to have more than one catch block to manage the exceptions thrown from a single try block.
What is a custom exception, and how do we create one in C++?
A custom exception is a user-defined exception class. We create one by inheriting from the ‘std::exception’ class and overriding the ‘what’ method.
What are standard exceptions in C++?
Standard exceptions are predefined in the C++ standard library. Examples include ‘std::runtime_error’, ‘std::out_of_range’, and ‘std::bad_alloc’.
Why should we separate normal code from error-handling code?
Separating normal code from error-handling code improves readability and maintainability, making the code easier to understand and manage.
Hero Vired is a leading LearnTech company dedicated to offering cutting-edge programs in collaboration with top-tier global institutions. As part of the esteemed Hero Group, we are committed to revolutionizing the skill development landscape in India. Our programs, delivered by industry experts, are designed to empower professionals and students with the skills they need to thrive in today’s competitive job market.