C++ Try Catch
Introduction
Exception handling is a crucial aspect of writing robust and reliable C++ programs. It provides a structured way to detect and respond to runtime errors or exceptional situations without disrupting the normal flow of the program. The primary mechanism for handling exceptions in C++ is the try-catch block.
In this tutorial, we'll explore how to use try-catch blocks in C++ to effectively handle exceptions, making your code more resilient and maintainable.
What is a Try-Catch Block?
A try-catch block in C++ consists of two main components:
- Try Block: A block of code that might throw an exception.
- Catch Block: A block of code that handles the exception if one is thrown in the try block.
The basic syntax looks like this:
try {
// Code that might throw an exception
} catch (ExceptionType e) {
// Code to handle the exception
}
Basic Try-Catch Example
Let's start with a simple example to demonstrate how try-catch works:
#include <iostream>
using namespace std;
int main() {
try {
// This will throw an int exception
throw 20;
} catch (int e) {
cout << "Exception caught: " << e << endl;
}
cout << "Program continues after exception handling" << endl;
return 0;
}
Output:
Exception caught: 20
Program continues after exception handling
Explanation:
- Inside the try block, we explicitly throw an integer exception using the
throw
keyword. - The execution immediately jumps to the catch block that can handle an integer exception.
- The catch block catches the exception, displays a message with the exception value (20).
- After the catch block executes, the program continues normally.
Multiple Catch Blocks
A try block can be followed by multiple catch blocks to handle different types of exceptions:
#include <iostream>
#include <string>
using namespace std;
int main() {
try {
int choice;
cout << "Enter 1 for int exception or 2 for string exception: ";
cin >> choice;
if (choice == 1) {
throw 100;
} else if (choice == 2) {
throw string("String exception");
} else {
throw 3.14;
}
}
catch (int e) {
cout << "Integer exception caught: " << e << endl;
}
catch (string s) {
cout << "String exception caught: " << s << endl;
}
catch (...) {
cout << "Default exception handler: Unknown exception type caught" << endl;
}
cout << "Program continues after exception handling" << endl;
return 0;
}
Example Outputs:
For input 1
:
Enter 1 for int exception or 2 for string exception: 1
Integer exception caught: 100
Program continues after exception handling
For input 2
:
Enter 1 for int exception or 2 for string exception: 2
String exception caught: String exception
Program continues after exception handling
For input 3
:
Enter 1 for int exception or 2 for string exception: 3
Default exception handler: Unknown exception type caught
Program continues after exception handling
Key Points:
- The catch blocks are checked in order from top to bottom.
- Only the first matching catch block is executed.
- The
catch(...)
syntax is a catch-all handler that catches any type of exception.
Exception Objects
In C++, you can throw and catch objects, not just basic types. This allows for more detailed exception information.
#include <iostream>
#include <stdexcept>
using namespace std;
double divide(double a, double b) {
if (b == 0) {
throw runtime_error("Division by zero attempted!");
}
return a / b;
}
int main() {
double a, b;
cout << "Enter two numbers (a and b) for division (a/b): ";
cin >> a >> b;
try {
double result = divide(a, b);
cout << "Result of " << a << "/" << b << " = " << result << endl;
}
catch (const runtime_error& e) {
cout << "ERROR: " << e.what() << endl;
cout << "Please try again with a non-zero denominator." << endl;
}
cout << "Program execution continues..." << endl;
return 0;
}
Example Outputs:
For input 10 2
:
Enter two numbers (a and b) for division (a/b): 10 2
Result of 10/2 = 5
Program execution continues...
For input 10 0
:
Enter two numbers (a and b) for division (a/b): 10 0
ERROR: Division by zero attempted!
Please try again with a non-zero denominator.
Program execution continues...
Important Notes:
- The C++ Standard Library provides several exception classes in the
<stdexcept>
header. - We use
const exception& e
to catch the exception by reference, which is more efficient. - The
what()
method returns the error message associated with the exception.
Standard Exception Classes
C++ provides standard exception classes in the <stdexcept>
header:
#include <iostream>
#include <stdexcept>
#include <vector>
using namespace std;
int main() {
try {
// May throw out_of_range
vector<int> vec(5);
cout << "Accessing element at index 10" << endl;
cout << vec.at(10) << endl; // This will throw an exception
}
catch (const out_of_range& e) {
cout << "Out of Range exception: " << e.what() << endl;
}
catch (const exception& e) {
cout << "Standard exception: " << e.what() << endl;
}
return 0;
}
Output:
Accessing element at index 10
Out of Range exception: vector::_M_range_check: __n (which is 10) >= this->size() (which is 5)
Here's a hierarchy of some common standard exception classes:
Nested Try-Catch Blocks
You can nest try-catch blocks within each other:
#include <iostream>
using namespace std;
int main() {
try {
cout << "Outer try block" << endl;
try {
cout << "Inner try block" << endl;
throw 100; // This will be caught by the inner catch
}
catch (int e) {
cout << "Inner catch: " << e << endl;
throw; // Rethrow the exception to the outer block
}
}
catch (int e) {
cout << "Outer catch: " << e << endl;
}
cout << "Program continues" << endl;
return 0;
}
Output:
Outer try block
Inner try block
Inner catch: 100
Outer catch: 100
Program continues
Key Points:
- When an exception is thrown, the program looks for the nearest enclosing try-catch block.
- The
throw;
statement without a value rethrows the current exception.
Real-World Example: File Handling with Exceptions
Let's look at a practical example of using try-catch for file operations:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void readFile(const string& filename) {
ifstream file;
try {
file.open(filename);
if (!file.is_open()) {
throw runtime_error("Could not open file: " + filename);
}
string line;
cout << "File contents:" << endl;
while (getline(file, line)) {
cout << line << endl;
}
file.close();
}
catch (const exception& e) {
cout << "ERROR: " << e.what() << endl;
// Always close the file even if an exception occurs
if (file.is_open()) {
file.close();
}
// You might want to rethrow or handle differently depending on your application
}
}
int main() {
string filename;
cout << "Enter a filename to read: ";
cin >> filename;
readFile(filename);
cout << "Program execution completed." << endl;
return 0;
}
Example Output (when file doesn't exist):
Enter a filename to read: nonexistent.txt
ERROR: Could not open file: nonexistent.txt
Program execution completed.
This example demonstrates how exception handling can be used to handle file operation failures gracefully.
Best Practices for Using Try-Catch in C++
-
Only Catch What You Can Handle: Don't catch exceptions you don't know how to handle. Let them propagate to a higher level that can handle them.
-
Use Specific Exception Types: Catch the most specific exception type first, followed by more general types.
-
Catch Exceptions by Reference: Use
catch (const ExceptionType& e)
instead ofcatch (ExceptionType e)
to avoid copying the exception object. -
Resource Management: Use RAII (Resource Acquisition Is Initialization) with smart pointers and containers to automatically manage resources even when exceptions occur.
-
Don't Use Exceptions for Normal Flow Control: Exceptions should be for exceptional cases, not normal program flow.
-
Document Exceptions: Document which exceptions your functions might throw to inform other developers.
// Good practice: documenting exceptions
/**
* Divides two numbers.
* @param a The numerator
* @param b The denominator
* @return The result of a/b
* @throws std::runtime_error if b is zero
*/
double divide(double a, double b);
Summary
Try-catch blocks are a powerful feature in C++ for handling exceptions:
- We use
try
to enclose code that might throw exceptions. - We use
catch
to handle exceptions if they occur. - Multiple catch blocks can handle different types of exceptions.
- C++ provides standard exception classes in the
<stdexcept>
header. - We can create custom exception classes by inheriting from
std::exception
. - Exception handling helps make programs more robust by providing structured ways to handle errors.
By learning to use try-catch blocks effectively, you can create more resilient C++ programs that handle errors gracefully and maintain program stability even when things go wrong.
Exercises
- Write a program that uses a try-catch block to handle division by zero.
- Create a custom exception class that inherits from
std::exception
and use it in a try-catch block. - Write a program that demonstrates the use of nested try-catch blocks with rethrowing exceptions.
- Create a program that reads integers from a file and handles both file opening exceptions and format exceptions (like when a non-integer is in the file).
- Implement a function that validates user input for a range of values and throws appropriate exceptions when the input is invalid.
Additional Resources
- C++ Reference: Exception Handling
- C++ Standard Library Exceptions
- Best Practices for Exception Handling in C++
- Book: "Effective C++" by Scott Meyers (Item 29: Strive for exception-safe code)
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)