BIT153 Object Oriented Programming

Object Oriented ProgrammingUnit 710 min read

Exception Handling: Errors, Try-Catch, and Program Recovery

Unit 7 of Object Oriented Programming teaches how to anticipate, catch, and recover from runtime errors in C++ using structured exception handling (try-throw-catch), built-in exceptions, and user-defined exceptions—with real-world examples from banking, e-commerce, and telecom systems.

TAKEAWAYS:

  • Exception handling isolates errors to prevent program crashes by separating error-handling code from normal logic.
  • Built-in exceptions (e.g., std::runtime_error) and user-defined exceptions (custom classes) can be thrown and caught.
  • The try block contains risky code, catch blocks handle specific exceptions, and throw propagates errors upward.
  • Stack unwinding releases resources (e.g., file handles, memory) when exceptions propagate through function calls.
  • Best practices include catching exceptions by reference (catch(const std::exception& e)) and avoiding empty catch blocks.

What is Exception Handling?

Exception handling is a mechanism to manage runtime errors gracefully without crashing the program. It follows the E-A-T model:

  • Error detection (e.g., division by zero, file not found).
  • Action (throw an exception).
  • Treatment (catch and handle the exception).

Why Use Exception Handling?

  • Prevents abrupt termination: Programs continue running after errors.
  • Improves maintainability: Error-handling code is centralized.
  • Enhances robustness: Users get meaningful error messages (e.g., "Invalid input: age cannot be negative").

Types of Exceptions

Exceptions in C++ are categorized into three types:

Type Description Example
Synchronous Occur due to program logic (e.g., invalid input, arithmetic errors). throw std::runtime_error("Invalid age");
Asynchronous Triggered by external events (e.g., hardware failure, network issues). throw std::system_error(errno, "Disk full");
Built-in Provided by the C++ Standard Library (e.g., std::exception, std::logic_error). catch (const std::out_of_range& e)

How Exception Handling Works

The syntax involves three keywords:

  1. try: Encloses code that might throw an exception.
  2. throw: Propagates an exception (value, object, or string).
  3. catch: Handles the exception (can have multiple blocks for different exceptions).

Flowchart of Exception Handling

flowchart TD
    A["Start"] --> B["Execute try block"]
    B -->|"Exception thrown?"| C["Yes"]
    C --> D["Search catch blocks (top to bottom)"]
    D -->|"Match found?"| E["Yes"]
    E --> F["Execute catch block"]
    F --> G["Continue program"]
    D -->|"No match"| H["Terminate program (unhandled)"]
    B -->|"No exception"| G

Built-in Exceptions

The C++ Standard Library provides exception classes in the <stdexcept> header:

Exception Class When Thrown Example
std::runtime_error General runtime errors (e.g., invalid arguments). throw std::runtime_error("File not found");
std::logic_error Program logic errors (e.g., invalid state). throw std::logic_error("Negative salary");
std::out_of_range Accessing beyond container bounds (e.g., vector::at()). catch (const std::out_of_range& e)
std::invalid_argument Invalid function arguments. throw std::invalid_argument("Age < 0");

User-Defined Exceptions

You can create custom exceptions by deriving from std::exception or using classes.

Example: Custom Exception for Bank Transactions

#include <iostream>
#include <stdexcept>
#include <string>

