SyntaxStudy
Sign Up
C++ Custom Exception Classes and Exception Hierarchies
C++ Beginner 1 min read

Custom Exception Classes and Exception Hierarchies

The standard library exception hierarchy rooted at 'std::exception' provides a useful base for custom exception types. Deriving from 'std::runtime_error' or 'std::logic_error' is the recommended approach: it allows catch handlers to intercept custom exceptions at their specific type, at their superclass level, or at the most general 'std::exception' level, giving callers maximum flexibility. A well-designed custom exception carries enough context to diagnose the problem. The constructor typically accepts a message string forwarded to the base class, and may also store additional structured data such as error codes, file names, or invalid values. Overriding 'what()' provides a human-readable description; members expose the structured data programmatically. Exception specifications have evolved significantly across standards. C++11 introduced 'noexcept' to declare that a function will never throw; a violation causes 'std::terminate' to be called immediately. Marking move constructors and move-assignment operators 'noexcept' is especially important because STL containers use this information to decide whether to move or copy elements during reallocation.
Example
#include <iostream>
#include <stdexcept>
#include <string>

// Base application exception
class AppException : public std::runtime_error {
public:
    explicit AppException(const std::string& msg, int code = -1)
        : std::runtime_error(msg), errorCode_(code) {}

    int errorCode() const noexcept { return errorCode_; }

private:
    int errorCode_;
};

// Specific derived exception
class DatabaseException : public AppException {
public:
    DatabaseException(const std::string& query, const std::string& reason)
        : AppException("DB error on [" + query + "]: " + reason, 500)
        , query_(query) {}

    const std::string& query() const noexcept { return query_; }

private:
    std::string query_;
};

class NetworkException : public AppException {
public:
    explicit NetworkException(const std::string& host, int httpCode)
        : AppException("Network error connecting to " + host, httpCode)
        , host_(host) {}

    const std::string& host() const noexcept { return host_; }

private:
    std::string host_;
};

void simulateQuery(bool fail) {
    if (fail)
        throw DatabaseException("SELECT * FROM users", "table not found");
}

int main() {
    try {
        simulateQuery(true);
    } catch (const DatabaseException& e) {
        std::cout << "DB: " << e.what()
                  << " (code=" << e.errorCode()
                  << ", query=" << e.query() << ")\n";
    } catch (const AppException& e) {
        std::cout << "App: " << e.what() << "\n";
    } catch (const std::exception& e) {
        std::cout << "std: " << e.what() << "\n";
    }

    return 0;
}