Tutorials Logic, IN info@tutorialslogic.com

C++ Exceptions: Throwing, Catching, and Cleanup

Exception Safety

C++ exceptions separate failure reporting from the normal return path, while RAII ensures owned resources are released during stack unwinding. Throw exceptions for failures a caller can handle at a suitable boundary, not for expected loop conditions or ordinary optional results.

Translate Low-Level Failure Once

A boundary can add domain context with std::throw_with_nested or a new exception while preserving the original cause. This small example validates before division.

Catch a typed failure

Catch a typed failure
#include <iostream>
#include <stdexcept>

double ratio(double total, double count) {
    if (count == 0) throw std::invalid_argument{"count must not be zero"};
    return total / count;
}

int main() {
    try {
        std::cout << ratio(10, 0) << '\n';
    } catch (const std::invalid_argument& error) {
        std::cerr << "input error: " << error.what() << '\n';
    }
}
Output
input error: count must not be zero
  • The text is written to standard error.

noexcept Is a Contract

If an exception escapes a noexcept function, std::terminate is called. Mark functions noexcept only when the implementation and called operations support that guarantee; destructors should not allow exceptions to escape.

  • Do not use exceptions for ordinary branch outcomes.
  • Keep invariants valid before throwing.
  • Never throw from cleanup code.

Safety Guarantees

The basic guarantee preserves invariants and prevents leaks after failure. The strong guarantee leaves the observable operation unchanged, often by building a temporary result and committing with a non-throwing swap. The no-throw guarantee is required for destructors and important cleanup paths.

Catch by const reference to avoid slicing, order handlers from specific to general, and add context without discarding the original cause. Never let a destructor throw during unwinding. Define one top-level boundary that turns uncaught failures into a controlled exit or service response.

Design for Stack Unwinding

An exception transfers control, but destructors for fully constructed automatic objects still run as the stack unwinds. Put resource release in RAII owners rather than duplicating cleanup in every catch block. Catch only where code can recover, translate the failure, or add useful context.

Throw values by value and catch polymorphic standard exceptions by const reference. A catch-all at the application boundary may log and terminate an operation, but library code should not erase the original failure merely to return a generic value.

RAII Cleanup During an Exception

RAII Cleanup During an Exception
#include <iostream>
#include <stdexcept>

struct Guard {
    ~Guard() { std::cout << "cleanup\n"; }
};

void work() {
    Guard guard;
    throw std::runtime_error("failed");
}

int main() {
    try { work(); }
    catch (const std::exception& error) { std::cout << error.what() << '\n'; }
}
Output
cleanup
failed

The guard destructor runs before control reaches the handler, proving that cleanup follows object lifetime rather than duplicated error paths.

Before you move on

Failure Review

5 checks
  • Thrown types carry useful context.
  • Handlers catch by const reference.
  • Resources use RAII.
  • Recovery happens at a policy boundary.
  • noexcept declarations are truthful.

Try this next

Exception Safety Practice

0 of 2 completed

  1. Create a scope-owned resource that logs destruction, throw midway through the scope, and verify cleanup occurs without a catch at that level. The destructor should own release; do not duplicate it in each error path.
  2. Let a parsing function throw a typed exception and translate it into one user-facing error in main rather than catching and rethrowing at every helper. Catch where the program can make a recovery or reporting decision.

Exception Questions

It avoids copying and preserves the dynamic exception type without allowing the handler to modify it.

Stack objects are destroyed during unwinding, so their destructors can release files, locks, and memory.

The program calls std::terminate instead of passing the exception to an outer handler.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.