AlgoDaily

Error Handling in C++

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

Why Errors Matter

The final reliability layer

Now that we've covered Introduction to C++, Object-Oriented Programming in C++, Working with Files in C++, and Memory Management in C++, you're ready for the piece that ties real programs together: what happens when things go wrong.

As a web developer, you already know that production code is mostly about handling messy reality:

  • Missing files
  • Invalid input
  • Failed network calls
  • Bad state
  • Resource cleanup

In C++, error handling matters even more because your program is closer to files, memory, and system resources. A good C++ developer doesn't just write the happy path — they designs the failure path too.

Why Errors Matter

Try Catch Flow

Exceptions: a controlled emergency exit

An exception is a way for code to say: I cannot continue normally from here. Someone higher up must decide what to do.

The basic flow is:

try block
  ↓
code runs normally
  ↓
if something fails: throw exception
  ↓
matching catch block handles it

This is similar to try/catch in JavaScript, but in C++ you usually catch exceptions by const reference so you avoid copying and preserve the real exception type.

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

// Basketball stat helper: impossible if shots is zero.
double pointsPerShot(int points, int shots) {
    if (shots == 0) {
        throw std::invalid_argument("shots cannot be zero");
    }
    return static_cast<double>(points) / shots;
}

int main() {
    for (int shots : {10, 0}) {
        try {
            std::cout << "Points per shot: " << pointsPerShot(24, shots) << "\n";
        } catch (const std::invalid_argument& error) {
            std::cout << "Stat error: " << error.what() << "\n";
        }
    }

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

Catch By Reference

When catching a standard C++ exception, which style is usually preferred?

Standard Exceptions

Use the standard vocabulary

C++ gives you common exception types in <stdexcept>. These are useful because other C++ developers immediately understand the intent.

Common ones include:

  • std::invalid_argument — caller gave a bad argument
  • std::out_of_range — index/key/range access failed
  • std::runtime_error — something failed while running
  • std::logic_error — program logic is inconsistent

Notice how this builds on Working with Files in C++: there, you checked stream failure states. In other APIs, you may receive exceptions instead. C++ uses both styles depending on the library.

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

int main() {
    std::vector<std::string> parts = {"wire", "switch"};

    try {
        std::cout << "Part: " << parts.at(2) << "\n";
    } catch (const std::out_of_range& error) {
        std::cout << "Inventory lookup failed: " << error.what() << "\n";
    }

    try {
        int tempo = std::stoi("fast");
        std::cout << "Tempo: " << tempo << "\n";
    } catch (const std::invalid_argument& error) {
        std::cout << "Could not parse tempo: " << error.what() << "\n";
    }

    return 0;
}
```

Choosing Strategy

Exceptions are not for everything

A strong C++ habit is choosing the right error style for the situation.

Use return values or status objects when failure is normal and expected:

  • A search found no result
  • A form field is currently incomplete
  • A file may or may not exist

Use exceptions when the function cannot fulfill its promise:

  • A required config file is unreadable
  • An invariant is broken
  • A conversion that must succeed fails
  • A system resource cannot be acquired

A useful mental model: don't throw an exception every time someone misses a basketball shot. Throw when the scoreboard itself is broken.

Choosing Strategy

Cleanup On Exceptions

RAII makes exceptions safer

This is where Memory Management in C++ becomes incredibly important.

When an exception is thrown, C++ performs stack unwinding: it exits scopes until it finds a matching catch. As it exits each scope, destructors run for fully constructed local objects.

That means RAII objects still clean themselves up even when the normal path is interrupted.

This is one reason modern C++ prefers objects like std::string, std::vector, file streams, and smart pointers over manual cleanup.

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

class SpeakerCable {
public:
    SpeakerCable(const std::string& name) : name(name) {
        std::cout << "Connected: " << name << "\n";
    }

    ~SpeakerCable() {
        std::cout << "Disconnected: " << name << "\n";
    }

private:
    std::string name;
};

void rehearse() {
    SpeakerCable cable("guitar amp");
    std::cout << "Starting rehearsal...\n";
    throw std::runtime_error("power surge detected");
}

int main() {
    try {
        rehearse();
    } catch (const std::runtime_error& error) {
        std::cout << "Handled problem: " << error.what() << "\n";
    }

    return 0;
}
```

Stack Unwinding

True or false: when an exception leaves a scope, destructors run for local objects that were successfully constructed in that scope.

Handler Keyword

Fill in the blank: the block that handles a thrown exception begins with the keyword _____.

Custom Error Types

Name your domain failures

Standard exceptions are great, but sometimes your program has domain-specific failures.

For those, create a custom exception type. The common approach is to derive from std::runtime_error or another standard exception type.

This gives your errors a meaningful name while still supporting the standard .what() message and normal catch behavior.

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

class RenovationError : public std::runtime_error {
public:
    RenovationError(const std::string& message)
        : std::runtime_error(message) {}
};

void installOutlet(bool breakerIsOff) {
    if (!breakerIsOff) {
        throw RenovationError("turn off the breaker before installing outlet");
    }

    std::cout << "Outlet installed safely.\n";
}

int main() {
    try {
        installOutlet(false);
    } catch (const RenovationError& error) {
        std::cout << "Safety check failed: " << error.what() << "\n";
    }

    try {
        installOutlet(true);
    } catch (const RenovationError& error) {
        std::cout << "Safety check failed: " << error.what() << "\n";
    }

    return 0;
}
```

Course Wrap

You now have the core C++ toolkit

This lesson completes the section Learn C++ for WebDevs.

You started with compiled programs, variables, control flow, and functions in Introduction to C++. Then you modeled behavior with classes and polymorphism in Object-Oriented Programming in C++. You learned streams and persistence in Working with Files in C++. You got closer to the machine with stack, heap, pointers, RAII, and smart pointers in Memory Management in C++.

Now you can reason about failure too:

  • Use try / catch for exceptional failures
  • Throw meaningful standard or custom exception types
  • Catch by const reference
  • Let RAII handle cleanup during stack unwinding
  • Choose exceptions only when they match the problem

That's a real foundation: not just writing C++ that works once, but writing C++ that survives bad inputs, broken assumptions, and real-world conditions.

Course Wrap

Open interactive view →