Exception handling is a mechanism used in C++ to handle unexpected situations or runtime errors in a controlled way. It allows a program to detect an exceptional condition and transfer control to code that can handle it.
try,
throw, and catch keywords for exception
handling.
Exception handling allows a program to respond to exceptional conditions without mixing error-handling logic with normal program logic.
For example, dividing a number by zero is an invalid operation that a program may need to handle.
try {
// Code that may cause an exception
}
catch(...) {
// Handle exception
}
Exception handling can make programs more robust by separating exceptional situations from normal program flow.
C++ exception handling commonly uses three keywords:
| Keyword | Purpose |
|---|---|
try |
Contains code that may generate an exception. |
throw |
Generates or propagates an exception. |
catch |
Handles an exception. |
The basic structure of exception handling is:
try {
// Risky code
}
catch(...) {
// Exception handling code
}
The catch block is executed when a matching exception is
thrown from the associated try block.
The throw statement is used to signal an exception.
int age = 15;
if(age < 18) {
throw "Not eligible";
}
The thrown value can be handled by a suitable catch
handler.
#include <iostream>
int main() {
try {
throw 10;
}
catch(int value) {
std::cout <<
"Exception: "
<< value;
}
return 0;
}
The integer 10 is thrown and then handled by the
catch(int value) block.
try {
throw 100;
}
catch(int error) {
std::cout <<
"Error code: "
<< error;
}
The type of the catch parameter must match the thrown
exception type or be compatible with it.
try {
throw std::string(
"Something went wrong"
);
}
catch(const std::string& message) {
std::cout <<
message;
}
A string object can also be thrown and caught.
C++ provides standard exception classes in the standard library.
#include <stdexcept>
try {
throw std::runtime_error(
"File could not be opened"
);
}
catch(
const std::runtime_error& e
) {
std::cout <<
e.what();
}
The what() function provides an explanatory message.
#include <iostream>
#include <stdexcept>
double divide(
double a,
double b
) {
if(b == 0) {
throw std::runtime_error(
"Cannot divide by zero"
);
}
return a / b;
}
int main() {
try {
std::cout <<
divide(10, 0);
}
catch(
const std::runtime_error& e
) {
std::cout <<
e.what();
}
return 0;
}
A single try block can be followed by multiple
catch blocks.
try {
throw 10;
}
catch(int value) {
std::cout <<
"Integer exception";
}
catch(double value) {
std::cout <<
"Double exception";
}
The first suitable handler is selected for the thrown exception.
try {
throw 5.5;
}
catch(int value) {
std::cout <<
"Integer";
}
catch(double value) {
std::cout <<
"Double";
}
Since a double is thrown, the matching
double handler is selected.
A catch-all handler uses ....
try {
throw 100;
}
catch(...) {
std::cout <<
"Some exception occurred";
}
It can catch an exception of any type, although it does not directly provide the original typed value.
When using multiple handlers, more specific handlers should generally come before a catch-all handler.
try {
throw std::runtime_error(
"Error"
);
}
catch(
const std::runtime_error& e
) {
std::cout <<
e.what();
}
catch(...) {
std::cout <<
"Unknown exception";
}
A catch-all handler placed first would prevent later handlers from being reached.
The C++ standard library provides several exception classes.
std::exceptionstd::runtime_errorstd::logic_errorstd::invalid_argumentstd::out_of_rangestd::length_errorstd::overflow_errorstd::underflow_errorThese classes can provide more meaningful and structured error information.
std::exception is a standard base exception type.
#include <iostream>
#include <exception>
try {
throw std::runtime_error(
"Runtime error"
);
}
catch(
const std::exception& e
) {
std::cout <<
e.what();
}
A handler for std::exception can catch many standard
library exceptions through the common base type.
std::invalid_argument is useful when a function receives
an argument that is not valid for the requested operation.
#include <stdexcept>
int squareRootInput(int value) {
if(value < 0) {
throw std::invalid_argument(
"Value cannot be negative"
);
}
return value;
}
std::out_of_range can be used when a value is outside the
valid range of an operation.
#include <stdexcept>
int getValue(
int index
) {
if(index < 0 ||
index >= 5) {
throw std::out_of_range(
"Index is out of range"
);
}
return index;
}
If a function does not handle an exception, the exception can propagate back through the calling functions until a suitable handler is found.
void test() {
throw std::runtime_error(
"Error in test"
);
}
void process() {
test();
}
int main() {
try {
process();
}
catch(
const std::exception& e
) {
std::cout <<
e.what();
}
}
A handler can use throw; without an operand to rethrow the
currently handled exception.
try {
try {
throw std::runtime_error(
"Original error"
);
}
catch(
const std::exception& e
) {
std::cout <<
"Logging error\n";
throw;
}
}
catch(
const std::exception& e
) {
std::cout <<
e.what();
}
Rethrowing is useful when one layer needs to log or partially handle an exception while allowing another layer to handle it further.
You can create your own exception class when application-specific error information is useful.
#include <exception>
class AgeException :
public std::exception {
public:
const char* what()
const noexcept override {
return
"Age is not valid";
}
};
The custom exception can then be thrown and caught like other exceptions.
class AgeException :
public std::exception {
public:
const char* what()
const noexcept override {
return
"Age must be 18 or above";
}
};
void checkAge(int age) {
if(age < 18) {
throw AgeException();
}
}
int main() {
try {
checkAge(15);
}
catch(
const std::exception& e
) {
std::cout <<
e.what();
}
return 0;
}
double divide(
double a,
double b
) {
if(b == 0) {
throw std::invalid_argument(
"Division by zero"
);
}
return a / b;
}
int main() {
try {
double result =
divide(20, 0);
std::cout <<
result;
}
catch(
const std::exception& e
) {
std::cout <<
e.what();
}
return 0;
}
A function can throw an exception and let the caller decide how to handle it.
Constructors can also throw exceptions when an object cannot be created in a valid state.
class Student {
private:
int age;
public:
Student(int a) {
if(a < 0) {
throw std::invalid_argument(
"Age cannot be negative"
);
}
age = a;
}
};
The caller can catch the exception when constructing the object.
Exception-safe code should manage resources carefully. Modern C++ commonly uses RAII and standard library resource-managing types to ensure cleanup happens automatically.
#include <memory>
void process() {
auto value =
std::make_unique<int>(100);
// If an exception occurs,
// the resource is automatically released.
}
Using RAII reduces the risk of resource leaks when exceptions occur.
try blocks.const reference when appropriate.#include <iostream>
#include <stdexcept>
class Student {
private:
int marks;
public:
Student(int m) {
if(m < 0 || m > 100) {
throw std::out_of_range(
"Marks must be between 0 and 100"
);
}
marks = m;
}
void display() {
std::cout <<
"Marks: "
<< marks;
}
};
int main() {
try {
Student student(120);
student.display();
}
catch(
const std::exception& e
) {
std::cout <<
"Error: "
<< e.what();
}
return 0;
}
The constructor validates the data and throws an exception when the value is outside the allowed range.
| Concept | Meaning |
|---|---|
try |
Contains code that may throw an exception. |
throw |
Signals or propagates an exception. |
catch |
Handles a matching exception. |
std::exception |
Common standard exception base type. |
| Custom Exception | User-defined exception type for application-specific errors. |
| Rethrow | Uses throw; to propagate the currently handled exception. |
| RAII | Resource management technique that helps provide safe cleanup, including during exception handling. |
#include <iostream>
#include <stdexcept>
double divide(
double a,
double b
) {
if(b == 0) {
throw std::invalid_argument(
"Cannot divide by zero"
);
}
return a / b;
}
int main() {
try {
std::cout <<
divide(20, 0);
}
catch(
const std::exception& e
) {
std::cout <<
"Error: "
<< e.what();
}
return 0;
}
Exception handling provides a structured way to detect and handle exceptional situations while keeping normal program logic separate from error-handling logic.
try, throw, and catch for exception handling.try contains code that may generate an exception.throw signals an exceptional condition.catch handles a matching exception.catch(...) can handle an exception of any type.std::runtime_error and std::invalid_argument.Question: Which three keywords are mainly used for exception handling in C++?