Custom Exception Hierarchies
Part of the Object Oriented Programming section of Coddy's C++ journey. Lesson 82 of 104.
While the standard library provides useful exception classes like std::runtime_error, real-world applications often benefit from creating custom exception hierarchies. By defining your own exception classes that inherit from standard exceptions, you can create domain-specific error types that carry additional context and enable more precise error handling.
A custom exception class typically inherits from std::exception or one of its derived classes and overrides the what() method:
#include <exception>
#include <string>
class DatabaseException : public std::exception {
std::string message;
public:
DatabaseException(const std::string& msg) : message(msg) {}
const char* what() const noexcept override { return message.c_str(); }
};
class ConnectionException : public DatabaseException {
public:
ConnectionException(const std::string& host)
: DatabaseException("Failed to connect to: " + host) {}
};
class QueryException : public DatabaseException {
public:
QueryException(const std::string& query)
: DatabaseException("Invalid query: " + query) {}
};This hierarchy allows catch blocks to handle exceptions at different levels of specificity:
try {
// database operations...
throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
std::cout << "General error: " << e.what() << "\n";
}The noexcept specifier on what() is important - it guarantees the method won't throw, which is required by the base class interface. Order your catch blocks from most specific to most general, as C++ uses the first matching handler.
Challenge
EasyLet's build a file processing system with a custom exception hierarchy that handles different types of file-related errors. You'll create domain-specific exceptions that inherit from a base exception class, allowing precise error handling at different levels of specificity.
You'll organize your code across three files:
FileExceptions.h: Define your custom exception hierarchy for file operations.Create a base
FileExceptionclass that inherits fromstd::exception. It should store an error message and override thewhat()method with thenoexceptspecifier.Then create two derived exception classes:
FileNotFoundException: takes a filename and creates a message:File not found: [filename]PermissionDeniedException: takes a filename and creates a message:Permission denied: [filename]
Each derived class should call its parent constructor with the appropriate message.
FileProcessor.h: Define aFileProcessorclass that simulates file operations and throws your custom exceptions.Your
FileProcessorshould have a methodopenFile(const std::string& filename)that simulates opening a file:- If the filename contains
missing, throw aFileNotFoundException - If the filename contains
locked, throw aPermissionDeniedException - Otherwise, return the string
Successfully opened: [filename]
- If the filename contains
main.cpp: Read a single filename from input.Create a
FileProcessorand attempt to open the file. Use a try-catch block with handlers ordered from most specific to most general:- Catch
FileNotFoundExceptionand print:File Error: [message] - Catch
PermissionDeniedExceptionand print:Access Error: [message] - Catch
FileExceptionand print:General File Error: [message]
If no exception is thrown, print the success message returned by
openFile().- Catch
For example, with input report.txt:
Successfully opened: report.txtWith input missing_data.csv:
File Error: File not found: missing_data.csvWith input locked_config.ini:
Access Error: Permission denied: locked_config.iniThis challenge demonstrates how custom exception hierarchies let you handle errors at the appropriate level of detail: catching specific exceptions when you need precise handling, or catching the base exception when you want to handle all file errors uniformly.
Try it yourself
#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"
using namespace std;
int main() {
string filename;
cin >> filename;
FileProcessor processor;
// TODO: Use a try-catch block to handle exceptions
// Order handlers from most specific to most general:
// 1. Catch FileNotFoundException - print: "File Error: [message]"
// 2. Catch PermissionDeniedException - print: "Access Error: [message]"
// 3. Catch FileException - print: "General File Error: [message]"
// If no exception, print the success message from openFile()
try {
// TODO: Call processor.openFile() and handle the result
}
// TODO: Add catch blocks in order from most specific to most general
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator11Advanced OOP Concepts
Composition vs InheritanceMixins via CRTPPimpl IdiomType ErasureEnum Classes & Strong TypingException Handling in OOPCustom Exception Hierarchies3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic ContainerPractice on your own: Online C++ compiler