C++ Exception Handling
In C++, exception handling allows you to deal with runtime errors (exceptions) in a structured way, without crashing the program. Instead of checking for errors at every step, you can use exception handling to separate error-handling code from normal code flow, making the program more readable and maintainable.
C++ provides a set of keywords—try
, catch
, and throw
—to handle exceptions.
try
block: Contains the code that might throw an exception.throw
statement: Used to signal an exception.catch
block: Used to catch the exception thrown and handle it.
try {
// Code that might throw an exception
} catch (exceptionType1 e1) {
// Handle exception of type exceptionType1
} catch (exceptionType2 e2) {
// Handle exception of type exceptionType2
} catch (...) {
// Catch any other types of exceptions
}
try
block: This is where you write the code that might throw an exception.catch
block: After the try
block, you define one or more catch
blocks to catch specific types of exceptions.throw
statement: This is used to throw an exception from the code, which is then caught by the corresponding catch
block.In this example, we'll throw and catch an exception when dividing by zero.
#include <iostream>
#include <stdexcept> // For std::runtime_error
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero error!");
}
return a / b;
}
int main() {
try {
int result = divide(10, 0); // Will throw an exception
std::cout << "Result: " << result << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "Caught an exception: " << e.what() << std::endl;
}
return 0;
}
Output:
Caught an exception: Division by zero error!
In this example:
divide()
checks if b
is zero and throws a runtime_error
exception if true.catch
block and the error message is displayed.C++ allows you to catch different types of exceptions separately. This helps in handling each error in a specific manner.
#include <iostream>
#include <stdexcept> // For std::invalid_argument and std::out_of_range
void exampleFunction(int x) {
if (x < 0) {
throw std::invalid_argument("Negative number error!");
}
if (x > 100) {
throw std::out_of_range("Number out of range error!");
}
}
int main() {
try {
exampleFunction(150); // This will throw std::out_of_range
} catch (const std::invalid_argument& e) {
std::cout << "Caught invalid argument: " << e.what() << std::endl;
} catch (const std::out_of_range& e) {
std::cout << "Caught out of range exception: " << e.what() << std::endl;
} catch (...) {
std::cout << "Caught an unknown exception!" << std::endl;
}
return 0;
}
Output:
Caught out of range exception: Number out of range error!
In this example:
exampleFunction()
checks if the input is negative or exceeds 100, and throws different types of exceptions (invalid_argument
or out_of_range
).catch
blocks handle each type of exception separately, allowing more precise error handling.In addition to the standard exception classes like runtime_error
or invalid_argument
, you can create your own custom exceptions by extending the std::exception
class.
#include <iostream>
#include <exception>
class MyCustomException : public std::exception {
public:
const char* what() const noexcept override {
return "My custom exception occurred!";
}
};
void throwCustomException() {
throw MyCustomException();
}
int main() {
try {
throwCustomException(); // Will throw a custom exception
} catch (const MyCustomException& e) {
std::cout << "Caught custom exception: " << e.what() << std::endl;
} catch (...) {
std::cout << "Caught an unknown exception!" << std::endl;
}
return 0;
}
Output:
Caught custom exception: My custom exception occurred!
In this example:
MyCustomException
that overrides the what()
function to provide a custom error message.throwCustomException()
and caught in the catch
block.Exceptions in C++ are propagated from the point of throwing to the nearest catch
block that can handle the exception. If no catch
block is found, the exception will propagate up to the caller, and so on, until the program terminates.
#include <iostream>
#include <stdexcept>
void functionA() {
throw std::runtime_error("Error in function A");
}
void functionB() {
functionA(); // This will propagate the exception from functionA
}
int main() {
try {
functionB(); // Starts exception propagation
} catch (const std::runtime_error& e) {
std::cout << "Caught an exception: " << e.what() << std::endl;
}
return 0;
}
Output:
Caught an exception: Error in function A
In this example:
functionA()
propagates up to functionB()
, and is finally caught in main()
.catch(...)
The catch(...)
block is a catch-all handler that can be used when you don't know or care about the type of exception. It will catch any exception that isn't already caught by previous catch
blocks.
#include <iostream>
void functionWithError() {
throw 42; // Throwing an integer as an exception
}
int main() {
try {
functionWithError();
} catch (int e) {
std::cout << "Caught integer exception: " << e << std::endl;
} catch (...) {
std::cout << "Caught an unknown exception!" << std::endl;
}
return 0;
}
Output:
Caught integer exception: 42
In this example:
catch (int)
block.catch(...)
block would handle it.