Docs
README
Exception Handling in C++
Table of Contents
- •Exception Basics
- •try-catch Blocks
- •Throwing Exceptions
- •Standard Exceptions
- •Custom Exceptions
- •Exception Specifications
- •Best Practices
Exception Basics
Exceptions provide error handling separate from normal code flow:
try {
// Code that might throw
throw runtime_error("Something went wrong");
}
catch (const exception& e) {
// Handle error
cerr << e.what() << endl;
}
try-catch Blocks
Basic Syntax
try {
// Risky code
}
catch (const ExceptionType& e) {
// Handle specific exception
}
catch (...) {
// Catch all other exceptions
}
Multiple Catch Blocks
try {
functionThatMightThrow();
}
catch (const invalid_argument& e) {
cerr << "Invalid argument: " << e.what() << endl;
}
catch (const out_of_range& e) {
cerr << "Out of range: " << e.what() << endl;
}
catch (const exception& e) {
cerr << "Other error: " << e.what() << endl;
}
catch (...) {
cerr << "Unknown exception" << endl;
}
Nested try-catch
try {
try {
throw runtime_error("Inner error");
}
catch (const runtime_error& e) {
// Handle or rethrow
throw; // Rethrow same exception
}
}
catch (const exception& e) {
cerr << "Caught: " << e.what() << endl;
}
Throwing Exceptions
Throw Statement
// Throw built-in type
throw 42;
throw "Error message";
// Throw standard exception
throw runtime_error("Description");
throw invalid_argument("Bad value");
// Throw custom exception
throw MyException("Custom error");
Rethrowing
catch (const exception& e) {
// Log it
log(e.what());
// Rethrow
throw; // Preserves original exception
}
Throw in Functions
int divide(int a, int b) {
if (b == 0) {
throw invalid_argument("Division by zero");
}
return a / b;
}
try {
int result = divide(10, 0);
}
catch (const invalid_argument& e) {
cerr << e.what() << endl;
}
Standard Exceptions
#include <stdexcept>
// Base class
exception
// Logic errors (programmer bugs)
logic_error
├── invalid_argument // Bad argument
├── domain_error // Domain violation
├── length_error // Length exceeded
└── out_of_range // Index out of range
// Runtime errors (environment issues)
runtime_error
├── range_error // Range error
├── overflow_error // Arithmetic overflow
└── underflow_error // Arithmetic underflow
// Other
bad_alloc // Memory allocation failed
bad_cast // Dynamic cast failed
bad_typeid // typeid on null pointer
Examples
// out_of_range
vector<int> v = {1, 2, 3};
try {
v.at(10); // Throws out_of_range
}
catch (const out_of_range& e) { }
// bad_alloc
try {
int* arr = new int[1000000000000];
}
catch (const bad_alloc& e) { }
// invalid_argument
try {
stoi("not a number"); // Throws
}
catch (const invalid_argument& e) { }
Custom Exceptions
Inherit from exception
class MyException : public exception {
string message;
public:
MyException(const string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
// Usage
throw MyException("Custom error occurred");
Inherit from runtime_error
class FileError : public runtime_error {
public:
FileError(const string& msg) : runtime_error(msg) {}
};
class NetworkError : public runtime_error {
public:
NetworkError(const string& msg) : runtime_error(msg) {}
};
Exception with Data
class ValidationError : public exception {
string field;
string message;
public:
ValidationError(const string& f, const string& msg)
: field(f), message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
const string& getField() const { return field; }
};
// Usage
try {
throw ValidationError("email", "Invalid format");
}
catch (const ValidationError& e) {
cerr << e.getField() << ": " << e.what() << endl;
}
Exception Specifications
noexcept
// Function won't throw
void safe() noexcept {
// ...
}
// Conditional noexcept
template<typename T>
void process(T t) noexcept(noexcept(t.operation())) {
t.operation();
}
// Check at compile time
static_assert(noexcept(safe()), "must be noexcept");
noexcept and Move Operations
class MyClass {
public:
// noexcept enables optimizations
MyClass(MyClass&& other) noexcept {
// Move resources
}
MyClass& operator=(MyClass&& other) noexcept {
// Move assign
return *this;
}
};
Best Practices
✅ Do
// 1. Catch by const reference
catch (const exception& e)
// 2. Use RAII for cleanup
{
unique_ptr<File> file(new File("data.txt"));
// Exception safe - file closes automatically
}
// 3. Provide useful error messages
throw runtime_error("Failed to connect to " + host + ":" + to_string(port));
// 4. Document throwing functions
/// @throws invalid_argument if value is negative
void setAge(int value);
// 5. Use noexcept for move operations
MyClass(MyClass&&) noexcept;
❌ Don't
// 1. Don't catch by value (slicing)
catch (exception e) // BAD - slices derived types
// 2. Don't use exceptions for flow control
try {
while (true) {
data = getNext(); // Throws when done - BAD
}
} catch (...) { }
// 3. Don't throw from destructors
~MyClass() {
throw error; // BAD - can cause terminate()
}
// 4. Don't catch and ignore silently
catch (...) { } // BAD - hides errors
// 5. Don't throw raw pointers
throw new MyException(); // Memory leak risk
Quick Reference
// Try-catch
try { }
catch (const Type& e) { }
catch (...) { }
// Throw
throw exception_object;
throw; // Rethrow current exception
// Standard exceptions
#include <stdexcept>
runtime_error, logic_error
invalid_argument, out_of_range
overflow_error, bad_alloc
// Custom exception
class MyError : public runtime_error {
public:
MyError(const string& msg) : runtime_error(msg) {}
};
// noexcept
void func() noexcept;
void func() noexcept(condition);
noexcept(expression) // Check if noexcept
Compile & Run
g++ -std=c++17 -Wall examples.cpp -o examples && ./examples