Exception Handling in OOP
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 81 of 104.
Exception handling in C++ provides a structured way to detect and respond to runtime errors. In object-oriented programming, exceptions integrate naturally with class hierarchies, allowing you to create meaningful error types that carry context about what went wrong.
The basic mechanism uses three keywords: try, throw, and catch. Code that might fail goes in a try block, errors are signaled with throw, and catch blocks handle specific exception types:
#include <iostream>
#include <stdexcept>
class BankAccount {
double balance;
public:
BankAccount(double b) : balance(b) {}
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}
double getBalance() const { return balance; }
};
int main() {
BankAccount account(100.0);
try {
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}
std::cout << "Balance: " << account.getBalance() << "\n";
}The C++ standard library provides a hierarchy of exception classes rooted at std::exception. Common derived classes include std::runtime_error for runtime issues and std::logic_error for programming mistakes. All standard exceptions provide a what() method that returns an error message.
When an exception is thrown, the stack unwinds - destructors are called for all local objects as the program searches for a matching catch block. This ensures proper cleanup even when errors occur, which is why RAII and exception handling work so well together.
Challenge
EasyLet's build a safe division calculator that demonstrates exception handling in an object-oriented context. You'll create a Calculator class that performs division operations and throws meaningful exceptions when something goes wrong, then handle those exceptions gracefully in your main program.
You'll organize your code across three files:
Calculator.h: Define yourCalculatorclass that performs mathematical operations with proper error handling.Your
Calculatorshould have a methoddivide(double numerator, double denominator)that returns the result of the division. However, if the denominator is zero, it should throw astd::runtime_errorwith the messageDivision by zero. If either the numerator or denominator is negative, it should throw astd::invalid_argumentwith the messageNegative values not allowed.Also include a method
safeSqrt(double value)that returns the square root of the value. If the value is negative, throw astd::runtime_errorwith the messageCannot compute square root of negative number. You'll need to include<cmath>for thesqrtfunction.Calculator.cpp: Implement the methods declared in your header file. Make sure to include the necessary headers (<stdexcept>and<cmath>) and check the conditions before performing the operations.main.cpp: Read three inputs (each on a separate line):- Operation type: either
divideorsqrt - First number (double)
- Second number (double) — only used for division, ignored for sqrt
Create a
Calculatorobject and perform the requested operation inside a try block. If the operation succeeds, printResult: [value]. If an exception is caught, printError: [message]where the message comes from the exception'swhat()method.After the try-catch block (regardless of whether an exception occurred), print
Calculation completeto demonstrate that the program continues executing after handling exceptions.- Operation type: either
For example, with inputs divide, 10, and 2:
Result: 5
Calculation completeWith inputs divide, 10, and 0:
Error: Division by zero
Calculation completeWith inputs divide, -5, and 2:
Error: Negative values not allowed
Calculation completeWith inputs sqrt, 16, and 0:
Result: 4
Calculation completeWith inputs sqrt, -9, and 0:
Error: Cannot compute square root of negative number
Calculation completeNotice how the program gracefully handles errors by catching exceptions and displaying meaningful messages, then continues execution. This pattern keeps your error handling clean and your main logic focused on the happy path.
Cheat sheet
Exception handling in C++ uses three keywords: try, throw, and catch. Code that might fail goes in a try block, errors are signaled with throw, and catch blocks handle specific exception types:
try {
// Code that might throw an exception
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}Throwing exceptions from class methods:
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}The C++ standard library provides exception classes rooted at std::exception. Common types include:
std::runtime_error- for runtime issuesstd::logic_error- for programming mistakesstd::invalid_argument- for invalid arguments
All standard exceptions provide a what() method that returns an error message.
When an exception is thrown, the stack unwinds - destructors are called for all local objects as the program searches for a matching catch block, ensuring proper cleanup.
Include <stdexcept> to use standard exception classes.
Try it yourself
#include <iostream>
#include <string>
#include "Calculator.h"
using namespace std;
int main() {
// Read inputs
string operation;
double num1, num2;
cin >> operation;
cin >> num1;
cin >> num2;
// Create Calculator object
Calculator calc;
// TODO: Use a try-catch block to perform the operation
// - If operation is "divide", call calc.divide(num1, num2)
// - If operation is "sqrt", call calc.safeSqrt(num1)
// - On success, print "Result: " followed by the result
// - Catch exceptions and print "Error: " followed by the exception message (use what())
// - After the try-catch, always print "Calculation complete"
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 Container