User-Defined Exceptions in C++


In C++, the exception handling mechanism is designed to catch predefined exceptions from the Standard Library. However, there are situations where the errors occurring in a program may not match any predefined exceptions. In such cases, you can create your own custom (user-defined) exceptions to handle specific error conditions more effectively. This article explores how to define and use user-defined exceptions in C++.

Why Use User-Defined Exceptions?

Using user-defined exceptions allows you to capture specific error conditions that are unique to your application. For example, if your program works with a custom data structure, you might want to throw an exception when a certain operation violates the expected behavior of the structure. By defining your own exceptions, you can make error handling more meaningful and structured.

Steps to Create a User-Defined Exception

Creating a user-defined exception involves the following steps:

  • Define a custom exception class by inheriting from std::exception.
  • Override the what() method to return a description of the exception.
  • Throw the custom exception when the error condition is met in your code.
  • Catch the exception in a try-catch block.

Example: Simple User-Defined Exception

In this example, we will define a custom exception class called InvalidAgeException that will be thrown when an invalid age is encountered (e.g., a negative age).

    #include <iostream>
    #include <stdexcept>

    class InvalidAgeException : public std::exception {
    public:
        const char* what() const noexcept override {
            return "Invalid age provided. Age must be non-negative.";
        }
    };

    void checkAge(int age) {
        if (age < 0) {
            throw InvalidAgeException();  // Throw the custom exception
        }
        std::cout << "Age is valid: " << age << "\n";
    }

    int main() {
        try {
            checkAge(-1);  // This will throw an InvalidAgeException
        } catch (const InvalidAgeException& e) {
            std::cout << "Caught exception: " << e.what() << "\n";
        }
        return 0;
    }
        

Output:

    Caught exception: Invalid age provided. Age must be non-negative.
        

In this example, we define a user-defined exception InvalidAgeException, which is thrown if the age passed to the checkAge() function is negative. The custom exception provides a descriptive message using the overridden what() method.

Example: User-Defined Exception with Additional Information

You can extend user-defined exceptions to include additional information by adding custom data members. For example, we can create an exception that contains the invalid value that caused the exception to be thrown.

    #include <iostream>
    #include <stdexcept>

    class InvalidAgeException : public std::exception {
    private:
        int age;
    public:
        InvalidAgeException(int invalidAge) : age(invalidAge) {}
        
        int getAge() const {
            return age;
        }

        const char* what() const noexcept override {
            return "Invalid age provided.";
        }
    };

    void checkAge(int age) {
        if (age < 0) {
            throw InvalidAgeException(age);  // Throw custom exception with age value
        }
        std::cout << "Age is valid: " << age << "\n";
    }

    int main() {
        try {
            checkAge(-5);  // This will throw an InvalidAgeException with age -5
        } catch (const InvalidAgeException& e) {
            std::cout << "Caught exception: " << e.what() << " Invalid age: " << e.getAge() << "\n";
        }
        return 0;
    }
        

Output:

    Caught exception: Invalid age provided. Invalid age: -5
        

In this example, the custom exception InvalidAgeException now stores the invalid age as part of the exception object. This additional data can be retrieved using the getAge() method, which helps provide more detailed information when handling the exception.

Example: Multiple Custom Exceptions

In some cases, you might need to define multiple user-defined exceptions for different error scenarios. You can throw and catch them in a structured manner as shown in the example below:

    #include <iostream>
    #include <stdexcept>

    class InvalidAgeException : public std::exception {
    public:
        const char* what() const noexcept override {
            return "Invalid age provided. Age must be non-negative.";
        }
    };

    class NameTooShortException : public std::exception {
    public:
        const char* what() const noexcept override {
            return "Name is too short. It must be at least 3 characters.";
        }
    };

    void checkPerson(int age, const std::string& name) {
        if (age < 0) {
            throw InvalidAgeException();
        } 
        if (name.length() < 3) {
            throw NameTooShortException();
        }
        std::cout << "Person details are valid: Age " << age << ", Name " << name << "\n";
    }

    int main() {
        try {
            checkPerson(25, "Jo");  // This will throw NameTooShortException
        } catch (const InvalidAgeException& e) {
            std::cout << "Caught exception: " << e.what() << "\n";
        } catch (const NameTooShortException& e) {
            std::cout << "Caught exception: " << e.what() << "\n";
        }
        return 0;
    }
        

Output:

    Caught exception: Name is too short. It must be at least 3 characters.
        

Here, we define two custom exceptions: InvalidAgeException and NameTooShortException. The function checkPerson() checks both the age and the name of a person, throwing an appropriate exception if a condition is violated.

Best Practices for User-Defined Exceptions

  • Inherit from std::exception: Always inherit from the std::exception class to ensure that your custom exceptions can be caught as std::exception types.
  • Override the what() method: Always override the what() method to provide a meaningful error message describing the exception.
  • Provide meaningful data: When appropriate, include additional data members to store more information about the exception, such as invalid values or error codes.
  • Document your exceptions: Clearly document the circumstances under which your exceptions are thrown to help developers understand and use them correctly.

Conclusion

User-defined exceptions in C++ provide a powerful way to handle specific errors that arise in your program. By creating custom exception classes, you can provide more informative error messages and handle errors in a way that is tailored to your application's needs. With proper exception handling, you can improve the reliability and maintainability of your C++ programs.





Advertisement