```figure
{"type":"tree","nodes":["BankException","InsufficientFunds","NegativeBalance","InvalidTransaction"],"caption":"Hierarchy of custom exceptions for banking operations (e.g., extending `RuntimeException`)."}

class InsufficientFunds : public std::runtime_error { public: InsufficientFunds(double balance, double amount) : std::runtime_error("Insufficient funds"), balance(balance), amount(amount) {}

double getBalance() const { return balance; }
double getAmount() const { return amount; }

private: double balance, amount; };

int main() { double accountBalance = 1000.0; double withdrawalAmount = 1500.0;

try {
    if (withdrawalAmount > accountBalance) {
        throw InsufficientFunds(accountBalance, withdrawalAmount);
    }
    std::cout << "Withdrawal successful. Remaining balance: "
              << (accountBalance - withdrawalAmount) << std::endl;
}
catch (const InsufficientFunds& e) {
    std::cerr << "Error: " << e.what() << "\n"
              << "Available balance: " << e.getBalance() << "\n"
              << "Requested amount: " << e.getAmount() << std::endl;
}
return 0;

}


### Output:

Error: Insufficient funds Available balance: 1000 Requested amount: 1500


Stack Unwinding

When an exception is thrown, the program unwinds the call stack, releasing resources (e.g., closing files, freeing memory) until a matching catch block is found.

Visualization: Stack Unwinding

checkBalance()withdraw()main()TOP
Stack unwinding: Resources (e.g., file handles) are released as the call stack unwinds after an exception.

Best Practices for Exception Handling

  1. Catch by Reference: Avoid slicing (use catch(const std::exception& e)).
  2. Avoid Empty Catch Blocks: Log errors or rethrow (throw;).
  3. Use RAII: Ensure resources (e.g., file handles) are released via destructors.
  4. Prefer Standard Exceptions: Use std::exception hierarchy unless custom logic is needed.
  5. Document Exceptions: Specify which exceptions a function might throw.
012345678910Specific exceptionsAvoid empty catchLog exceptions

Real-World Applications

1. eSewa (Nepal) – Payment Failures

  • Idea Used: Exception handling for failed transactions.
  • How: If a user’s card declines, eSewa throws a PaymentFailedException and prompts for an alternative method (e.g., Khalti or mobile banking).
  • Code Snippet:
    try {
        processPayment(amount);
    }
    catch (const PaymentFailedException& e) {
        std::cout << "Payment failed: " << e.what()
                  << "\nTry another method (Khalti/Ncell)." << std::endl;
    }
    

2. Pathao (Ride-Hailing) – Driver Unavailability

  • Idea Used: Asynchronous exceptions for driver-side issues.
  • How: If a driver cancels a ride, Pathao throws a DriverUnavailableException and reassigns the ride to another driver or notifies the user.
  • Trace:
    Step 1: User requests ride.
    Step 2: System throws DriverUnavailableException.
    Step 3: Catch block reassigns ride or cancels order.
    

3. NTC (Electricity Billing) – Meter Reading Errors

  • Idea Used: File handling + exceptions for corrupted data.
  • How: If a meter reading file is corrupted, NTC’s system throws a FileCorruptionException and logs the error for manual review.
  • Example:
    try {
        readMeterData("corrupted_file.txt");
    }
    catch (const FileCorruptionException& e) {
        logError(e.what());
        notifyTechnician();
    }
    

Worked Example: File Handling with Exceptions

Scenario: A program reads student grades from a file. If the file is missing or corrupted, it throws an exception.

Code:

#include <iostream>
#include <fstream>
#include <stdexcept>

void readGrades(const std::string& filename) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        throw std::runtime_error("File not found: " + filename);
    }

    int grade;
    while (file >> grade) {
        std::cout << "Grade: " << grade << std::endl;
    }
    if (file.bad()) {
        throw std::runtime_error("File corrupted: " + filename);
    }
}

int main() {
    try {
        readGrades("grades.txt");
    }
    catch (const std::runtime_error& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        std::cout << "Using default grades..." << std::endl;
    }
    return 0;
}

Step-by-Step Trace:

Step Action State
1 readGrades("grades.txt") called File not found → throw runtime_error.
2 Exception propagates to main(). catch block executes.
3 Output: "Error: File not found..." Program continues with default grades.

Common Pitfalls and How to Avoid Them

Pitfall Solution
Catching all exceptions with catch(...) Use specific exception types for better debugging.
Throwing exceptions in destructors Destructors should not throw; use noexcept.
Ignoring exceptions Always handle or log exceptions to avoid silent failures.
Using exceptions for flow control Exceptions are for error handling, not normal program logic (use if-else).

Exam Tip

  1. Define Exception Handling: Start with "Exception handling is a mechanism to handle runtime errors gracefully using try, throw, and catch blocks."
  2. Compare Built-in vs. User-Defined:
    • Built-in: std::runtime_error, std::logic_error.
    • User-defined: Derive from std::exception or use custom classes.
  3. Code Structure:
    • Always show a try-catch block with a realistic example (e.g., file I/O, arithmetic).
    • Include one custom exception in your answer.
  4. Stack Unwinding: Mention that resources are released during unwinding.
  5. Real-World Link: Relate to eSewa/Khalti (payments), Pathao (rides), or NTC (billing) in your examples.

graph TD
  A["try block"] -->|"Exception thrown"| B["throw"]
  B --> C["catch block 1"]
  C -->|"No match"| D["catch block 2"]
  D -->|"No match"| E["Terminate (unhandled)"]
  C -->|"Match"| F["Handle exception"]
  F --> G["Program continues"]

Based on the TU BIT syllabus for Object Oriented Programming (BIT153), unit 7.

Discussion

Loading…