C++ Exception Handling
Learn C++ Exception Handling step by step with clear examples and exercises.
Why This Matters
Welcome to this in-depth guide on C++ Exception Handling! We'll dive into the world of error management, covering practical scenarios, real-life bugs, and interview-ready techniques. By the end of this lesson, you'll be well-equipped to handle runtime errors and abnormal conditions with ease.
Why This Matters
Exception Handling in C++ is crucial for maintaining program stability by preventing unexpected termination. It allows a program to continue execution smoothly even when faced with errors, ensuring that your code remains robust and reliable. In real-world programming scenarios, it's essential to be able to handle exceptions effectively to avoid crashes and ensure the best possible user experience.
Prerequisites
Before diving into C++ Exception Handling, you should have a solid understanding of:
- Basic C++ syntax (variables, functions, loops, etc.)
- Data structures like arrays and vectors
- Object-Oriented Programming (OOP) concepts (classes, objects, inheritance, etc.)
- Standard Template Library (STL) components such as iterators and algorithms
Core Concept
Exception Handling in C++ involves the use of try-catch blocks to handle runtime errors gracefully. The try block contains code that might throw an exception, while the catch block handles the exception if it occurs.
#include <iostream>
using namespace std;
int main() {
int n = 10;
int m = 0;
try {
if (m == 0) throw "Division by zero";
cout << "Answer: " << n / m;
} catch (const char* msg) {
cout << "Error: " << msg;
}
return 0;
}
In the above example, if m is zero, the program throws an exception containing the message "Division by zero". The catch block catches this exception and prints the error message instead of causing the program to crash.
Internal Working of try-catch Block
When an exception occurs:
- The runtime executes code inside the
tryblock. - If an exception is thrown, the remaining code inside the
tryblock is skipped. - The runtime searches for a matching
catchblock. - If found, the exception is handled.
- If no matching handler is found,
terminate()is called, which terminates the program abruptly. During this process, stack unwinding occurs and local objects are destroyed automatically.
throw Keyword
The throw keyword is used to explicitly throw an exception. For example:
void checkAge(int age) {
if (age < 18) throw "Age must be 18 or above";
}
int main() {
try {
checkAge(15);
} catch (const char* msg) {
cout << msg;
}
return 0;
}
In this example, the function checkAge() throws an exception if the input age is less than 18. The catch block in the main() function catches and prints the error message.
Exception Hierarchy
Most standard exceptions in C++ derive from the std::exception class. Types of Exceptions include:
- Built-in Exceptions (throwing primitive data types like int, char, or float)
- Standard Exceptions (a hierarchy of standard exception classes defined in `
and`) - Custom Exceptions (created when standard exceptions are insufficient)
Worked Example
Let's consider a simple example where we want to validate user input for a function that calculates the square root of a number. We'll use exception handling to handle invalid inputs gracefully.
#include <iostream>
#include <exception>
#include <cmath>
class InvalidInputException : public std::runtime_error {
public:
InvalidInputException(const char* msg) : std::runtime_error(msg) {}
};
double squareRoot(double number) {
if (number < 0) throw InvalidInputException("Invalid input: Number must be non-negative");
return sqrt(number);
}
int main() {
try {
double num = -1;
cout << "Square root of " << num << ": " << squareRoot(num) << endl;
} catch (const InvalidInputException& e) {
cerr << "Error: " << e.what() << endl;
}
return 0;
}
In this example, we've created a custom exception class InvalidInputException that derives from std::runtime_error. The function squareRoot() throws an instance of this exception if the input number is negative. In the main() function, we catch and print the error message when an exception occurs.
Common Mistakes
- Forgetting to include necessary headers (e.g., `
,`) - Not catching the correct type of exception (e.g., trying to catch a custom exception with a generic
catchblock) - Neglecting to handle exceptions, leading to program termination
- Improper use of the
throwkeyword (e.g., throwing non-exception objects) - Not providing meaningful error messages in custom exceptions
Practice Questions
- Write a function that checks if a string is a valid email address and throws an exception if it's not.
- Modify the
squareRoot()function to handle the case where the number to be squared is very large, causing overflow errors. - Create a custom exception class for handling memory allocation failures in your program.
FAQ
- Why should I use exceptions instead of error codes or assertions? Exceptions provide a more flexible and user-friendly way to handle errors, as they allow the program to continue execution even when an error occurs. Error codes and assertions, on the other hand, can lead to complex error handling logic and may cause the program to terminate abruptly if an error is encountered.
- Can I create my own exception classes in C++? Yes! You can create custom exception classes that inherit from
std::exceptionor any of its derived classes likestd::runtime_error. This allows you to provide more specific and meaningful error messages when exceptions are thrown. - What happens if an uncaught exception is not handled in a program? If an uncaught exception is not handled, the program will terminate abruptly with a call to
std::terminate(), which may result in lost data or unexpected behavior. To avoid this, it's essential to catch exceptions and handle them appropriately. - Is it possible to throw exceptions from constructors or destructors? No, you cannot throw exceptions directly from constructors or destructors because these functions are guaranteed to complete execution before the program terminates. However, you can throw exceptions from functions called within constructors or destructors.
- What is the difference between
std::runtime_errorandstd::logic_error? Bothstd::runtime_errorandstd::logic_errorare derived fromstd::exception, but they represent different types of errors.std::runtime_erroris used for runtime errors that occur during program execution, whilestd::logic_erroris used for logic errors, such as invalid arguments or illegal states in the program.