AlgoDaily

Exception Handling

Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.

Errors Need Paths

When programs leave the happy path

In File Handling and I/O operations, you checked whether a file opened successfully before reading it. That was your first serious encounter with runtime failure: the code may be correct, but the world may not cooperate.

Exception handling gives C++ a structured way to handle those runtime problems.

Think of it like this:

  • Normal code handles the expected path.
  • Exceptions handle unusual but possible failures.
  • try, throw, and catch create a separate error path without burying every line in if checks.

This matters in real systems: finance apps reject invalid trades, AI pipelines fail on missing model files, and backend services receive malformed requests.

Errors Need Paths

Try Throw Catch

The exception-handling trio

C++ exception handling centers on three keywords:

  • try: marks code where something may go wrong.
  • throw: signals that an error occurred.
  • catch: handles the thrown error.

A useful mental model:

try block
   |
   | normal execution
   v
continues normally

OR

try block
   |
   | throw error
   v
matching catch block

When C++ sees throw, it stops the current path and searches for a matching catch block.

```cpp
#include <iostream>
#include <stdexcept>

int divide(int numerator, int denominator) {
    if (denominator == 0) {
        throw std::runtime_error("Cannot divide by zero");
    }
    return numerator / denominator;
}

int main() {
    try {
        std::cout << "Result: " << divide(10, 0) << "\n";
        std::cout << "This line will not run if an exception is thrown.\n";
    } catch (const std::runtime_error& error) {
        std::cout << "Caught error: " << error.what() << "\n";
    }

    std::cout << "Program recovered and kept running.\n";
    return 0;
}
```

What Happens Next

A function inside a try block executes throw std::runtime_error("bad input").

What does C++ do next?

Catching Specific Errors

Catch by type

A catch block matches exceptions by type. This lets you respond differently to different kinds of failures.

Common standard exception types include:

  • std::runtime_error: something failed while the program was running.
  • std::invalid_argument: a function received an unacceptable argument.
  • std::out_of_range: a value tried to access something outside a valid range.

Prefer catching exceptions by const reference:

catch (const std::runtime_error& error)

That avoids unnecessary copying and preserves the exception object's real type.

Ordering matters: put more specific catches before more general ones.

Catching Specific Errors

Choose The Handler

Fill in the blank with the C++ keyword that signals an exception:

_____ std::runtime_error("Missing price data");

Multiple Catch Blocks

Different problems, different responses

In web development, you might handle a missing route differently from invalid JSON. C++ has a similar idea: use multiple catch blocks when different failures deserve different responses.

In this example, invalid finance-style input is treated differently from a broader runtime failure.

```cpp
#include <iostream>
#include <stdexcept>
#include <string>

int parseShares(int shares) {
    if (shares <= 0) {
        throw std::invalid_argument("Share count must be positive");
    }
    if (shares > 1000000) {
        throw std::runtime_error("Trade size exceeds risk limit");
    }
    return shares;
}

int main() {
    try {
        int shares = parseShares(-50);
        std::cout << "Parsed shares: " << shares << "\n";
    } catch (const std::invalid_argument& error) {
        std::cout << "Input error: " << error.what() << "\n";
    } catch (const std::runtime_error& error) {
        std::cout << "Runtime error: " << error.what() << "\n";
    } catch (const std::exception& error) {
        std::cout << "General error: " << error.what() << "\n";
    }

    return 0;
}
```

Catch Order Matters

True or false?

When using multiple catch blocks, a general catch (const std::exception& error) should usually come before more specific handlers like catch (const std::invalid_argument& error).

Custom Exceptions

When built-in exceptions are not expressive enough

Standard exceptions are often enough. But sometimes your domain has a failure that deserves a clear name.

Examples:

  • InsufficientFundsError
  • ModelLoadError
  • PermissionDeniedError
  • InvalidPortfolioError

A custom exception makes code easier to read because the type itself explains what went wrong.

The common approach is to create a class that inherits from std::exception and overrides what().

```cpp
#include <iostream>
#include <exception>

class InsufficientFundsError : public std::exception {
public:
    const char* what() const noexcept override {
        return "Insufficient funds for withdrawal";
    }
};

void withdraw(double balance, double amount) {
    if (amount > balance) {
        throw InsufficientFundsError();
    }
    std::cout << "Withdrawal approved. Remaining balance: "
              << balance - amount << "\n";
}

int main() {
    try {
        withdraw(100.0, 250.0);
    } catch (const InsufficientFundsError& error) {
        std::cout << "Transaction rejected: " << error.what() << "\n";
    }

    std::cout << "Account system still running.\n";
    return 0;
}
```

Unwinding The Stack

Exceptions travel upward

An exception does not need to be caught in the same function where it is thrown.

If a function throws and does not catch the exception, C++ returns control to the caller and keeps searching. This process is called stack unwinding.

main()
  calls loadConfig()
    calls openFile()
      throws error
    no catch here
  catch found here

This connects to the call stack mental model from Functions And Recursion: C++ backs out of active function calls until it finds a matching handler.

If no handler is found, the program terminates.

Unwinding The Stack

Exception Habits

Practical rules of thumb

Use exceptions for unusual failure cases, not everyday control flow.

Good uses:

  • A file cannot be opened.
  • A required configuration value is missing.
  • A function receives invalid input that prevents meaningful work.
  • A risk check fails before placing a trade.

Avoid using exceptions for:

  • Normal loop exits.
  • Simple if / else decisions.
  • Cases where returning a normal value is clearer.

A solid pattern is:

  1. Detect the failure close to where it happens.
  2. throw an exception with a useful type or message.
  3. catch it at a level that can actually decide what to do.

Next, you will meet the Standard Template Library, where these ideas become even more useful when working with reusable containers and algorithms.

Exception Habits

Open interactive view →