Exceptions recap

Excellent work! You've learned about exception handling in C++. Let's review the key concepts from this section.

Exception Handling Fundamentals

Exception handling provides a mechanism to decouple error handling from typical control flow. This allows greater flexibility in handling errors when and however is most useful for a given situation, eliminating many (if not all) of the complications that return codes cause.

Throw Statements, Try Blocks, and Catch Blocks

A throw statement raises an exception. Try blocks look for exceptions thrown by code written or called within them. These exceptions get routed to catch blocks, which catch exceptions of particular types (when they match) and handle them. By default, an exception that is caught is considered handled.

Exception Handling Process

Exceptions are handled immediately. When an exception is raised, control jumps to the nearest enclosing try block, looking for catch handlers that can handle the exception. If a matching try/catch is found, the stack is unwound to the point of the catch block, and control resumes at the top of the matching catch. If no try block is found or no catch blocks match, the program calls std::terminate, which terminates with an unhandled exception error.

Exception Types

Exceptions of any data type can be thrown, including classes.

Catch Block Matching

Catch blocks can be configured to catch exceptions of a particular data type, or a catch-all handler can be set up using ellipses (...). A catch block catching a base class reference will also catch exceptions of derived classes. All exceptions thrown by the standard library derive from the std::exception class (which lives in the <exception> header), so catching a std::exception by reference will catch all standard library exceptions. The what() member function can be used to determine what kind of std::exception was thrown.

Throwing and Rethrowing Exceptions

Inside a catch block, a new exception may be thrown. Because this new exception is thrown outside of the try block associated with that catch block, it won't be caught by the catch block it's thrown within. Exceptions may be rethrown from a catch block by using the keyword throw by itself. Do not rethrow an exception using the exception variable that was caught, otherwise object slicing may result.

Function Try Blocks

Function try blocks give you a way to catch any exception that occurs within a function or an associated member initialization list. These are typically only used with derived class constructors.

Destructors and Exceptions

You should never throw an exception from a destructor.

The noexcept Specifier

The noexcept exception specifier can be used to denote that a function is no-throw/no-fail.

std::move_if_noexcept

std::move_if_noexcept will return a movable r-value if the object has a noexcept move constructor, otherwise it will return a copyable l-value. We can use the noexcept specifier in conjunction with std::move_if_noexcept to use move semantics only when a strong exception guarantee exists (and use copy semantics otherwise).

Performance Considerations

Finally, exception handling does have a cost. In most cases, code using exceptions will run slightly slower, and the cost of handling an exception is very high. You should only use exceptions to handle exceptional circumstances, not for normal error handling cases (e.g., invalid input).

Key Terminology

  • Exception: Error condition signaled during program execution
  • Exception handling: Mechanism for decoupling error handling from normal control flow
  • throw statement: Statement that raises an exception
  • try block: Block that monitors for exceptions
  • catch block: Block that handles exceptions of a particular type
  • Catch handler: Another name for catch block
  • Stack unwinding: Process of destroying local variables when an exception is thrown
  • std::terminate: Function called for unhandled exceptions
  • Unhandled exception: Exception with no matching catch block
  • Catch-all handler: Catch block using ellipsis (...) to catch any exception
  • std::exception: Base class for standard library exceptions
  • what(): Member function returning exception description
  • Rethrow: Throwing the current exception again using bare throw
  • Object slicing: Losing derived type info when rethrowing by variable name
  • Function try block: Try block wrapping entire function including member initializer list
  • noexcept specifier: Specifies that a function doesn't throw exceptions
  • No-throw/No-fail: Function guaranteed not to throw
  • std::move_if_noexcept: Conditionally moves based on noexcept guarantee
  • Strong exception guarantee: Operations either complete or have no effect

Looking Forward

Exception handling is an important tool for managing errors in C++ programs. The concepts you've learned—throw/try/catch, stack unwinding, exception specifications, and the performance considerations—provide a foundation for writing robust code that handles errors gracefully. As you build larger applications, you'll develop a better sense for when exceptions are the right tool versus other error handling approaches like return codes or std::optional.