Skip to content

C++ Exception Handling

Overview

Exception handling provides a way to deal with runtime errors and exceptional situations in a structured manner.

Basic Exception Handling

try-catch Blocks

cpp
#include <iostream>
#include <stdexcept>

int main() {
    try {
        int numerator = 10;
        int denominator = 0;
        
        if (denominator == 0) {
            throw std::runtime_error("Division by zero!");
        }
        
        double result = numerator / denominator;
        std::cout << "Result: " << result << std::endl;
        
    } catch (const std::runtime_error& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

Multiple catch Blocks

cpp
#include <iostream>
#include <stdexcept>
#include <vector>

int main() {
    try {
        std::vector<int> vec = {1, 2, 3};
        
        // This will throw std::out_of_range
        int value = vec.at(10);
        
    } catch (const std::out_of_range& e) {
        std::cout << "Out of range error: " << e.what() << std::endl;
        
    } catch (const std::exception& e) {
        std::cout << "Standard exception: " << e.what() << std::endl;
        
    } catch (...) {
        std::cout << "Unknown exception occurred" << std::endl;
    }
    
    return 0;
}

Custom Exceptions

Creating Custom Exception Classes

cpp
#include <iostream>
#include <exception>
#include <string>

// Custom exception class
class InsufficientFundsException : public std::exception {
private:
    std::string message;
    
public:
    InsufficientFundsException(double balance, double amount) {
        message = "Insufficient funds. Balance: " + std::to_string(balance) + 
                  ", Attempted: " + std::to_string(amount);
    }
    
    const char* what() const noexcept override {
        return message.c_str();
    }
};

class BankAccount {
private:
    double balance;
    
public:
    BankAccount(double initial_balance) : balance(initial_balance) {}
    
    void withdraw(double amount) {
        if (amount > balance) {
            throw InsufficientFundsException(balance, amount);
        }
        balance -= amount;
        std::cout << "Withdrawal successful. New balance: " << balance << std::endl;
    }
    
    double getBalance() const { return balance; }
};

int main() {
    try {
        BankAccount account(100.0);
        account.withdraw(50.0);
        account.withdraw(75.0);  // This will throw exception
        
    } catch (const InsufficientFundsException& e) {
        std::cout << "Transaction failed: " << e.what() << std::endl;
    }
    
    return 0;
}

Exception Safety

RAII and Exception Safety

cpp
#include <iostream>
#include <memory>
#include <fstream>

class Resource {
private:
    int* data;
    
public:
    Resource(int size) : data(new int[size]) {
        std::cout << "Resource allocated" << std::endl;
    }
    
    ~Resource() {
        delete[] data;
        std::cout << "Resource deallocated" << std::endl;
    }
    
    void processData() {
        throw std::runtime_error("Error during processing");
    }
};

int main() {
    try {
        Resource res(100);
        res.processData();
        
    } catch (const std::exception& e) {
        std::cout << "Exception caught: " << e.what() << std::endl;
        // Resource destructor is automatically called here
    }
    
    return 0;
}

Smart Pointers and Exception Safety

cpp
#include <iostream>
#include <memory>
#include <vector>

void processData() {
    // Using unique_ptr for automatic cleanup
    auto data = std::make_unique<std::vector<int>>(1000);
    
    // Fill the vector
    for (int i = 0; i < 1000; i++) {
        data->push_back(i);
    }
    
    // Simulate an exception
    throw std::runtime_error("Processing failed");
    
    // No need for manual cleanup - unique_ptr handles it
}

int main() {
    try {
        processData();
        
    } catch (const std::exception& e) {
        std::cout << "Exception caught: " << e.what() << std::endl;
        std::cout << "Memory was automatically cleaned up" << std::endl;
    }
    
    return 0;
}

noexcept Specifier

cpp
#include <iostream>
#include <stdexcept>

// Function that doesn't throw exceptions
int safeDivide(int a, int b) noexcept {
    return a / b;  // Undefined behavior if b == 0
}

// Function that might throw exceptions
int unsafeDivide(int a, int b) {
    if (b == 0) {
        throw std::runtime_error("Division by zero");
    }
    return a / b;
}

int main() {
    try {
        int result1 = safeDivide(10, 2);
        std::cout << "Safe division result: " << result1 << std::endl;
        
        int result2 = unsafeDivide(10, 0);
        
    } catch (const std::exception& e) {
        std::cout << "Exception caught: " << e.what() << std::endl;
    }
    
    return 0;
}

Best Practices

Exception Handling Guidelines

cpp
#include <iostream>
#include <stdexcept>
#include <vector>

class Database {
private:
    std::vector<std::string> records;
    
public:
    void addRecord(const std::string& record) {
        if (record.empty()) {
            throw std::invalid_argument("Record cannot be empty");
        }
        records.push_back(record);
    }
    
    std::string getRecord(size_t index) const {
        if (index >= records.size()) {
            throw std::out_of_range("Record index out of range");
        }
        return records[index];
    }
    
    size_t getRecordCount() const noexcept {
        return records.size();
    }
};

int main() {
    Database db;
    
    try {
        db.addRecord("First record");
        db.addRecord("Second record");
        db.addRecord("");  // This will throw an exception
        
    } catch (const std::invalid_argument& e) {
        std::cout << "Invalid argument: " << e.what() << std::endl;
        
    } catch (const std::exception& e) {
        std::cout << "Standard exception: " << e.what() << std::endl;
    }
    
    // Check record count (noexcept function)
    std::cout << "Record count: " << db.getRecordCount() << std::endl;
    
    return 0;
}

Content is for learning and research only.