Module 13: Exception Handling
Chapter 13 • Advanced
Exception Handling
Exception handling allows programs to deal with errors gracefully instead of crashing. It's essential for writing robust, production-quality C++ code.
What are Exceptions?
Exceptions are runtime errors or unusual conditions that disrupt normal program flow. Instead of crashing, exceptions can be caught and handled.
Benefits:
- Error Recovery: Handle errors without crashing
- Clean Code: Separate error handling from business logic
- Resource Safety: Ensure cleanup even when errors occur
- Information Propagation: Pass error information up call stack
Exception Handling Syntax
Basic try-catch Block
try {
// Code that might throw exception
riskyFunction();
} catch (exception_type e) {
// Handle exception
cout << "Error: " << e.what() << endl;
}
Example
#include <iostream>
#include <stdexcept>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw runtime_error("Division by zero!");
}
return a / b;
}
int main() {
try {
int result = divide(10, 0);
cout << "Result: " << result << endl;
} catch (runtime_error& e) {
cout << "Caught: " << e.what() << endl;
}
return 0;
}
Standard Exception Classes
C++ provides standard exception hierarchy:
exception
├── logic_error
│ ├── invalid_argument
│ ├── domain_error
│ ├── length_error
│ └── out_of_range
├── runtime_error
│ ├── range_error
│ ├── overflow_error
│ ├── underflow_error
│ └── system_error
└── bad_alloc (from new)
Using Standard Exceptions
#include <stdexcept>
// Invalid argument
if (value < 0) {
throw invalid_argument("Value must be non-negative");
}
// Out of range
if (index >= size) {
throw out_of_range("Index out of bounds");
}
// Runtime error
if (fileNotFound) {
throw runtime_error("File not found");
}
Multiple catch Blocks
Handle different exception types:
try {
// Code that might throw
} catch (invalid_argument& e) {
// Handle invalid argument
} catch (out_of_range& e) {
// Handle out of range
} catch (exception& e) {
// Handle any other exception
}
Order Matters: More specific exceptions first, general last.
Exception Propagation
Exceptions propagate up the call stack until caught:
void function3() {
throw runtime_error("Error in function3");
}
void function2() {
function3(); // Exception propagates
}
void function1() {
try {
function2();
} catch (runtime_error& e) {
cout << "Caught: " << e.what() << endl;
}
}
Creating Custom Exceptions
Derive from standard exception classes:
class MyException : public runtime_error {
public:
MyException(const string& msg) : runtime_error(msg) {}
};
// Usage
throw MyException("Custom error message");
Exception Specifications (Deprecated)
Old Style (C++98):
void function() throw(runtime_error); // Deprecated!
Modern Style (C++11):
void function() noexcept; // No exceptions
void function() noexcept(false); // May throw
RAII and Exception Safety
RAII (Resource Acquisition Is Initialization) ensures resources are cleaned up even when exceptions occur.
Without RAII (Problem)
void badFunction() {
int* ptr = new int(10);
riskyOperation(); // If this throws, memory leak!
delete ptr;
}
With RAII (Solution)
void goodFunction() {
unique_ptr<int> ptr = make_unique<int>(10);
riskyOperation(); // If this throws, ptr automatically deleted!
}
Exception Safety Guarantees
- Basic Guarantee: No resource leaks, valid state
- Strong Guarantee: Operation succeeds or state unchanged
- No-throw Guarantee: Never throws exceptions
noexcept Specifier
Mark functions that don't throw:
void safeFunction() noexcept {
// This function promises not to throw
}
// Conditional noexcept
template <typename T>
void swap(T& a, T& b) noexcept(noexcept(a.swap(b))) {
a.swap(b);
}
Best Practices
- ✅ Use exceptions for exceptional conditions
- ✅ Catch by reference (not by value)
- ✅ Use specific exception types when possible
- ✅ Don't throw from destructors (undefined behavior)
- ✅ Use RAII for resource management
- ✅ Document exceptions in function comments
- ✅ Catch specific exceptions first, general last
- ✅ Re-throw with `throw;` to preserve stack trace
Common Patterns
Pattern 1: Resource Guard
class FileGuard {
FILE* file;
public:
FileGuard(const char* name) : file(fopen(name, "r")) {
if (!file) throw runtime_error("Cannot open file");
}
~FileGuard() { if (file) fclose(file); }
FILE* get() { return file; }
};
Pattern 2: Exception Wrapper
template <typename Func>
auto safeCall(Func f) {
try {
return f();
} catch (exception& e) {
cout << "Error: " << e.what() << endl;
throw; // Re-throw
}
}
Common Mistakes
- ❌ Catching by value instead of reference
- ❌ Catching
exceptionbefore specific types - ❌ Throwing from destructors
- ❌ Swallowing exceptions silently
- ❌ Using exceptions for control flow
- ❌ Not cleaning up resources in catch blocks
- ❌ Forgetting to re-throw when needed
Exception vs Error Codes
| Aspect | Exceptions | Error Codes |
|---|---|---|
| **Performance** | Slower (when thrown) | Fast |
| **Visibility** | Must be handled | Easy to ignore |
| **Information** | Rich (what(), type) | Limited |
| **Control Flow** | Automatic propagation | Manual checking |
| **Use Case** | Exceptional conditions | Expected errors |
Next Module
In Module 14, we'll learn about File I/O - reading and writing files in C++!
Hands-on Examples
Basic Exception Handling
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw runtime_error("Division by zero is not allowed!");
}
return a / b;
}
int getElement(int arr[], int size, int index) {
if (index < 0 || index >= size) {
throw out_of_range("Index out of bounds!");
}
return arr[index];
}
int main() {
// Example 1: Division by zero
cout << "=== Example 1: Division ===" << endl;
try {
int result = divide(10, 0);
cout << "Result: " << result << endl;
} catch (runtime_error& e) {
cout << "Caught exception: " << e.what() << endl;
}
// Example 2: Array bounds
cout << "\n=== Example 2: Array Access ===" << endl;
int arr[] = {10, 20, 30, 40, 50};
try {
int value = getElement(arr, 5, 10); // Invalid index
cout << "Value: " << value << endl;
} catch (out_of_range& e) {
cout << "Caught exception: " << e.what() << endl;
}
// Example 3: Successful operation
cout << "\n=== Example 3: Success ===" << endl;
try {
int result = divide(20, 4);
cout << "Result: " << result << endl;
int value = getElement(arr, 5, 2);
cout << "Array value: " << value << endl;
} catch (exception& e) {
cout << "Caught: " << e.what() << endl;
}
return 0;
}Basic exception handling uses try-catch blocks. Code that might throw is in try block. catch blocks handle specific exception types. Exceptions propagate up call stack until caught. Use standard exception types from <stdexcept>. Always catch by reference, not by value.
Practice with Programs
Reinforce your learning with hands-on practice programs
Related Program Topics
Recommended Programs
Hello World
BeginnerA simple C++ program that prints "Hello World" on the screen and introduces basic C++ concepts for beginners.
Display Your Name
BeginnerBeginner-friendly C++ program that shows how to store your name in a string variable and print it on the screen.
User Input
BeginnerBeginner-friendly C++ program that teaches how to take input from the user using cin and display it on the screen.
Check for Even Odd
BeginnerProgram to check if a number is even or odd
Largest Among 3
BeginnerProgram to find the largest of three numbers