User-Defined Exceptions

User-defined exceptions allow developers to handle specific error scenarios by creating custom exception classes. These custom classes can be designed to represent unique problems in an application, improving error tracking and making debugging more efficient.

Why Use User-Defined Exceptions?

User-defined exceptions are useful when built-in exceptions (like std::runtime_error, std::out_of_range, etc.) are insufficient for handling specific errors in a program. By defining your own exceptions, you can tailor them to represent particular issues relevant to your domain or business logic, such as database errors, file handling issues, or validation failures.

How to Create a User-Defined Exception

A user-defined exception class typically inherits from a standard C++ exception class like std::exception or its derived classes (std::runtime_error, std::logic_error, etc.). By overriding the what() method, you can provide a custom error message that describes the specific error condition.

Steps to Create a User-Defined Exception Class:

  1. Inherit from std::exception or its subclasses: This ensures the custom exception is compatible with the standard exception-handling mechanism in C++.
  2. Override the what() method: The what() method returns an error message, which can be customized to include more detailed and specific information about the error.

Example: Creating a User-Defined Exception

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

// Define a custom exception class
class InvalidAgeException : public std::exception {
private:
    std::string message;

public:
    // Constructor to set the custom error message
    InvalidAgeException(const std::string& msg) : message(msg) {}

    // Override the what() method to return the custom error message
    const char* what() const noexcept override {
        return message.c_str();
    }
};

int main() {
    int age;
    std::cout << "Enter your age: ";
    std::cin >> age;

    try {
        // Throw custom exception if age is invalid
        if (age < 0) {
            throw InvalidAgeException("Error: Age cannot be negative.");
        }
        std::cout << "Your age is " << age << std::endl;
    } catch (const InvalidAgeException& e) {
        // Catch and display the custom exception message
        std::cerr << "Caught an exception: " << e.what() << std::endl;
    }

    return 0;
}

Key Features of the Example:

  • Custom Error Message: The custom exception class, InvalidAgeException, allows the program to provide a specific error message when invalid age input is detected.
  • Error-Specific Handling: In this example, the exception is thrown only when the input is a negative age, providing targeted error handling for that particular scenario.

Creating Multiple Custom Exceptions

In large applications, you can define multiple user-defined exceptions, each targeting specific error types. For instance, you can create exceptions like FileNotFoundException, InvalidUserInputException, or DatabaseConnectionException to handle distinct issues.

Example: Multiple User-Defined Exceptions

class FileNotFoundException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Error: File not found.";
    }
};

class InvalidInputException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Error: Invalid input provided.";
    }
};

int main() {
    try {
        // Simulate file not found error
        throw FileNotFoundException();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    try {
        // Simulate invalid input error
        throw InvalidInputException();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

Best Practices for User-Defined Exceptions:

  1. Descriptive Error Messages: Provide meaningful error messages that give context to the exception, which makes debugging easier.
  2. Use Inheritance: Inherit from standard exception classes like std::exception or its derivatives to maintain compatibility with C++’s exception-handling system.
  3. Catch Specific Exceptions: Catch user-defined exceptions separately to ensure specific error-handling logic is applied to different types of errors.
  4. Custom Error Codes: You can add more complexity to custom exceptions by including error codes, additional data, or methods that provide further context for the error.

User-Defined Exception Hierarchy

In larger applications, custom exception classes can be organized in a hierarchy. For instance, you might have a base exception class like ApplicationException, with subclasses for more specific exceptions like FileException, DatabaseException, etc.

Example: Custom Exception Hierarchy

class ApplicationException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Application encountered an error.";
    }
};

class FileException : public ApplicationException {
public:
    const char* what() const noexcept override {
        return "File error occurred.";
    }
};

class NetworkException : public ApplicationException {
public:
    const char* what() const noexcept override {
        return "Network error occurred.";
    }
};